Blame source/vdo/kernel/histogram.c

Packit Service cbade1
/*
Packit Service cbade1
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service cbade1
 *
Packit Service cbade1
 * This program is free software; you can redistribute it and/or
Packit Service cbade1
 * modify it under the terms of the GNU General Public License
Packit Service cbade1
 * as published by the Free Software Foundation; either version 2
Packit Service cbade1
 * of the License, or (at your option) any later version.
Packit Service cbade1
 * 
Packit Service cbade1
 * This program is distributed in the hope that it will be useful,
Packit Service cbade1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cbade1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cbade1
 * GNU General Public License for more details.
Packit Service cbade1
 * 
Packit Service cbade1
 * You should have received a copy of the GNU General Public License
Packit Service cbade1
 * along with this program; if not, write to the Free Software
Packit Service cbade1
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service cbade1
 * 02110-1301, USA. 
Packit Service cbade1
 *
Packit Service cbade1
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/histogram.c#2 $
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
#include <linux/kobject.h>
Packit Service cbade1
Packit Service cbade1
#include "memoryAlloc.h"
Packit Service cbade1
#include "typeDefs.h"
Packit Service cbade1
Packit Service cbade1
#include "histogram.h"
Packit Service cbade1
#include "logger.h"
Packit Service cbade1
#include "numUtils.h"
Packit Service cbade1
Packit Service cbade1
/*
Packit Service cbade1
 * Set NO_BUCKETS to streamline the histogram code by reducing it to
Packit Service cbade1
 * tracking just minimum, maximum, mean, etc. Only one bucket counter
Packit Service cbade1
 * (the final one for "bigger" values) will be used, no range checking
Packit Service cbade1
 * is needed to find the right bucket, and no histogram will be
Packit Service cbade1
 * reported. With newer compilers, the histogram output code will be
Packit Service cbade1
 * optimized out.
Packit Service cbade1
 */
