[Patch] statistics infrastructure - update 7

From: Martin Peschke
Date: Fri Jun 30 2006 - 10:55:52 EST


This patch adds a set of slimmed down add-functions as discussed:

- add statistic_add_as() as faster, less flexible alternative for
statistic_add()
- statistic_add_as() requires type= attribute to be unalterable
- shorten constant names (for use with statistic_add_as)

Signed-off-by: Martin Peschke <mp3@xxxxxxxxxx>
---

Documentation/statistics.txt | 52 +++++++++++++++++-
include/linux/statistic.h | 122 +++++++++++++++++++++++++++++++++++++++----
lib/statistic.c | 54 ++++++++++---------
3 files changed, 193 insertions(+), 35 deletions(-)

diff -urp a/Documentation/statistics.txt b/Documentation/statistics.txt
--- a/Documentation/statistics.txt 2006-06-30 15:57:44.000000000 +0200
+++ b/Documentation/statistics.txt 2006-06-30 16:06:19.000000000 +0200
@@ -646,6 +646,33 @@ Now, here is how to tie the knot for sta

Reporting statistics data

+In short, this is the complete list of function that can be used
+to update a statistic:
+
+ _statistic_add()
+ _statistic_inc()
+
+ statistic_add()
+ statistic_inc()
+
+ _statistic_add_as()
+ _statistic_inc_as()
+
+ statistic_add_as()
+ statistic_inc_as()
+
+ statistic_set()
+
+Function names starting with an "_" indicate that the function leaves it to
+the calling code to make updates smp-safe (see details below).
+
+The *statistic_*_as() functions are stripped down version that are faster and
+less flexible from the user's perspective (see details below).
+
+While the add/inc-functions are used for accumulating incremental statistics
+data, the set-function is used for storing statistics coming as total numbers
+(see details below).
+
Add statistic_add*() or statistic_inc*() calls where appropriate for
reporting statistics data. Data to be reported through these functions has the
form of (X, Y) as explained above:
@@ -690,10 +717,33 @@ in one go for improved performance:
local_irq_restore(flags);
}

+You may use the *statistic_*_as() functions instead if you feel that - for your
+purposes - the performance gain outweighs the flexibility of statistic_add() &
+friends. The *statistic_*_as() functions do not allow user's to change the way
+data processing is done (that is the "type=" attribute), but require the client
+to provide this information through an additional parameter passed to the
+*statistic_*_as() functions. For example, the counter named MY_ENTITY_STAT_O
+can't be inflated to a histogram at run time.
+
+ {
+ struct my_entity *one;
+ unsigned long flags;
+ ...
+
+ local_irq_save(flags);
+ _statistic_inc_as(STAT_CNTR_INC, &one->stat, MY_ENTITY_STAT_O, o);
+ _statistic_add_as(STAT_UTIL, &one->stat, MY_ENTITY_STAT_P, p, number);
+ ...
+ local_irq_restore(flags);
+ }
+
+Make sure you have set the STATISTIC_FLAGS_NOFLEX flag for statistics
+which are fed through *statistic_*_as() function to prohibit the alteration
+of the "type=" attribute.
+
The above examples show statistics that feed on incremental updates that
get accumulated by the statistics infrastructure on top of data already
gathered by the statistics infrastructure.
-That is why statistic_add() or statistic_inc() respectively are used.

There might be statistics that come as total numbers, e.g. because they feed
on counters already maintained by the client or some hardware feature.
diff -urp a/include/linux/statistic.h b/include/linux/statistic.h
--- a/include/linux/statistic.h 2006-06-30 15:57:44.000000000 +0200
+++ b/include/linux/statistic.h 2006-06-30 16:06:19.000000000 +0200
@@ -34,7 +34,7 @@
* @name: pointer to name name string
* @x_unit: pointer to string describing unit of X of (X, Y) data pair
* @y_unit: pointer to string describing unit of Y of (X, Y) data pair
- * @flags: STATISTIC_FLAGS_NOINCR means no incremental data
+ * @flags: bits describing special settings
* @defaults: pointer to string describing defaults setting for attributes
*
* Exploiters must setup an array of struct statistic_info for a
@@ -53,7 +53,8 @@ struct statistic_info {
char *x_unit;
char *y_unit;
int flags;
-#define STATISTIC_FLAGS_NOINCR 0x01
+#define STATISTIC_FLAGS_NOINCR 0x01 /* no incremental data */
+#define STATISTIC_FLAGS_NOFLEX 0x02 /* type can't be altered by user */
char *defaults;
};

@@ -66,13 +67,13 @@ enum statistic_state {
};

