Blame source/vdo/kernel/workItemStats.h

Packit Service 7e342f
/*
Packit Service 7e342f
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 7e342f
 *
Packit Service 7e342f
 * This program is free software; you can redistribute it and/or
Packit Service 7e342f
 * modify it under the terms of the GNU General Public License
Packit Service 7e342f
 * as published by the Free Software Foundation; either version 2
Packit Service 7e342f
 * of the License, or (at your option) any later version.
Packit Service 7e342f
 * 
Packit Service 7e342f
 * This program is distributed in the hope that it will be useful,
Packit Service 7e342f
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 7e342f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 7e342f
 * GNU General Public License for more details.
Packit Service 7e342f
 * 
Packit Service 7e342f
 * You should have received a copy of the GNU General Public License
Packit Service 7e342f
 * along with this program; if not, write to the Free Software
Packit Service 7e342f
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 7e342f
 * 02110-1301, USA. 
Packit Service 7e342f
 *
Packit Service 7e342f
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/workItemStats.h#2 $
Packit Service 7e342f
 */
Packit Service 7e342f
Packit Service 7e342f
#ifndef WORK_ITEM_STATS_H
Packit Service 7e342f
#define WORK_ITEM_STATS_H
Packit Service 7e342f
Packit Service 7e342f
#include "timeUtils.h"
Packit Service 7e342f
Packit Service 7e342f
#include "workQueue.h"
Packit Service 7e342f
Packit Service 7e342f
enum {
Packit Service 7e342f
  // Whether to enable tracking of per-work-function run-time stats.
Packit Service 7e342f
  ENABLE_PER_FUNCTION_TIMING_STATS = 0,
Packit Service 7e342f
  // How many work function/priority pairs to track call stats for
Packit Service 7e342f
  NUM_WORK_QUEUE_ITEM_STATS        = 18,
Packit Service 7e342f
};
Packit Service 7e342f
Packit Service 7e342f
typedef struct simpleStats {
Packit Service 7e342f
  uint64_t count;
Packit Service 7e342f
  uint64_t sum;
Packit Service 7e342f
  uint64_t min;
Packit Service 7e342f
  uint64_t max;
Packit Service 7e342f
} SimpleStats;
Packit Service 7e342f
Packit Service 7e342f
/*
Packit Service 7e342f
 * We track numbers of work items handled (and optionally the
Packit Service 7e342f
 * wall-clock time to run the work functions), broken down by
Packit Service 7e342f
 * individual work functions (or alternate functions that the caller
Packit Service 7e342f
 * wants recorded, like the VIO completion callback function if we're
Packit Service 7e342f
 * just enqueueing a work function that invokes that indirectly) and
Packit Service 7e342f
 * priority.
Packit Service 7e342f
 *
Packit Service 7e342f
 * The first part of this structure manages the function/priority
Packit Service 7e342f
 * pairs, and is read frequently but updated rarely (once for each
Packit Service 7e342f
 * pair, plus possibly spin lock contention).
Packit Service 7e342f
 *
Packit Service 7e342f
 * The second part holds counters, and is updated often; different
Packit Service 7e342f
 * parts are updated by various threads as described below. The last
Packit Service 7e342f
 * element of each array, index NUM_WORK_QUEUE_ITEM_STATS, is updated
Packit Service 7e342f
 * only if we have filled the arrays and can't add the current work
Packit Service 7e342f
 * function/priority. See how the statTableIndex field is set in
Packit Service 7e342f
 * workItemStats.c.
Packit Service 7e342f
 *
Packit Service 7e342f
 * All fields may additionally be read when reporting statistics
Packit Service 7e342f
 * (including optionally reporting stats when the worker thread shuts
Packit Service 7e342f
 * down), but that's rare and shouldn't significantly affect cache
Packit Service 7e342f
 * contention issues.
Packit Service 7e342f
 *
Packit Service 7e342f
 * There is no "pending" count per work function here. For reporting
Packit Service 7e342f
 * statistics, it can be approximated by looking at the other fields.
Packit Service 7e342f
 * Do not rely on them being precise and synchronized, though.
Packit Service 7e342f
 */
