Add a simple mechanism for exporting percpu data through metricfs. The API follows the existing metricfs pattern. A percpu file is defined with: METRIC_EXPORT_PERCPU_INT(name, desc, fn) METRIC_EXPORT_PERCPU_COUNTER(name, desc, fn) The first defines a file for exposing a percpu int. The second is similar, but is for a counter that accumulates since boot. The 'name' is used as the metricfs file. The 'desc' is a description of the metric. The 'fn' is a callback function to emit a single percpu value: void (*fn)(struct metric_emitter *e, int cpu); The callback must call METRIC_EMIT_PERCPU_INT with the value for the specified CPU. Signed-off-by: Jonathan Adams <jwadams@xxxxxxxxxx> --- jwadams@xxxxxxxxxx: rebased to 5.6-pre6, renamed funcs to start with metric_. This is work originally done by another engineer at google, who would rather not have their name associated with this patchset. They're okay with me sending it under my name. --- include/linux/metricfs.h | 28 +++++++++++++++++++ kernel/metricfs.c | 58 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/include/linux/metricfs.h b/include/linux/metricfs.h index 65a1baa8e8c1..f103dc8c44ec 100644 --- a/include/linux/metricfs.h +++ b/include/linux/metricfs.h @@ -22,6 +22,19 @@ void metric_exit_##name(void) \ metric_unregister(metric_##name); \ } +#define METRIC_EXPORT_PERCPU(name, desc, fn, cumulative) \ +static struct metric *metric_##name; \ +void metric_init_##name(struct metricfs_subsys *parent) \ +{ \ + metric_##name = metric_register_percpu(__stringify(name), (parent), \ + (desc), (fn), \ + (cumulative), THIS_MODULE); \ +} \ +void metric_exit_##name(void) \ +{ \ + metric_unregister(metric_##name); \ +} + /* * Metricfs only deals with two types: int64_t and const char*. * @@ -47,6 +60,11 @@ void metric_exit_##name(void) \ METRIC_EXPORT_GENERIC(name, (desc), (fname0), (fname1), (fn), \ true, false) +#define METRIC_EXPORT_PERCPU_INT(name, desc, fn) \ + METRIC_EXPORT_PERCPU(name, (desc), (fn), false) +#define METRIC_EXPORT_PERCPU_COUNTER(name, desc, fn) \ + METRIC_EXPORT_PERCPU(name, (desc), (fn), true) + /* Subsystem support. */ /* Pass NULL as 'parent' to create a new top-level subsystem. */ struct metricfs_subsys *metricfs_create_subsys(const char *name, @@ -69,6 +87,8 @@ struct metric_emitter; metric_emit_int_value((e), (v), (f0), (f1)) #define METRIC_EMIT_STR(e, v, f0, f1) \ metric_emit_str_value((e), (v), (f0), (f1)) +#define METRIC_EMIT_PERCPU_INT(e, cpu, v) \ + metric_emit_percpu_int_value((e), (cpu), (v)) /* Users don't have to call any functions below; * use the macro definitions above instead. @@ -77,6 +97,7 @@ void metric_emit_int_value(struct metric_emitter *e, int64_t v, const char *f0, const char *f1); void metric_emit_str_value(struct metric_emitter *e, const char *v, const char *f0, const char *f1); +void metric_emit_percpu_int_value(struct metric_emitter *e, int cpu, int64_t v); struct metric *metric_register(const char *name, struct metricfs_subsys *parent, @@ -98,6 +119,13 @@ struct metric *metric_register_parm(const char *name, bool is_cumulative, struct module *owner); +struct metric *metric_register_percpu(const char *name, + struct metricfs_subsys *parent, + const char *description, + void (*fn)(struct metric_emitter *e, int cpu), + bool is_cumulative, + struct module *owner); + void metric_unregister(struct metric *m); #endif /* _METRICFS_H_ */ diff --git a/kernel/metricfs.c b/kernel/metricfs.c index 676b7b04aa2b..992fdd9a4d0a 100644 --- a/kernel/metricfs.c +++ b/kernel/metricfs.c @@ -76,6 +76,8 @@ struct metric { bool is_string; bool is_cumulative; bool has_parm; + bool is_percpu; + void (*percpu_fn)(struct metric_emitter *e, int cpu); /* dentry for the directory that contains the metric */ struct dentry *dentry; @@ -285,6 +287,19 @@ void metric_emit_str_value(struct metric_emitter *e, const char *v, } EXPORT_SYMBOL(metric_emit_str_value); +void metric_emit_percpu_int_value(struct metric_emitter *e, int cpu, int64_t v) +{ + char *ckpt = e->buf; + bool ok = true; + + ok &= emit_int(e, cpu); + ok &= emit_string(e, " "); + ok &= emit_int(e, v); + ok &= emit_string(e, "\n"); + if (!ok) + e->buf = ckpt; +} + /* Contains file data generated at open() */ struct metricfs_file_private { size_t bytes_written; @@ -400,11 +415,15 @@ static int metricfs_fields_open(struct inode *inode, struct file *filp) } ok &= emit_string(&e, "value\n"); - if (m->fname0) - ok &= emit_string(&e, "str "); - if (m->fname1) - ok &= emit_string(&e, "str "); - ok &= emit_string(&e, (m->is_string) ? "str\n" : "int\n"); + if (m->is_percpu) { + ok &= emit_string(&e, "int int\n"); + } else { + if (m->fname0) + ok &= emit_string(&e, "str "); + if (m->fname1) + ok &= emit_string(&e, "str "); + ok &= emit_string(&e, (m->is_string) ? "str\n" : "int\n"); + } /* Emit all or nothing. */ if (ok) { @@ -640,6 +659,35 @@ struct metric *metric_register(const char *name, } EXPORT_SYMBOL(metric_register); +static void metric_emit_percpu(struct metric_emitter *e) +{ + int cpu; + + for_each_possible_cpu(cpu) + e->metric->percpu_fn(e, cpu); +} + +struct metric *metric_register_percpu(const char *name, + struct metricfs_subsys *parent, + const char *description, + void (*fn)(struct metric_emitter *e, int cpu), + bool is_cumulative, + struct module *owner) +{ + struct metric *metric = + metric_register(name, parent, description, + "cpu", NULL, + metric_emit_percpu, + false, + is_cumulative, owner); + if (metric) { + metric->is_percpu = true; + metric->percpu_fn = fn; + } + return metric; +} +EXPORT_SYMBOL(metric_register_percpu); + struct metric *metric_register_parm(const char *name, struct metricfs_subsys *parent, const char *description, -- 2.28.0.236.gb10cc79966-goog