Blame source/vdo/kernel/workItemStats.c

Packit Service d40955
/*
Packit Service d40955
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service d40955
 *
Packit Service d40955
 * This program is free software; you can redistribute it and/or
Packit Service d40955
 * modify it under the terms of the GNU General Public License
Packit Service d40955
 * as published by the Free Software Foundation; either version 2
Packit Service d40955
 * of the License, or (at your option) any later version.
Packit Service d40955
 * 
Packit Service d40955
 * This program is distributed in the hope that it will be useful,
Packit Service d40955
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d40955
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d40955
 * GNU General Public License for more details.
Packit Service d40955
 * 
Packit Service d40955
 * You should have received a copy of the GNU General Public License
Packit Service d40955
 * along with this program; if not, write to the Free Software
Packit Service d40955
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service d40955
 * 02110-1301, USA. 
Packit Service d40955
 *
Packit Service d40955
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/workItemStats.c#4 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "workItemStats.h"
Packit Service d40955
Packit Service d40955
#include "atomic.h"
Packit Service d40955
#include "logger.h"
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Scan the work queue stats table for the provided work function and
Packit Service d40955
 * priority value. If it's not found, see if an empty slot is
Packit Service d40955
 * available.
Packit Service d40955
 *
Packit Service d40955
 * @param table       The work queue's function table
Packit Service d40955
 * @param work        The function we want to record stats for
Packit Service d40955
 * @param priority    The priority of the work item
Packit Service d40955
 *
Packit Service d40955
 * @return   The index of the slot to use (matching or empty), or
Packit Service d40955
 *           NUM_WORK_QUEUE_ITEM_STATS if the table is full of
Packit Service d40955
 *           non-matching entries.
Packit Service d40955
 **/
Packit Service d40955
static inline unsigned int scanStatTable(const KvdoWorkFunctionTable *table,
Packit Service d40955
                                         KvdoWorkFunction             work,
Packit Service d40955
                                         unsigned int                 priority)