Packit Service 7e342f
typedef struct kvdoWorkItemStatsFunctionTable {
Packit Service 7e342f
  /*
Packit Service 7e342f
   * The spin lock is used to protect .functions and .priorities
Packit Service 7e342f
   * during updates. All three are modified by producers (enqueueing
Packit Service 7e342f
   * threads) but only rarely. The .functions and .priorities arrays
Packit Service 7e342f
   * are read by producers very frequently.
Packit Service 7e342f
   */
Packit Service 7e342f
  spinlock_t       lock;
Packit Service 7e342f
  KvdoWorkFunction functions[NUM_WORK_QUEUE_ITEM_STATS];
Packit Service 7e342f
  uint8_t          priorities[NUM_WORK_QUEUE_ITEM_STATS];
Packit Service 7e342f
} KvdoWorkFunctionTable;
Packit Service 7e342f
Packit Service 7e342f
typedef struct kvdoWorkItemStats {
Packit Service 7e342f
  /*
Packit Service 7e342f
   * Table of functions and priorities, for determining the index to
Packit Service 7e342f
   * use into the counter arrays below.
Packit Service 7e342f
   *
Packit Service 7e342f
   * This table is read by producers (usually multiple entries) for
Packit Service 7e342f
   * every work item enqueued, and when reporting stats. It is updated
Packit Service 7e342f
   * by producers, and only the first time a new (work-function,
Packit Service 7e342f
   * priority) combination is seen.
Packit Service 7e342f
   */
Packit Service 7e342f
  KvdoWorkFunctionTable  functionTable;
Packit Service 7e342f
  // Skip to (somewhere on) the next cache line
Packit Service 7e342f
  char                   pad[CACHE_LINE_BYTES - sizeof(atomic64_t)];
Packit Service 7e342f
  /*
Packit Service 7e342f
   * The .enqueued field is updated by producers only, once per work
Packit Service 7e342f
   * item processed; __sync operations are used to update these
Packit Service 7e342f
   * values.
Packit Service 7e342f
   */
Packit Service 7e342f
  atomic64_t             enqueued[NUM_WORK_QUEUE_ITEM_STATS + 1];
Packit Service 7e342f
  // Skip to (somewhere on) the next cache line
Packit Service 7e342f
  char                   pad2[CACHE_LINE_BYTES - sizeof(atomic64_t)];
Packit Service 7e342f
  /*
Packit Service 7e342f
   * These values are updated only by the consumer (worker thread). We
Packit Service 7e342f
   * overload the .times[].count field as a count of items processed,
Packit Service 7e342f
   * so if we're not doing the optional processing-time tracking
Packit Service 7e342f
   * (controlled via an option in workQueue.c), we need to explicitly
Packit Service 7e342f
   * update the count.
Packit Service 7e342f
   *
Packit Service 7e342f
   * Since only one thread can ever update these values, no
Packit Service 7e342f
   * synchronization is used.
Packit Service 7e342f
   */
Packit Service 7e342f
  SimpleStats            times[NUM_WORK_QUEUE_ITEM_STATS + 1];
Packit Service 7e342f
} KvdoWorkItemStats;
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Initialize a statistics structure for tracking sample
Packit Service 7e342f
 * values. Assumes the storage was already zeroed out at allocation