enum statistic_type {
- STATISTIC_TYPE_COUNTER_INC,
- STATISTIC_TYPE_COUNTER_PROD,
- STATISTIC_TYPE_UTIL,
- STATISTIC_TYPE_HISTOGRAM_LIN,
- STATISTIC_TYPE_HISTOGRAM_LOG2,
- STATISTIC_TYPE_SPARSE,
- STATISTIC_TYPE_NONE
+ STAT_CNTR_INC,
+ STAT_CNTR_PROD,
+ STAT_UTIL,
+ STAT_HGRAM_LIN,
+ STAT_HGRAM_LOG2,
+ STAT_SPARSE,
+ STAT_NONE
};

/**
@@ -127,9 +128,10 @@ struct statistic_interface {
extern int statistic_create(struct statistic_interface *, const char *);
extern int statistic_remove(struct statistic_interface *);

+extern void statistic_set(struct statistic *, int, s64, u64);
+
extern void _statistic_add(struct statistic *, int, s64, u64);
extern void statistic_add(struct statistic *, int, s64, u64);
-extern void statistic_set(struct statistic *, int, s64, u64);

#define _statistic_inc(stat, i, value) \
_statistic_add(stat, i, value, 1)
@@ -137,4 +139,104 @@ extern void statistic_set(struct statist
#define statistic_inc(stat, i, value) \
statistic_add(stat, i, value, 1)

+/*
+ * Clients are not supposed to call these directly.
+ * The declarations are needed to allow optimisation of _statistic_add_as()
+ * at compile time.
+ */
+extern void statistic_add_counter_inc(struct statistic *, s64, u64);
+extern void statistic_add_counter_prod(struct statistic *, s64, u64);
+extern void statistic_add_util(struct statistic *, s64, u64);
+extern void statistic_add_histogram_lin(struct statistic *, s64, u64);
+extern void statistic_add_histogram_log2(struct statistic *, s64, u64);
+extern void statistic_add_sparse(struct statistic *, s64, u64);
+
+/**
+ * _statistic_add_as - update statistic with incremental data in (X, Y) pair
+ * @type: data proessing mode to be used (must match statistic_info::defaults)
+ * @stat: struct statistic array
+ * @i: index of statistic to be updated
+ * @value: X
+ * @incr: Y
+ *
+ * The actual processing of the (X, Y) data pair is determined by the current
+ * definition applied to the statistic. See Documentation/statistics.txt.
+ *
+ * This function is faster than _statistic_add() because the data
+ * processing mode is already determined at compile time.
+ * Use this when you feel that the perfomance gain outweighs the loss
+ * of flexibility for your particular statistic.
+ *
+ * This variant leaves protecting per-cpu data to clients. It is preferred
+ * whenever clients update several statistics of the same entity in one go.
+ *
+ * You may want to use _statistic_inc_as() for (X, 1) data pairs.
+ */
+static inline void _statistic_add_as(int type, struct statistic *stat, int i,
+ s64 value, u64 incr)
+{
+#ifdef CONFIG_STATISTICS
+ if (stat[i].state == STATISTIC_STATE_ON) {
+ switch (type) {
+ case STAT_CNTR_INC:
+ statistic_add_counter_inc(&stat[i], value, incr);
+ break;
+ case STAT_CNTR_PROD:
+ statistic_add_counter_prod(&stat[i], value, incr);
+ break;
+ case STAT_UTIL:
+ statistic_add_util(&stat[i], value, incr);
+ break;
+ case STAT_HGRAM_LIN:
+ statistic_add_histogram_lin(&stat[i], value, incr);
+ break;
+ case STAT_HGRAM_LOG2:
+ statistic_add_histogram_log2(&stat[i], value, incr);
+ break;
+ case STAT_SPARSE:
+ statistic_add_sparse(&stat[i], value, incr);
+ break;
+ }
+ }
+#endif
+}
+
+/**
+ * statistic_add_as - update statistic with incremental data in (X, Y) pair
+ * @type: data proessing mode to be used (must match statistic_info::defaults)
+ * @stat: struct statistic array
+ * @i: index of statistic to be updated
+ * @value: X
+ * @incr: Y
+ *
+ * The actual processing of the (X, Y) data pair is determined by the current
+ * the definition applied to the statistic. See Documentation/statistics.txt.
+ *
+ * This function is faster than statistic_add() because the data
+ * processing mode is already determined at compile time.
+ * Use this when you feel that the perfomance gain outweighs the loss
+ * of flexibility for your particular statistic.
+ *
+ * This variant takes care of protecting per-cpu data. It is preferred whenever
+ * clients don't update several statistics of the same entity in one go.
+ *
+ * You may want to use statistic_inc() for (X, 1) data pairs.
+ */
+static inline void statistic_add_as(int type, struct statistic *stat, int i,
+ s64 value, u64 incr)
+{
+#ifdef CONFIG_STATISTICS
+ unsigned long flags;
+ local_irq_save(flags);
+ _statistic_add_as(type, stat, i, value, incr);
+ local_irq_restore(flags);
+#endif
+}
+
+#define _statistic_inc_as(type, stat, i, value) \
+ _statistic_add_as(type, stat, i, value, 1)
+
+#define statistic_inc_as(type, stat, i, value) \
+ statistic_add_as(type, stat, i, value, 1)
+
#endif /* STATISTIC_H */
diff -urp a/lib/statistic.c b/lib/statistic.c
--- a/lib/statistic.c 2006-06-30 15:57:44.000000000 +0200
+++ b/lib/statistic.c 2006-06-30 16:06:19.000000000 +0200
@@ -120,7 +120,7 @@ static struct statistic_discipline stati