Packit Service cbade1
enum {
Packit Service cbade1
  NO_BUCKETS = 1
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
/*
Packit Service cbade1
 * Support histogramming in the VDO code.
Packit Service cbade1
 *
Packit Service cbade1
 * This is not a complete and general histogram package.  It follows the XP
Packit Service cbade1
 * practice of implementing the "customer" requirements, and no more.  We can
Packit Service cbade1
 * support other requirements after we know what they are.
Packit Service cbade1
 *
Packit Service cbade1
 * The code was originally borrowed from Albireo, and includes both linear and
Packit Service cbade1
 * logarithmic histograms.  VDO only uses the logarithmic histograms.
Packit Service cbade1
 *
Packit Service cbade1
 * All samples are uint64_t values.
Packit Service cbade1
 *
Packit Service cbade1
 * A unit conversion option is supported internally to allow sample values to
Packit Service cbade1
 * be supplied in "jiffies" and results to be reported via /sys in
Packit Service cbade1
 * milliseconds. Depending on the system configuration, this could mean a
Packit Service cbade1
 * factor of four (a bucket for values of 1 jiffy is reported as 4-7
Packit Service cbade1
 * milliseconds). In theory it could be a non-integer ratio (including less
Packit Service cbade1
 * than one), but as the x86-64 platforms we've encountered appear to use 1 or
Packit Service cbade1
 * 4 milliseconds per jiffy, we don't support non-integer values yet.
Packit Service cbade1
 *
Packit Service cbade1
 * All internal processing uses the values as passed to enterHistogramSample.
Packit Service cbade1
 * Conversions only affect the values seen or input through the /sys interface,
Packit Service cbade1
 * including possibly rounding a "limit" value entered.
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
struct histogram {
Packit Service cbade1
  // These fields are ordered so that enterHistogramSample touches
Packit Service cbade1
  // only the first cache line.
Packit Service cbade1
  atomic64_t     *counters;     // Counter for each bucket
Packit Service cbade1
  uint64_t        limit;        // We want to know how many samples are larger
Packit Service cbade1
  atomic64_t      sum;          // Sum of all the samples
Packit Service cbade1
  atomic64_t      count;        // Number of samples
Packit Service cbade1
  atomic64_t      minimum;      // Minimum value
Packit Service cbade1
  atomic64_t      maximum;      // Maximum value
Packit Service cbade1
  atomic64_t      unacceptable; // Number of samples that exceed the limit
Packit Service cbade1
  int             numBuckets;   // The number of buckets
Packit Service cbade1
  bool            logFlag;      // True if the y scale should be logarithmic
Packit Service cbade1
  // These fields are used only when reporting results.
Packit Service cbade1
  const char     *label;        // Histogram label
Packit Service cbade1
  const char     *countedItems; // Name for things being counted
Packit Service cbade1
  const char     *metric;       // Term for value used to divide into buckets
Packit Service cbade1
  const char     *sampleUnits;  // Unit for measuring metric; NULL for count
Packit Service cbade1
  unsigned int    conversionFactor; // Converts input units to reporting units
Packit Service cbade1
  struct kobject  kobj;
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
/*
Packit Service cbade1
 * Fixed table defining the top value for each bucket of a logarithmic
Packit Service cbade1
 * histogram.  We arbitrarily limit the histogram to 12 orders of magnitude.
Packit Service cbade1
 */
Packit Service cbade1
enum { MAX_LOG_SIZE = 12 };
Packit Service cbade1
static const uint64_t bottomValue[1 + 10 * MAX_LOG_SIZE] = {
Packit Service cbade1
  // 0 to 10 - The first 10 buckets are linear
Packit Service cbade1
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
Packit Service cbade1
  // 10 to 100 - From this point on, the Nth entry of the table is
Packit Service cbade1
  //             floor(exp10((double)N/10.0)).
Packit Service cbade1
  12, 15, 19, 25, 31, 39, 50, 63, 79, 100,
Packit Service cbade1
  // 100 to 1K
Packit Service cbade1
  125, 158, 199, 251, 316, 398, 501, 630, 794, 1000,
Packit Service cbade1
  // 1K to 10K
Packit Service cbade1
  1258, 1584, 1995, 2511, 3162, 3981, 5011, 6309, 7943, 10000,
Packit Service cbade1
  // 10K to 100K
Packit Service cbade1
  12589, 15848, 19952, 25118, 31622, 39810, 50118, 63095, 79432, 100000,
Packit Service cbade1
  // 100K to 1M
Packit Service cbade1
  125892, 158489, 199526, 251188, 316227,
Packit Service cbade1
  398107, 501187, 630957, 794328, 1000000,
Packit Service cbade1
  // 1M to 10M
Packit Service cbade1
  1258925, 1584893, 1995262, 2511886, 3162277,
Packit Service cbade1
  3981071, 5011872, 6309573, 7943282, 10000000,
Packit Service cbade1
  // 10M to 100M
Packit Service cbade1
  12589254, 15848931, 19952623, 25118864, 31622776,
Packit Service cbade1
  39810717, 50118723, 63095734, 79432823, 100000000,
Packit Service cbade1
  // 100M to 1G
Packit Service cbade1
  125892541, 158489319, 199526231, 251188643, 316227766,
Packit Service cbade1
  398107170, 501187233, 630957344, 794328234, 1000000000,
Packit Service cbade1
  // 1G to 10G
Packit Service cbade1
  1258925411L, 1584893192L, 1995262314L, 2511886431L, 3162277660L,
Packit Service cbade1
  3981071705L, 5011872336L, 6309573444L, 7943282347L, 10000000000L,
Packit Service cbade1
  // 10G to 100G
Packit Service cbade1
  12589254117L, 15848931924L, 19952623149L, 25118864315L, 31622776601L,
Packit Service cbade1
  39810717055L, 50118723362L, 63095734448L, 79432823472L, 100000000000L,
Packit Service cbade1
  // 100G to 1T
Packit Service cbade1
  125892541179L, 158489319246L, 199526231496L, 251188643150L, 316227766016L,
Packit Service cbade1
  398107170553L, 501187233627L, 630957344480L, 794328234724L, 1000000000000L,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static unsigned int divideRoundingToNearest(uint64_t number, uint64_t divisor)
Packit Service cbade1
{
Packit Service cbade1
  number += divisor / 2;
Packit Service cbade1
  return number / divisor;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static int maxBucket(Histogram *h)
Packit Service cbade1
{
Packit Service cbade1
  int max = h->numBuckets;
Packit Service cbade1
  while ((max >= 0) && (atomic64_read(&h->counters[max]) == 0)) {
Packit Service cbade1
    max--;
Packit Service cbade1
  }
Packit Service cbade1
  // max == -1 means that there were no samples
Packit Service cbade1
  return max;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
Packit Service cbade1
typedef struct {
Packit Service cbade1
  struct attribute attr;
Packit Service cbade1
  ssize_t (*show)(Histogram *h, char *buf);
Packit Service cbade1
  ssize_t (*store)(Histogram *h, const char *buf, size_t length);
Packit Service cbade1
} HistogramAttribute;
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static void histogramKobjRelease(struct kobject *kobj)
Packit Service cbade1
{
Packit Service cbade1
  Histogram *h = container_of(kobj, Histogram, kobj);
Packit Service cbade1
  FREE(h->counters);
Packit Service cbade1
  FREE(h);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShow(struct kobject   *kobj,
Packit Service cbade1
                             struct attribute *attr,
Packit Service cbade1
                             char             *buf)
Packit Service cbade1
{
Packit Service cbade1
  HistogramAttribute *ha = container_of(attr, HistogramAttribute, attr);
Packit Service cbade1
  if (ha->show == NULL) {
Packit Service cbade1
    return -EINVAL;
Packit Service cbade1
  }
Packit Service cbade1
  Histogram *h = container_of(kobj, Histogram, kobj);
Packit Service cbade1
  return ha->show(h, buf);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramStore(struct kobject   *kobj,
Packit Service cbade1
                              struct attribute *attr,
Packit Service cbade1
                              const char       *buf,
Packit Service cbade1
                              size_t            length)
Packit Service cbade1
{
Packit Service cbade1
  HistogramAttribute *ha = container_of(attr, HistogramAttribute, attr);
Packit Service cbade1
  if (ha->show == NULL) {
Packit Service cbade1
    return -EINVAL;
Packit Service cbade1
  }
Packit Service cbade1
  Histogram *h = container_of(kobj, Histogram, kobj);
Packit Service cbade1
  return ha->store(h, buf, length);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowCount(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  int64_t count = atomic64_read(&h->count);
Packit Service cbade1
  return sprintf(buf, "%" PRId64 "\n", count);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowHistogram(Histogram *h, char *buffer)
Packit Service cbade1
{
Packit Service cbade1
  /*
Packit Service cbade1
   * We're given one page in which to write. The caller logs a complaint if we
Packit Service cbade1
   * report that we've written too much, so we'll truncate to PAGE_SIZE-1.
Packit Service cbade1
   */
Packit Service cbade1
  size_t bufferSize = PAGE_SIZE;
Packit Service cbade1
  bool bars = true;
Packit Service cbade1
  ssize_t length = 0;
Packit Service cbade1
  int max = maxBucket(h);
Packit Service cbade1
  // If max is -1, we'll fall through to reporting the total of zero.
Packit Service cbade1
Packit Service cbade1
  enum { BAR_SIZE = 50 };
Packit Service cbade1
  char bar[BAR_SIZE + 2];
Packit Service cbade1
  bar[0] = ' ';
Packit Service cbade1
  memset(bar + 1, '=', BAR_SIZE);
Packit Service cbade1
  bar[BAR_SIZE + 1] = '\0';
Packit Service cbade1
Packit Service cbade1
  uint64_t total = 0;
Packit Service cbade1
  for (int i = 0; i <= max; i++) {
Packit Service cbade1
    total += atomic64_read(&h->counters[i]);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  length += snprintf(buffer, bufferSize, "%s Histogram - number of %s by %s",
Packit Service cbade1
                     h->label, h->countedItems, h->metric);
Packit Service cbade1
  if (length >= (bufferSize - 1)) {
Packit Service cbade1
    return bufferSize - 1;
Packit Service cbade1
  }
Packit Service cbade1
  if (h->sampleUnits != NULL) {
Packit Service cbade1
    length += snprintf(buffer + length, bufferSize - length, " (%s)",
Packit Service cbade1
                       h->sampleUnits);
Packit Service cbade1
    if (length >= (bufferSize - 1)) {
Packit Service cbade1
      return bufferSize - 1;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
  length += snprintf(buffer + length, bufferSize - length, "\n");
Packit Service cbade1
  if (length >= (bufferSize - 1)) {
Packit Service cbade1
    return bufferSize - 1;
Packit Service cbade1
  }
Packit Service cbade1
  for (int i = 0; i <= max; i++) {
Packit Service cbade1
    uint64_t value = atomic64_read(&h->counters[i]);
Packit Service cbade1
Packit Service cbade1
    unsigned int barLength;
Packit Service cbade1
    if (bars && (total != 0)) {
Packit Service cbade1
      // +1 for the space at the beginning
Packit Service cbade1
      barLength = (divideRoundingToNearest(value * BAR_SIZE, total) + 1);
Packit Service cbade1
      if (barLength == 1) {
Packit Service cbade1
        // Don't bother printing just the initial space.
Packit Service cbade1
        barLength = 0;
Packit Service cbade1
      }
Packit Service cbade1
    } else {
Packit Service cbade1
      // 0 means skip the space and the bar
Packit Service cbade1
      barLength = 0;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    if (h->logFlag) {
Packit Service cbade1
      if (i == h->numBuckets) {
Packit Service cbade1
        length += snprintf(buffer + length, bufferSize - length, "%-16s",
Packit Service cbade1
                           "Bigger");
Packit Service cbade1
      } else {
Packit Service cbade1
        unsigned int lower = h->conversionFactor * bottomValue[i];
Packit Service cbade1
        unsigned int upper = h->conversionFactor * bottomValue[i + 1] - 1;
Packit Service cbade1
        length += snprintf(buffer + length, bufferSize - length, "%6u - %7u",
Packit Service cbade1
                           lower, upper);
Packit Service cbade1
      }
Packit Service cbade1
    } else {
Packit Service cbade1
      if (i == h->numBuckets) {
Packit Service cbade1
        length += snprintf(buffer + length, bufferSize - length, "%6s",
Packit Service cbade1
                           "Bigger");
Packit Service cbade1
      } else {
Packit Service cbade1
        length += snprintf(buffer + length, bufferSize - length, "%6d", i);
Packit Service cbade1
      }
Packit Service cbade1
    }
Packit Service cbade1
    if (length >= (bufferSize - 1)) {
Packit Service cbade1
      return bufferSize - 1;
Packit Service cbade1
    }
Packit Service cbade1
    length += snprintf(buffer + length, bufferSize - length,
Packit Service cbade1
                       " : %12llu%.*s\n", value, barLength, bar);
Packit Service cbade1
    if (length >= (bufferSize - 1)) {
Packit Service cbade1
      return bufferSize - 1;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  length += snprintf(buffer + length, bufferSize - length,
Packit Service cbade1
                     "total %llu\n", total);
Packit Service cbade1
  return minSizeT(bufferSize - 1, length);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowMaximum(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  // Maximum is initialized to 0.
Packit Service cbade1
  unsigned long value = atomic64_read(&h->maximum);
Packit Service cbade1
  return sprintf(buf, "%lu\n", h->conversionFactor * value);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowMinimum(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  // Minimum is initialized to -1.
Packit Service cbade1
  unsigned long value = ((atomic64_read(&h->count) > 0)
Packit Service cbade1
                         ? atomic64_read(&h->minimum)
Packit Service cbade1
                         : 0);
Packit Service cbade1
  return sprintf(buf, "%lu\n", h->conversionFactor * value);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowLimit(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  // Display the limit in the reporting units
Packit Service cbade1
  return sprintf(buf, "%u\n", (unsigned int)(h->conversionFactor * h->limit));
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramStoreLimit(Histogram  *h,
Packit Service cbade1
                                   const char *buf,
Packit Service cbade1
                                   size_t      length)
Packit Service cbade1
{
Packit Service cbade1
  unsigned int value;
Packit Service cbade1
  if ((length > 12) || (sscanf(buf, "%u", &value) != 1)) {
Packit Service cbade1
    return -EINVAL;
Packit Service cbade1
  }
Packit Service cbade1
  /*
Packit Service cbade1
   * Convert input from reporting units (e.g., milliseconds) to internal
Packit Service cbade1
   * recording units (e.g., jiffies).
Packit Service cbade1
   *
Packit Service cbade1
   * computeBucketCount could also be called "divideRoundingUp".
Packit Service cbade1
   */
Packit Service cbade1
  h->limit = computeBucketCount(value, h->conversionFactor);
Packit Service cbade1
  atomic64_set(&h->unacceptable, 0);
Packit Service cbade1
  return length;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowMean(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  uint64_t count = atomic64_read(&h->count);
Packit Service cbade1
  if (count == 0) {
Packit Service cbade1
    return sprintf(buf, "0/0\n");
Packit Service cbade1
  }
Packit Service cbade1
  // Compute mean, scaled up by 1000, in reporting units
Packit Service cbade1
  unsigned long sumTimes1000InReportingUnits
Packit Service cbade1
    = h->conversionFactor * atomic64_read(&h->sum) * 1000;
Packit Service cbade1
  unsigned int meanTimes1000
Packit Service cbade1
    = divideRoundingToNearest(sumTimes1000InReportingUnits, count);
Packit Service cbade1
  // Print mean with fractional part
Packit Service cbade1
  return sprintf(buf, "%u.%03u\n", meanTimes1000 / 1000,
Packit Service cbade1
                 meanTimes1000 % 1000);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowUnacceptable(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  int64_t count = atomic64_read(&h->unacceptable);
Packit Service cbade1
  return sprintf(buf, "%" PRId64 "\n", count);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowLabel(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  return sprintf(buf, "%s\n", h->label);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static ssize_t histogramShowUnit(Histogram *h, char *buf)
Packit Service cbade1
{
Packit Service cbade1
  if (h->sampleUnits != NULL) {
Packit Service cbade1
    return sprintf(buf, "%s\n", h->sampleUnits);
Packit Service cbade1
  } else {
Packit Service cbade1
    *buf = 0;
Packit Service cbade1
    return 0;
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
Packit Service cbade1
static struct sysfs_ops histogramSysfsOps = {
Packit Service cbade1
  .show  = histogramShow,
Packit Service cbade1
  .store = histogramStore,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute countAttribute = {
Packit Service cbade1
  .attr = { .name = "count", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowCount,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute histogramAttribute = {
Packit Service cbade1
  .attr = { .name = "histogram", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowHistogram,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute labelAttribute = {
Packit Service cbade1
  .attr = { .name = "label", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowLabel,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute maximumAttribute = {
Packit Service cbade1
  .attr = { .name = "maximum", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowMaximum,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute minimumAttribute = {
Packit Service cbade1
  .attr = { .name = "minimum", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowMinimum,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute limitAttribute = {
Packit Service cbade1
  .attr  = { .name = "limit", .mode = 0644, },
Packit Service cbade1
  .show  = histogramShowLimit,
Packit Service cbade1
  .store = histogramStoreLimit,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute meanAttribute = {
Packit Service cbade1
  .attr = { .name = "mean", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowMean,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute unacceptableAttribute = {
Packit Service cbade1
  .attr = { .name = "unacceptable", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowUnacceptable,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static HistogramAttribute unitAttribute = {
Packit Service cbade1
  .attr = { .name = "unit", .mode = 0444, },
Packit Service cbade1
  .show = histogramShowUnit,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
// "Real" histogram plotting.
Packit Service cbade1
static struct attribute *histogramAttributes[] = {
Packit Service cbade1
  &countAttribute.attr,
Packit Service cbade1
  &histogramAttribute.attr,
Packit Service cbade1
  &labelAttribute.attr,
Packit Service cbade1
  &limitAttribute.attr,
Packit Service cbade1
  &maximumAttribute.attr,
Packit Service cbade1
  &meanAttribute.attr,
Packit Service cbade1
  &minimumAttribute.attr,
Packit Service cbade1
  &unacceptableAttribute.attr,
Packit Service cbade1
  &unitAttribute.attr,
Packit Service cbade1
  NULL,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static struct kobj_type histogramKobjType = {
Packit Service cbade1
  .release       = histogramKobjRelease,
Packit Service cbade1
  .sysfs_ops     = &histogramSysfsOps,
Packit Service cbade1
  .default_attrs = histogramAttributes,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static struct attribute *bucketlessHistogramAttributes[] = {
Packit Service cbade1
  &countAttribute.attr,
Packit Service cbade1
  &labelAttribute.attr,
Packit Service cbade1
  &maximumAttribute.attr,
Packit Service cbade1
  &meanAttribute.attr,
Packit Service cbade1
  &minimumAttribute.attr,
Packit Service cbade1
  &unitAttribute.attr,
Packit Service cbade1
  NULL,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
static struct kobj_type bucketlessHistogramKobjType = {
Packit Service cbade1
  .release       = histogramKobjRelease,
Packit Service cbade1
  .sysfs_ops     = &histogramSysfsOps,
Packit Service cbade1
  .default_attrs = bucketlessHistogramAttributes,
Packit Service cbade1
};
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
static Histogram *makeHistogram(struct kobject *parent,
Packit Service cbade1
                                const char     *name,
Packit Service cbade1
                                const char     *label,
Packit Service cbade1
                                const char     *countedItems,
Packit Service cbade1
                                const char     *metric,
Packit Service cbade1
                                const char     *sampleUnits,
Packit Service cbade1
                                int             numBuckets,
Packit Service cbade1
                                unsigned long   conversionFactor,
Packit Service cbade1
                                bool            logFlag)
Packit Service cbade1
{
Packit Service cbade1
  Histogram *h;
Packit Service cbade1
  if (ALLOCATE(1, Histogram, "histogram", &h) != UDS_SUCCESS) {
Packit Service cbade1
    return NULL;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (NO_BUCKETS) {
Packit Service cbade1
    numBuckets = 0;             // plus 1 for "bigger" bucket
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  if (numBuckets <= 10) {
Packit Service cbade1
    /*
Packit Service cbade1
     * The first buckets in a "logarithmic" histogram are still
Packit Service cbade1
     * linear, but the bucket-search mechanism is a wee bit slower
Packit Service cbade1
     * than for linear, so change the type.
Packit Service cbade1
     */
Packit Service cbade1
    logFlag = false;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  h->label            = label;
Packit Service cbade1
  h->countedItems     = countedItems;
Packit Service cbade1
  h->metric           = metric;
Packit Service cbade1
  h->sampleUnits      = sampleUnits;
Packit Service cbade1
  h->logFlag          = logFlag;
Packit Service cbade1
  h->numBuckets       = numBuckets;
Packit Service cbade1
  h->conversionFactor = conversionFactor;
Packit Service cbade1
  atomic64_set(&h->minimum, -1UL);
Packit Service cbade1
Packit Service cbade1
  if (ALLOCATE(h->numBuckets + 1, atomic64_t, "histogram counters",
Packit Service cbade1
               &h->counters) != UDS_SUCCESS) {
Packit Service cbade1
    histogramKobjRelease(&h->kobj);
Packit Service cbade1
    return NULL;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  kobject_init(&h->kobj,
Packit Service cbade1
               ((numBuckets > 0)
Packit Service cbade1
                ? &histogramKobjType
Packit Service cbade1
                : &bucketlessHistogramKobjType));
Packit Service cbade1
  if (kobject_add(&h->kobj, parent, name) != 0) {
Packit Service cbade1
    histogramKobjRelease(&h->kobj);
Packit Service cbade1
    return NULL;
Packit Service cbade1
  }
Packit Service cbade1
  return h;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
Histogram *makeLinearHistogram(struct kobject *parent,
Packit Service cbade1
                               const char     *name,
Packit Service cbade1
                               const char     *initLabel,
Packit Service cbade1
                               const char     *countedItems,
Packit Service cbade1
                               const char     *metric,
Packit Service cbade1
                               const char     *sampleUnits,
Packit Service cbade1
                               int             size)
Packit Service cbade1
{
Packit Service cbade1
  return makeHistogram(parent, name, initLabel, countedItems,
Packit Service cbade1
                       metric, sampleUnits, size, 1, false);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Intermediate routine for creating logarithmic histograms.
Packit Service cbade1
 *
Packit Service cbade1
 * Limits the histogram size, and computes the bucket count from the
Packit Service cbade1
 * orders-of-magnitude count.
Packit Service cbade1
 *
Packit Service cbade1
 * @param parent            The parent kobject.
Packit Service cbade1
 * @param name              The short name of the histogram.  This label is
Packit Service cbade1
 *                          used for the sysfs node.
Packit Service cbade1
 * @param initLabel         The label for the sampled data.  This label is used
Packit Service cbade1
 *                          when we plot the data.
Packit Service cbade1
 * @param countedItems      A name (plural) for the things being counted.
Packit Service cbade1
 * @param metric            The measure being used to divide samples into
Packit Service cbade1
 *                          buckets.
Packit Service cbade1
 * @param sampleUnits       The units (plural) for the metric, or NULL if it's
Packit Service cbade1
 *                          a simple counter.
Packit Service cbade1
 * @param logSize           The number of buckets.  There are buckets for a
Packit Service cbade1
 *                          range of sizes up to 10^logSize, and an extra
Packit Service cbade1
 *                          bucket for larger samples.
Packit Service cbade1
 * @param conversionFactor  Unit conversion factor for reporting.
Packit Service cbade1
 *
Packit Service cbade1
 * @return the histogram
Packit Service cbade1
 **/
Packit Service cbade1
static Histogram *
Packit Service cbade1
makeLogarithmicHistogramWithConversionFactor(struct kobject *parent,
Packit Service cbade1
                                             const char     *name,
Packit Service cbade1
                                             const char     *initLabel,
Packit Service cbade1
                                             const char     *countedItems,
Packit Service cbade1
                                             const char     *metric,
Packit Service cbade1
                                             const char     *sampleUnits,
Packit Service cbade1
                                             int             logSize,
Packit Service cbade1
                                             uint64_t        conversionFactor)
Packit Service cbade1
{
Packit Service cbade1
  if (logSize > MAX_LOG_SIZE) {
Packit Service cbade1
    logSize = MAX_LOG_SIZE;
Packit Service cbade1
  }
Packit Service cbade1
  return makeHistogram(parent, name,
Packit Service cbade1
                       initLabel, countedItems, metric, sampleUnits,
Packit Service cbade1
                       10 * logSize, conversionFactor, true);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
Histogram *makeLogarithmicHistogram(struct kobject *parent,
Packit Service cbade1
                                    const char     *name,
Packit Service cbade1
                                    const char     *initLabel,
Packit Service cbade1
                                    const char     *countedItems,
Packit Service cbade1
                                    const char     *metric,
Packit Service cbade1
                                    const char     *sampleUnits,
Packit Service cbade1
                                    int             logSize)
Packit Service cbade1
{
Packit Service cbade1
  return makeLogarithmicHistogramWithConversionFactor(parent, name, initLabel,
Packit Service cbade1
                                                      countedItems,
Packit Service cbade1
                                                      metric, sampleUnits,
Packit Service cbade1
                                                      logSize, 1);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
Histogram *makeLogarithmicJiffiesHistogram(struct kobject *parent,
Packit Service cbade1
                                           const char     *name,
Packit Service cbade1
                                           const char     *initLabel,
Packit Service cbade1
                                           const char     *countedItems,
Packit Service cbade1
                                           const char     *metric,
Packit Service cbade1
                                           int             logSize)
Packit Service cbade1
{
Packit Service cbade1
  /*
Packit Service cbade1
   * If these fail, we have a jiffy duration that is not an integral number of
Packit Service cbade1
   * milliseconds, and the unit conversion code needs updating.
Packit Service cbade1
   */
Packit Service cbade1
  STATIC_ASSERT(HZ <= MSEC_PER_SEC);
Packit Service cbade1
  STATIC_ASSERT((MSEC_PER_SEC % HZ) == 0);
Packit Service cbade1
  return makeLogarithmicHistogramWithConversionFactor(parent, name, initLabel,
Packit Service cbade1
                                                      countedItems,
Packit Service cbade1
                                                      metric, "milliseconds",
Packit Service cbade1
                                                      logSize,
Packit Service cbade1
                                                      jiffies_to_msecs(1));
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
void enterHistogramSample(Histogram *h, uint64_t sample)
Packit Service cbade1
{
Packit Service cbade1
  int bucket;
Packit Service cbade1
  if (h->logFlag) {
Packit Service cbade1
    int lo = 0;
Packit Service cbade1
    int hi = h->numBuckets;
Packit Service cbade1
    while (lo < hi) {
Packit Service cbade1
      int middle = (lo + hi) / 2;
Packit Service cbade1
      if (sample < bottomValue[middle + 1]) {
Packit Service cbade1
        hi = middle;
Packit Service cbade1
      } else {
Packit Service cbade1
        lo = middle + 1;
Packit Service cbade1
      }
Packit Service cbade1
    }
Packit Service cbade1
    bucket = lo;
Packit Service cbade1
  } else {
Packit Service cbade1
    bucket = sample < h->numBuckets ? sample : h->numBuckets;
Packit Service cbade1
  }
Packit Service cbade1
  atomic64_inc(&h->counters[bucket]);
Packit Service cbade1
  atomic64_inc(&h->count);
Packit Service cbade1
  atomic64_add(sample, &h->sum);
Packit Service cbade1
  if ((h->limit > 0) && (sample > h->limit)) {
Packit Service cbade1
    atomic64_inc(&h->unacceptable);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  /*
Packit Service cbade1
   * Theoretically this could loop a lot; in practice it should rarely
Packit Service cbade1
   * do more than a single read, with no memory barrier, from a cache
Packit Service cbade1
   * line we've already referenced above.
Packit Service cbade1
   */
Packit Service cbade1
  uint64_t oldMaximum = atomic64_read(&h->maximum);
Packit Service cbade1
  while (oldMaximum < sample) {
Packit Service cbade1
    uint64_t readValue = atomic64_cmpxchg(&h->maximum, oldMaximum, sample);
Packit Service cbade1
    if (readValue == oldMaximum) {
Packit Service cbade1
      break;
Packit Service cbade1
    }
Packit Service cbade1
    oldMaximum = readValue;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  uint64_t oldMinimum = atomic64_read(&h->minimum);
Packit Service cbade1
  while (oldMinimum > sample) {
Packit Service cbade1
    uint64_t readValue = atomic64_cmpxchg(&h->minimum, oldMinimum, sample);
Packit Service cbade1
    if (readValue == oldMinimum) {
Packit Service cbade1
      break;
Packit Service cbade1
    }
Packit Service cbade1
    oldMinimum = readValue;
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/***********************************************************************/
Packit Service cbade1
void freeHistogram(Histogram **hp)
Packit Service cbade1
{
Packit Service cbade1
  if (*hp != NULL) {
Packit Service cbade1
    Histogram *h = *hp;
Packit Service cbade1
    kobject_put(&h->kobj);
Packit Service cbade1
    *hp = NULL;
Packit Service cbade1
  }
Packit Service cbade1
}