Packit Service 7e342f
 * time.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param stats    The statistics structure
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline void initSimpleStats(SimpleStats *stats)
Packit Service 7e342f
{
Packit Service 7e342f
  // Assume other fields are initialized to zero at allocation.
Packit Service 7e342f
  stats->min = UINT64_MAX;
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Update the statistics being tracked for a new sample value.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param stats    The statistics structure
Packit Service 7e342f
 * @param value    The new value to be folded in
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline void addSample(SimpleStats *stats, uint64_t value)
Packit Service 7e342f
{
Packit Service 7e342f
  stats->count++;
Packit Service 7e342f
  stats->sum += value;
Packit Service 7e342f
  if (stats->min > value) {
Packit Service 7e342f
    stats->min = value;
Packit Service 7e342f
  }
Packit Service 7e342f
  if (stats->max < value) {
Packit Service 7e342f
    stats->max = value;
Packit Service 7e342f
  }
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Return the average of the samples collected.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param stats    The statistics structure
Packit Service 7e342f
 *
Packit Service 7e342f
 * @return         The average sample value
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline uint64_t getSampleAverage(const SimpleStats *stats)
Packit Service 7e342f
{
Packit Service 7e342f
  uint64_t slop = stats->count / 2;
Packit Service 7e342f
  return (stats->sum + slop) / stats->count;
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Update all work queue statistics (work-item and otherwise) after
Packit Service 7e342f
 * enqueueing a work item.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param  stats     The statistics structure
Packit Service 7e342f
 * @param  item      The work item enqueued
Packit Service 7e342f
 * @param  priority  The work item's priority
Packit Service 7e342f
 **/
Packit Service 7e342f
void updateWorkItemStatsForEnqueue(KvdoWorkItemStats *stats,
Packit Service 7e342f
                                   KvdoWorkItem      *item,
Packit Service 7e342f
                                   int                priority);
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Update all work queue statistics (work-item and otherwise) after enqueueing
Packit Service 7e342f
 * a work item.
Packit Service 7e342f
 *
Packit Service 7e342f
 * This is a very lightweight function (after optimizing away conditionals and
Packit Service 7e342f
 * no-ops) and is called for every work item processed, hence the inline
Packit Service 7e342f
 * definition.
Packit Service 7e342f
 *
Packit Service 7e342f
 * This function requires that recordStartTime and
Packit Service 7e342f
 * updateWorkItemStatsForWorkTime below both get called as well; in some cases
Packit Service 7e342f
 * counters may be updated in updateWorkItemStatsForWorkTime rather than here.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param  stats  The statistics structure
Packit Service 7e342f
 * @param  item   The work item enqueued
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline void updateWorkItemStatsForDequeue(KvdoWorkItemStats *stats,
Packit Service 7e342f
                                                 KvdoWorkItem      *item)
Packit Service 7e342f
{
Packit Service 7e342f
  // The times[].count field is overloaded as a count of items
Packit Service 7e342f
  // processed.
Packit Service 7e342f
  if (!ENABLE_PER_FUNCTION_TIMING_STATS) {
Packit Service 7e342f
    stats->times[item->statTableIndex].count++;
Packit Service 7e342f
  } else {
Packit Service 7e342f
    // In this case, updateWorkItemStatsForWorkTime will bump the counter.
Packit Service 7e342f
  }
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Record the starting time for processing a work item, if timing
Packit Service 7e342f
 * stats are enabled and if we haven't run out of room for recording
Packit Service 7e342f
 * stats in the table.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param  index  The work item's index into the internal array
Packit Service 7e342f
 *
Packit Service 7e342f
 * @return    The current time, or zero
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline uint64_t recordStartTime(unsigned int index)
Packit Service 7e342f
{
Packit Service 7e342f
  return (ENABLE_PER_FUNCTION_TIMING_STATS ? currentTime(CLOCK_MONOTONIC) : 0);
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Update the work queue statistics with the wall-clock time for
Packit Service 7e342f
 * processing a work item, if timing stats are enabled and if we
Packit Service 7e342f
 * haven't run out of room for recording stats in the table.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param  stats      The statistics structure
Packit Service 7e342f
 * @param  index      The work item's index into the internal array
Packit Service 7e342f
 * @param  startTime  The start time as reported by recordStartTime
Packit Service 7e342f
 **/
Packit Service 7e342f
static inline void updateWorkItemStatsForWorkTime(KvdoWorkItemStats *stats,
Packit Service 7e342f
                                                  unsigned int       index,
Packit Service 7e342f
                                                  uint64_t           startTime)
Packit Service 7e342f
{
Packit Service 7e342f
  if (ENABLE_PER_FUNCTION_TIMING_STATS) {
Packit Service 7e342f
    uint64_t endTime = currentTime(CLOCK_MONOTONIC);
Packit Service 7e342f
    addSample(&stats->times[index], endTime - startTime);
Packit Service 7e342f
  }
Packit Service 7e342f
}
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Convert the pointer into a string representation, using a function
Packit Service 7e342f
 * name if available.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param pointer       The pointer to be converted
Packit Service 7e342f
 * @param buffer        The output buffer
Packit Service 7e342f
 * @param bufferLength  The size of the output buffer
Packit Service 7e342f
 **/
Packit Service 7e342f
char *getFunctionName(void *pointer, char *buffer, size_t bufferLength);
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Dump statistics broken down by work function and priority into the
Packit Service 7e342f
 * kernel log.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param  stats  The statistics structure
Packit Service 7e342f
 **/
Packit Service 7e342f
void logWorkItemStats(const KvdoWorkItemStats *stats);
Packit Service 7e342f
Packit Service 7e342f
/**
Packit Service 7e342f
 * Format counters for per-work-function stats for reporting via /sys.
Packit Service 7e342f
 *
Packit Service 7e342f
 * @param [in]  stats   The statistics structure
Packit Service 7e342f
 * @param [out] buffer  The output buffer
Packit Service 7e342f
 * @param [in]  length  The size of the output buffer
Packit Service 7e342f
 *
Packit Service 7e342f
 * @return  The size of the string actually written
Packit Service 7e342f
 **/
Packit Service 7e342f
size_t formatWorkItemStats(const KvdoWorkItemStats *stats,
Packit Service 7e342f
                           char                    *buffer,
Packit Service 7e342f
                           size_t                   length);
Packit Service 7e342f
Packit Service 7e342f
#endif // WORK_ITEM_STATS_H