static int statistic_initialise(struct statistic *stat)
{
- stat->type = STATISTIC_TYPE_NONE;
+ stat->type = STAT_NONE;
stat->state = STATISTIC_STATE_UNCONFIGURED;
return 0;
}
@@ -133,7 +133,7 @@ static int statistic_uninitialise(struct

static int statistic_define(struct statistic *stat)
{
- if (stat->type == STATISTIC_TYPE_NONE)
+ if (stat->type == STAT_NONE)
return -EINVAL;
stat->state = STATISTIC_STATE_RELEASED;
return 0;
@@ -384,6 +384,9 @@ static int statistic_fdef(struct statist

seg->offset += sprintf(seg->address + seg->offset, " type=%s",
disc->name);
+ if (info->flags & STATISTIC_FLAGS_NOFLEX)
+ seg->offset += sprintf(seg->address + seg->offset, "(fix)");
+
if (disc->fdef)
seg->offset += disc->fdef(stat, seg->address + seg->offset);
if (stat->state == STATISTIC_STATE_RELEASED) {
@@ -509,6 +512,9 @@ static int statistic_parse_single(struct
int prev_state = stat->state, retval = 0;
char *copy;

+ if (info->flags & STATISTIC_FLAGS_NOFLEX && stat->type != type &&
+ def != info->defaults)
+ return -EINVAL;
if (disc->parse) {
copy = kstrdup(def, GFP_KERNEL);
if (unlikely(!copy))
@@ -550,7 +556,7 @@ static int statistic_parse_match(struct
if (match_token(p, statistic_match_type, args) != 1)
continue;
len = (args[0].to - args[0].from) + 1;
- for (type = 0; type < STATISTIC_TYPE_NONE; type++) {
+ for (type = 0; type < STAT_NONE; type++) {
disc = &statistic_discs[type];
if (unlikely(strncmp(disc->name, args[0].from, len)))
continue;
@@ -559,7 +565,7 @@ static int statistic_parse_match(struct
}
}
kfree(copy);
- if (unlikely(stat->type == STATISTIC_TYPE_NONE))
+ if (unlikely(stat->type == STAT_NONE))
return -EINVAL;
return statistic_parse_single(stat, info, def, stat->type);
}
@@ -844,19 +850,19 @@ static void statistic_reset_counter(stru
*(u64*)ptr = 0;
}

-static void statistic_add_counter_inc(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_counter_inc(struct statistic *stat, s64 value, u64 incr)
{
*(u64*)stat->pdata->ptrs[smp_processor_id()] += incr;
}
+EXPORT_SYMBOL_GPL(statistic_add_counter_inc);

-static void statistic_add_counter_prod(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_counter_prod(struct statistic *stat, s64 value, u64 incr)
{
if (unlikely(value < 0))
value = -value;
*(u64*)stat->pdata->ptrs[smp_processor_id()] += value * incr;
}
+EXPORT_SYMBOL_GPL(statistic_add_counter_prod);

static void statistic_set_counter_inc(struct statistic *stat,
s64 value, u64 total)
@@ -910,8 +916,7 @@ static void statistic_reset_util(struct
util->max = LLONG_MIN;
}

-static void statistic_add_util(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_util(struct statistic *stat, s64 value, u64 incr)
{
int cpu = smp_processor_id();
struct statistic_entry_util *util = stat->pdata->ptrs[cpu];
@@ -922,6 +927,7 @@ static void statistic_add_util(struct st
if (unlikely(value > util->max))
util->max = value;
}
+EXPORT_SYMBOL_GPL(statistic_add_util);

static void statistic_set_util(struct statistic *stat, s64 value, u64 total)
{
@@ -1008,7 +1014,7 @@ static inline s64 statistic_histogram_ca

static s64 statistic_histogram_calc_value(struct statistic *stat, int i)
{
- if (stat->type == STATISTIC_TYPE_HISTOGRAM_LIN)
+ if (stat->type == STAT_HGRAM_LIN)
return statistic_histogram_calc_value_lin(stat, i);
else
return statistic_histogram_calc_value_log2(stat, i);
@@ -1037,19 +1043,19 @@ static void statistic_reset_histogram(st
memset(ptr, 0, (stat->u.histogram.last_index + 1) * sizeof(u64));
}

-static void statistic_add_histogram_lin(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_histogram_lin(struct statistic *stat, s64 value, u64 incr)
{
int i = statistic_histogram_calc_index_lin(stat, value);
((u64*)stat->pdata->ptrs[smp_processor_id()])[i] += incr;
}
+EXPORT_SYMBOL_GPL(statistic_add_histogram_lin);

-static void statistic_add_histogram_log2(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_histogram_log2(struct statistic *stat, s64 value, u64 incr)
{
int i = statistic_histogram_calc_index_log2(stat, value);
((u64*)stat->pdata->ptrs[smp_processor_id()])[i] += incr;
}
+EXPORT_SYMBOL_GPL(statistic_add_histogram_log2);

static void statistic_set_histogram_lin(struct statistic *stat,
s64 value, u64 total)
@@ -1253,13 +1259,13 @@ static void _statistic_add_sparse(struct
slist->hits_missed += incr;
}

-static void statistic_add_sparse(struct statistic *stat,
- s64 value, u64 incr)
+void statistic_add_sparse(struct statistic *stat, s64 value, u64 incr)
{
int cpu = smp_processor_id();
struct statistic_sparse_list *slist = stat->pdata->ptrs[cpu];
_statistic_add_sparse(slist, value, incr);
}
+EXPORT_SYMBOL_GPL(statistic_add_sparse);

static void statistic_set_sparse(struct statistic *stat, s64 value, u64 total)
{
@@ -1347,7 +1353,7 @@ static int statistic_parse_sparse(struct
/* code mostly concerned with managing statistics */

static struct statistic_discipline statistic_discs[] = {
- [STATISTIC_TYPE_COUNTER_INC] = {
+ [STAT_CNTR_INC] = {
.alloc = statistic_alloc_generic,
.free = statistic_free_generic,
.reset = statistic_reset_counter,
@@ -1358,7 +1364,7 @@ static struct statistic_discipline stati
.name = "counter_inc",
.size = sizeof(u64)
},
- [STATISTIC_TYPE_COUNTER_PROD] = {
+ [STAT_CNTR_PROD] = {
.alloc = statistic_alloc_generic,
.free = statistic_free_generic,
.reset = statistic_reset_counter,
@@ -1369,7 +1375,7 @@ static struct statistic_discipline stati
.name = "counter_prod",
.size = sizeof(u64)
},
- [STATISTIC_TYPE_UTIL] = {
+ [STAT_UTIL] = {
.alloc = statistic_alloc_generic,
.free = statistic_free_generic,
.reset = statistic_reset_util,
@@ -1380,7 +1386,7 @@ static struct statistic_discipline stati
.name = "utilisation",
.size = sizeof(struct statistic_entry_util)
},
- [STATISTIC_TYPE_HISTOGRAM_LIN] = {
+ [STAT_HGRAM_LIN] = {
.parse = statistic_parse_histogram,
.alloc = statistic_alloc_histogram,
.free = statistic_free_generic,
@@ -1393,7 +1399,7 @@ static struct statistic_discipline stati
.name = "histogram_lin",
.size = sizeof(u64)
},
- [STATISTIC_TYPE_HISTOGRAM_LOG2] = {
+ [STAT_HGRAM_LOG2] = {
.parse = statistic_parse_histogram,
.alloc = statistic_alloc_histogram,
.free = statistic_free_generic,
@@ -1406,7 +1412,7 @@ static struct statistic_discipline stati
.name = "histogram_log2",
.size = sizeof(u64)
},
- [STATISTIC_TYPE_SPARSE] = {
+ [STAT_SPARSE] = {
.parse = statistic_parse_sparse,
.alloc = statistic_alloc_sparse,
.free = statistic_free_sparse,
@@ -1419,7 +1425,7 @@ static struct statistic_discipline stati
.name = "sparse",
.size = sizeof(struct statistic_sparse_list)
},
- [STATISTIC_TYPE_NONE] = {}
+ [STAT_NONE] = {}
};

/* programming interface functions */


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/