Blame vdo/kernel/workItemStats.h

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