Packit Service d40955
{
Packit Service d40955
  unsigned int i;
Packit Service d40955
  /*
Packit Service d40955
   * See comments in getStatTableIndex regarding order of memory
Packit Service d40955
   * accesses. Work function first, then a barrier, then priority.
Packit Service d40955
   */
Packit Service d40955
  for (i = 0; i < NUM_WORK_QUEUE_ITEM_STATS; i++) {
Packit Service d40955
    if (table->functions[i] == NULL) {
Packit Service d40955
      return i;
Packit Service d40955
    } else if (table->functions[i] == work) {
Packit Service d40955
      smp_rmb();
Packit Service d40955
      if (table->priorities[i] == priority) {
Packit Service d40955
        return i;
Packit Service d40955
      }
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  return NUM_WORK_QUEUE_ITEM_STATS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Scan the work queue stats table for the provided work function and
Packit Service d40955
 * priority value. Assign an empty slot if necessary.
Packit Service d40955
 *
Packit Service d40955
 * @param stats       The stats structure
Packit Service d40955
 * @param work        The function we want to record stats for
Packit Service d40955
 * @param priority    The priority of the work item
Packit Service d40955
 *
Packit Service d40955
 * @return   The index of the matching slot, or NUM_WORK_QUEUE_ITEM_STATS
Packit Service d40955
 *           if the table is full of non-matching entries.
Packit Service d40955
 **/
Packit Service d40955
static unsigned int getStatTableIndex(KvdoWorkItemStats *stats,
Packit Service d40955
                                      KvdoWorkFunction   work,
Packit Service d40955
                                      unsigned int       priority)
Packit Service d40955
{
Packit Service d40955
  KvdoWorkFunctionTable *functionTable = &stats->functionTable;
Packit Service d40955
Packit Service d40955
  unsigned int index = scanStatTable(functionTable, work, priority);
Packit Service d40955
  if (unlikely(index == NUM_WORK_QUEUE_ITEM_STATS)
Packit Service d40955
      || likely(functionTable->functions[index] != NULL)) {
Packit Service d40955
    return index;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  unsigned long flags = 0;
Packit Service d40955
  // The delayed-work-item processing uses queue->lock in some cases,
Packit Service d40955
  // and one case may call into this function, so we can't reuse
Packit Service d40955
  // queue->lock here.
Packit Service d40955
  spin_lock_irqsave(&functionTable->lock, flags);
Packit Service d40955
  // Recheck now that we've got the lock...
Packit Service d40955
  index = scanStatTable(functionTable, work, priority);
Packit Service d40955
  if ((index == NUM_WORK_QUEUE_ITEM_STATS)
Packit Service d40955
      || (functionTable->functions[index] != NULL)) {
Packit Service d40955
    spin_unlock_irqrestore(&functionTable->lock, flags);
Packit Service d40955
    return index;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  /*
Packit Service d40955
   * An uninitialized priority is indistinguishable from a zero
Packit Service d40955
   * priority. So store the priority first, and enforce the ordering,
Packit Service d40955
   * so that a non-null work function pointer indicates we've finished
Packit Service d40955
   * filling in the value. (And, to make this work, we have to read
Packit Service d40955
   * the work function first and priority second, when comparing.)
Packit Service d40955
   */
Packit Service d40955
  functionTable->priorities[index] = priority;
Packit Service d40955
  smp_wmb();
Packit Service d40955
  functionTable->functions[index] = work;
Packit Service d40955
  spin_unlock_irqrestore(&functionTable->lock, flags);
Packit Service d40955
  return index;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get counters on work items, identified by index into the internal
Packit Service d40955
 * array.
Packit Service d40955
 *
Packit Service d40955
 * @param [in]  stats         The collected statistics
Packit Service d40955
 * @param [in]  index         The index
Packit Service d40955
 * @param [out] enqueuedPtr   The total work items enqueued
Packit Service d40955
 * @param [out] processedPtr  The number of work items processed
Packit Service d40955
 * @param [out] pendingPtr    The number of work items still pending
Packit Service d40955
 **/
Packit Service d40955
static void getWorkItemCountsByItem(const KvdoWorkItemStats *stats,
Packit Service d40955
                                    unsigned int             index,
Packit Service d40955
                                    uint64_t                *enqueuedPtr,
Packit Service d40955
                                    uint64_t                *processedPtr,
Packit Service d40955
                                    unsigned int            *pendingPtr)
Packit Service d40955
{
Packit Service d40955
  uint64_t enqueued  = atomic64_read(&stats->enqueued[index]);
Packit Service d40955
  uint64_t processed = stats->times[index].count;
Packit Service d40955
  unsigned int pending;
Packit Service d40955
  if (enqueued < processed) {
Packit Service d40955
    // Probably just out of sync.
Packit Service d40955
    pending = 1;
Packit Service d40955
  } else {
Packit Service d40955
    pending = enqueued - processed;
Packit Service d40955
    // Pedantic paranoia: Check for overflow of the 32-bit "pending".
Packit Service d40955
    if ((pending + processed) < enqueued) {
Packit Service d40955
      pending = UINT_MAX;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  *enqueuedPtr  = enqueued;
Packit Service d40955
  *processedPtr = processed;
Packit Service d40955
  *pendingPtr   = pending;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get counters on work items not covered by any index value.
Packit Service d40955
 *
Packit Service d40955
 * @param [in]  stats         The collected statistics
Packit Service d40955
 * @param [out] enqueuedPtr   The total work items enqueued
Packit Service d40955
 * @param [out] processedPtr  The number of work items processed
Packit Service d40955
 **/
Packit Service d40955
static void getOtherWorkItemCounts(const KvdoWorkItemStats *stats,
Packit Service d40955
                                   uint64_t                *enqueuedPtr,
Packit Service d40955
                                   uint64_t                *processedPtr)
Packit Service d40955
{
Packit Service d40955
  unsigned int pending;
Packit Service d40955
  getWorkItemCountsByItem(stats, NUM_WORK_QUEUE_ITEM_STATS,
Packit Service d40955
                          enqueuedPtr, processedPtr, &pending);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Get timing stats on work items, identified by index into the
Packit Service d40955
 * internal array.
Packit Service d40955
 *
Packit Service d40955
 * @param [in]  stats  The collected statistics
Packit Service d40955
 * @param [in]  index  The index into the array
Packit Service d40955
 * @param [out] min    The minimum execution time
Packit Service d40955
 * @param [out] mean   The mean execution time
Packit Service d40955
 * @param [out] max    The maximum execution time
Packit Service d40955
 **/
Packit Service d40955
static void getWorkItemTimesByItem(const KvdoWorkItemStats *stats,
Packit Service d40955
                                   unsigned int             index,
Packit Service d40955
                                   uint64_t                *min,
Packit Service d40955
                                   uint64_t                *mean,
Packit Service d40955
                                   uint64_t                *max)
Packit Service d40955
{
Packit Service d40955
  *min  = stats->times[index].min;
Packit Service d40955
  *mean = getSampleAverage(&stats->times[index]);
Packit Service d40955
  *max  = stats->times[index].max;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void updateWorkItemStatsForEnqueue(KvdoWorkItemStats *stats,
Packit Service d40955
                                   KvdoWorkItem      *item,
Packit Service d40955
                                   int                priority)
Packit Service d40955
{
Packit Service d40955
  item->statTableIndex = getStatTableIndex(stats, item->statsFunction,
Packit Service d40955
                                           priority);
Packit Service d40955
  atomic64_add(1, &stats->enqueued[item->statTableIndex]);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
char *getFunctionName(void *pointer, char *buffer, size_t bufferLength)
Packit Service d40955
{
Packit Service d40955
  if (pointer == NULL) {
Packit Service d40955
    /*
Packit Service d40955
     * Format "%ps" logs a null pointer as "(null)" with a bunch of
Packit Service d40955
     * leading spaces. We sometimes use this when logging lots of
Packit Service d40955
     * data; don't be so verbose.
Packit Service d40955
     */
Packit Service d40955
    strncpy(buffer, "-", bufferLength);
Packit Service d40955
  } else {
Packit Service d40955
    /*
Packit Service d40955
     * Use a non-const array instead of a string literal below to
Packit Service d40955
     * defeat gcc's format checking, which doesn't understand that
Packit Service d40955
     * "%ps" actually does support a precision spec in Linux kernel
Packit Service d40955
     * code.
Packit Service d40955
     */
Packit Service d40955
    static char truncatedFunctionNameFormatString[] = "%.*ps";
Packit Service d40955
    snprintf(buffer, bufferLength,
Packit Service d40955
             truncatedFunctionNameFormatString,
Packit Service d40955
             bufferLength - 1,
Packit Service d40955
             pointer);
Packit Service d40955
Packit Service d40955
    char *space = strchr(buffer, ' ');
Packit Service d40955
    if (space != NULL) {
Packit Service d40955
      *space = '\0';
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  return buffer;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
size_t formatWorkItemStats(const KvdoWorkItemStats *stats,
Packit Service d40955
                           char                    *buffer,
Packit Service d40955
                           size_t                   length)
Packit Service d40955
{
Packit Service d40955
  const KvdoWorkFunctionTable *functionIDs = &stats->functionTable;
Packit Service d40955
  size_t currentOffset = 0;
Packit Service d40955
Packit Service d40955
  uint64_t enqueued, processed;
Packit Service d40955
  int i;
Packit Service d40955
  for (i = 0; i < NUM_WORK_QUEUE_ITEM_STATS; i++) {
Packit Service d40955
    if (functionIDs->functions[i] == NULL) {
Packit Service d40955
      break;
Packit Service d40955
    }
Packit Service d40955
    if (atomic64_read(&stats->enqueued[i]) == 0) {
Packit Service d40955
      continue;
Packit Service d40955
    }
Packit Service d40955
    /*
Packit Service d40955
     * The reporting of all of "pending", "enqueued" and "processed"
Packit Service d40955
     * here seems redundant, but "pending" is limited to 0 in the case
Packit Service d40955
     * where "processed" exceeds "enqueued", either through current
Packit Service d40955
     * activity and a lack of synchronization when fetching stats, or
Packit Service d40955
     * a coding bug. This report is intended largely for debugging, so
Packit Service d40955
     * we'll go ahead and print the not-necessarily-redundant values.
Packit Service d40955
     */
Packit Service d40955
    unsigned int pending;
Packit Service d40955
    getWorkItemCountsByItem(stats, i, &enqueued, &processed, &pending);
Packit Service d40955
Packit Service d40955
    // Format: fn prio enq proc timeo [ min max mean ]
Packit Service d40955
    if (ENABLE_PER_FUNCTION_TIMING_STATS) {
Packit Service d40955
      uint64_t min, mean, max;
Packit Service d40955
      getWorkItemTimesByItem(stats, i, &min, &mean, &max;;
Packit Service d40955
      currentOffset += snprintf(buffer + currentOffset,
Packit Service d40955
                                length - currentOffset,
Packit Service d40955
                                "%-36ps %d %10llu %10" PRIu64
Packit Service d40955
                                " %10llu %10llu %10" PRIu64
Packit Service d40955
                                "\n",
Packit Service d40955
                                functionIDs->functions[i],
Packit Service d40955
                                functionIDs->priorities[i],
Packit Service d40955
                                enqueued, processed,
Packit Service d40955
                                min, max, mean);
Packit Service d40955
    } else {
Packit Service d40955
      currentOffset += snprintf(buffer + currentOffset,
Packit Service d40955
                                length - currentOffset,
Packit Service d40955
                                "%-36ps %d %10llu %10" PRIu64
Packit Service d40955
                                "\n",
Packit Service d40955
                                functionIDs->functions[i],
Packit Service d40955
                                functionIDs->priorities[i],
Packit Service d40955
                                enqueued, processed);
Packit Service d40955
    }
Packit Service d40955
    if (currentOffset >= length) {
Packit Service d40955
      break;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  if ((i == NUM_WORK_QUEUE_ITEM_STATS) && (currentOffset < length)) {
Packit Service d40955
    uint64_t enqueued, processed;
Packit Service d40955
    getOtherWorkItemCounts(stats, &enqueued, &processed);
Packit Service d40955
    if (enqueued > 0) {
Packit Service d40955
      currentOffset += snprintf(buffer + currentOffset,
Packit Service d40955
                                length - currentOffset,
Packit Service d40955
                                "%-36s %d %10llu %10" PRIu64
Packit Service d40955
                                "\n",
Packit Service d40955
                                "OTHER", 0,
Packit Service d40955
                                enqueued, processed);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  return currentOffset;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void logWorkItemStats(const KvdoWorkItemStats *stats)
Packit Service d40955
{
Packit Service d40955
  uint64_t totalEnqueued = 0;
Packit Service d40955
  uint64_t totalProcessed = 0;
Packit Service d40955
Packit Service d40955
  const KvdoWorkFunctionTable *functionIDs = &stats->functionTable;
Packit Service d40955
Packit Service d40955
  int i;
Packit Service d40955
  for (i = 0; i < NUM_WORK_QUEUE_ITEM_STATS; i++) {
Packit Service d40955
    if (functionIDs->functions[i] == NULL) {
Packit Service d40955
      break;
Packit Service d40955
    }
Packit Service d40955
    if (atomic64_read(&stats->enqueued[i]) == 0) {
Packit Service d40955
      continue;
Packit Service d40955
    }
Packit Service d40955
    /*
Packit Service d40955
     * The reporting of all of "pending", "enqueued" and "processed"
Packit Service d40955
     * here seems redundant, but "pending" is limited to 0 in the case
Packit Service d40955
     * where "processed" exceeds "enqueued", either through current
Packit Service d40955
     * activity and a lack of synchronization when fetching stats, or
Packit Service d40955
     * a coding bug. This report is intended largely for debugging, so
Packit Service d40955
     * we'll go ahead and print the not-necessarily-redundant values.
Packit Service d40955
     */
Packit Service d40955
    uint64_t enqueued, processed;
Packit Service d40955
    unsigned int pending;
Packit Service d40955
    getWorkItemCountsByItem(stats, i, &enqueued, &processed, &pending);
Packit Service d40955
    totalEnqueued  += enqueued;
Packit Service d40955
    totalProcessed += processed;
Packit Service d40955
Packit Service d40955
    static char work[256]; // arbitrary size
Packit Service d40955
    getFunctionName(functionIDs->functions[i], work, sizeof(work));
Packit Service d40955
Packit Service d40955
    if (ENABLE_PER_FUNCTION_TIMING_STATS) {
Packit Service d40955
      uint64_t min, mean, max;
Packit Service d40955
      getWorkItemTimesByItem(stats, i, &min, &mean, &max;;
Packit Service d40955
      logInfo("  priority %d: %u pending"
Packit Service d40955
              " %llu enqueued %llu processed"
Packit Service d40955
              " %s"
Packit Service d40955
              " times %llu/%llu/%lluns",
Packit Service d40955
              functionIDs->priorities[i],
Packit Service d40955
              pending, enqueued, processed, work,
Packit Service d40955
              min, mean, max);
Packit Service d40955
    } else {
Packit Service d40955
      logInfo("  priority %d: %u pending"
Packit Service d40955
              " %llu enqueued %llu processed"
Packit Service d40955
              " %s",
Packit Service d40955
              functionIDs->priorities[i],
Packit Service d40955
              pending, enqueued, processed, work);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  if (i == NUM_WORK_QUEUE_ITEM_STATS) {
Packit Service d40955
    uint64_t enqueued, processed;
Packit Service d40955
    getOtherWorkItemCounts(stats, &enqueued, &processed);
Packit Service d40955
    if (enqueued > 0) {
Packit Service d40955
      totalEnqueued  += enqueued;
Packit Service d40955
      totalProcessed += processed;
Packit Service d40955
      logInfo("  ... others: %llu enqueued %llu processed",
Packit Service d40955
              enqueued, processed);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  logInfo("  total: %llu enqueued %llu processed",
Packit Service d40955
          totalEnqueued, totalProcessed);
Packit Service d40955
}