From: Andreas Dilger <adilger@dilger.ca>
To: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Cc: kvm@vger.kernel.org, linux-fsdevel@vger.kernel.org,
mst@redhat.com, borntraeger@de.ibm.com,
Paolo Bonzini <pbonzini@redhat.com>
Subject: Re: [RFC PATCH 2/5] statsfs API: create, add and remove statsfs sources and values
Date: Mon, 27 Apr 2020 15:53:07 -0600 [thread overview]
Message-ID: <97C10529-DFBF-47E3-9E51-A4C5A63535F3@dilger.ca> (raw)
In-Reply-To: <20200427141816.16703-3-eesposit@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 29462 bytes --]
On Apr 27, 2020, at 8:18 AM, Emanuele Giuseppe Esposito <eesposit@redhat.com> wrote:
>
> Introduction to the statsfs API, that allows to easily create, add
> and remove statsfs sources and values.
Not a huge issue, but IMHO the "statsfs" name is confusingly similar to
the existing "statfs" function name. Could you name this interface
something more distinct? Even "fs_stats" or "stats_fs" or similar would
at least be visibly different.
Cheers, Andreas
> The API allows to easily building
> the statistics directory tree to automatically gather them for the linux
> kernel. The main functionalities are: create a source, add child
> sources/values/aggregates, register it to the root source (that on
> the virtual fs would be /sys/kernel/statsfs), ad perform a search for
> a value/aggregate.
>
> This allows creating any kind of source tree, making it more flexible
> also to future readjustments.
>
> The API representation is only logical and will be backed up
> by a virtual file system in patch 4.
> Its usage will be shared between the statsfs file system
> and the end-users like kvm, the former calling it when it needs to
> display and clear statistics, the latter to add values and sources.
>
> Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
> ---
> fs/Kconfig | 7 +
> fs/Makefile | 1 +
> fs/statsfs/Makefile | 4 +
> fs/statsfs/internal.h | 20 ++
> fs/statsfs/statsfs.c | 618 ++++++++++++++++++++++++++++++++++++++++
> include/linux/statsfs.h | 222 +++++++++++++++
> 6 files changed, 872 insertions(+)
> create mode 100644 fs/statsfs/Makefile
> create mode 100644 fs/statsfs/internal.h
> create mode 100644 fs/statsfs/statsfs.c
> create mode 100644 include/linux/statsfs.h
>
> diff --git a/fs/Kconfig b/fs/Kconfig
> index f08fbbfafd9a..824fcf86d12b 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -328,4 +328,11 @@ source "fs/unicode/Kconfig"
> config IO_WQ
> bool
>
> +config STATS_FS
> + bool "Statistics Filesystem"
> + default y
> + help
> + statsfs is a virtual file system that provides counters and other
> + statistics about the running kernel.
> +
> endmenu
> diff --git a/fs/Makefile b/fs/Makefile
> index 2ce5112b02c8..6942070f54b2 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -125,6 +125,7 @@ obj-$(CONFIG_BEFS_FS) += befs/
> obj-$(CONFIG_HOSTFS) += hostfs/
> obj-$(CONFIG_CACHEFILES) += cachefiles/
> obj-$(CONFIG_DEBUG_FS) += debugfs/
> +obj-$(CONFIG_STATS_FS) += statsfs/
> obj-$(CONFIG_TRACING) += tracefs/
> obj-$(CONFIG_OCFS2_FS) += ocfs2/
> obj-$(CONFIG_BTRFS_FS) += btrfs/
> diff --git a/fs/statsfs/Makefile b/fs/statsfs/Makefile
> new file mode 100644
> index 000000000000..d494a3f30ba5
> --- /dev/null
> +++ b/fs/statsfs/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +statsfs-objs := statsfs.o
> +
> +obj-$(CONFIG_STATS_FS) += statsfs.o
> diff --git a/fs/statsfs/internal.h b/fs/statsfs/internal.h
> new file mode 100644
> index 000000000000..f124683a2ded
> --- /dev/null
> +++ b/fs/statsfs/internal.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _STATSFS_INTERNAL_H_
> +#define _STATSFS_INTERNAL_H_
> +
> +#include <linux/list.h>
> +#include <linux/kref.h>
> +#include <linux/rwsem.h>
> +#include <linux/statsfs.h>
> +
> +/* values, grouped by base */
> +struct statsfs_value_source {
> + void *base_addr;
> + bool files_created;
> + struct statsfs_value *values;
> + struct list_head list_element;
> +};
> +
> +int statsfs_val_get_mode(struct statsfs_value *val);
> +
> +#endif /* _STATSFS_INTERNAL_H_ */
> diff --git a/fs/statsfs/statsfs.c b/fs/statsfs/statsfs.c
> new file mode 100644
> index 000000000000..0ad1d985be46
> --- /dev/null
> +++ b/fs/statsfs/statsfs.c
> @@ -0,0 +1,618 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/file.h>
> +#include <linux/fs.h>
> +#include <linux/slab.h>
> +#include <linux/rwsem.h>
> +#include <linux/list.h>
> +#include <linux/kref.h>
> +#include <linux/limits.h>
> +#include <linux/statsfs.h>
> +
> +#include "internal.h"
> +
> +struct statsfs_aggregate_value {
> + uint64_t sum, min, max;
> + uint32_t count, count_zero;
> +};
> +
> +static int is_val_signed(struct statsfs_value *val)
> +{
> + return val->type & STATSFS_SIGN;
> +}
> +
> +int statsfs_val_get_mode(struct statsfs_value *val)
> +{
> + return val->mode ? val->mode : 0644;
> +}
> +
> +static struct statsfs_value *find_value(struct statsfs_value_source *src,
> + struct statsfs_value *val)
> +{
> + struct statsfs_value *entry;
> +
> + for (entry = src->values; entry->name; entry++) {
> + if (entry == val) {
> + WARN_ON(strcmp(entry->name, val->name) != 0);
> + return entry;
> + }
> + }
> + return NULL;
> +}
> +
> +static struct statsfs_value *
> +search_value_in_source(struct statsfs_source *src, struct statsfs_value *arg,
> + struct statsfs_value_source **val_src)
> +{
> + struct statsfs_value *entry;
> + struct statsfs_value_source *src_entry;
> +
> + list_for_each_entry(src_entry, &src->values_head, list_element) {
> + entry = find_value(src_entry, arg);
> + if (entry) {
> + *val_src = src_entry;
> + return entry;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/* Called with rwsem held for writing */
> +static struct statsfs_value_source *create_value_source(void *base)
> +{
> + struct statsfs_value_source *val_src;
> +
> + val_src = kzalloc(sizeof(struct statsfs_value_source), GFP_KERNEL);
> + if (!val_src)
> + return ERR_PTR(-ENOMEM);
> +
> + val_src->base_addr = base;
> + val_src->list_element =
> + (struct list_head)LIST_HEAD_INIT(val_src->list_element);
> +
> + return val_src;
> +}
> +
> +int statsfs_source_add_values(struct statsfs_source *source,
> + struct statsfs_value *stat, void *ptr)
> +{
> + struct statsfs_value_source *val_src;
> + struct statsfs_value_source *entry;
> +
> + down_write(&source->rwsem);
> +
> + list_for_each_entry(entry, &source->values_head, list_element) {
> + if (entry->base_addr == ptr && entry->values == stat) {
> + up_write(&source->rwsem);
> + return -EEXIST;
> + }
> + }
> +
> + val_src = create_value_source(ptr);
> + val_src->values = (struct statsfs_value *)stat;
> +
> + /* add the val_src to the source list */
> + list_add(&val_src->list_element, &source->values_head);
> +
> + up_write(&source->rwsem);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_add_values);
> +
> +void statsfs_source_add_subordinate(struct statsfs_source *source,
> + struct statsfs_source *sub)
> +{
> + down_write(&source->rwsem);
> +
> + statsfs_source_get(sub);
> + list_add(&sub->list_element, &source->subordinates_head);
> +
> + up_write(&source->rwsem);
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_add_subordinate);
> +
> +/* Called with rwsem held for writing */
> +static void
> +statsfs_source_remove_subordinate_locked(struct statsfs_source *source,
> + struct statsfs_source *sub)
> +{
> + struct list_head *it, *safe;
> + struct statsfs_source *src_entry;
> +
> + list_for_each_safe(it, safe, &source->subordinates_head) {
> + src_entry = list_entry(it, struct statsfs_source, list_element);
> + if (src_entry == sub) {
> + WARN_ON(strcmp(src_entry->name, sub->name) != 0);
> + list_del_init(&src_entry->list_element);
> + statsfs_source_put(src_entry);
> + return;
> + }
> + }
> +}
> +
> +void statsfs_source_remove_subordinate(struct statsfs_source *source,
> + struct statsfs_source *sub)
> +{
> + down_write(&source->rwsem);
> + statsfs_source_remove_subordinate_locked(source, sub);
> + up_write(&source->rwsem);
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_remove_subordinate);
> +
> +/* Called with rwsem held for reading */
> +static uint64_t get_simple_value(struct statsfs_value_source *src,
> + struct statsfs_value *val)
> +{
> + uint64_t value_found;
> + void *address;
> +
> + address = src->base_addr + val->offset;
> +
> + switch (val->type) {
> + case STATSFS_U8:
> + value_found = *((uint8_t *)address);
> + break;
> + case STATSFS_U8 | STATSFS_SIGN:
> + value_found = *((int8_t *)address);
> + break;
> + case STATSFS_U16:
> + value_found = *((uint16_t *)address);
> + break;
> + case STATSFS_U16 | STATSFS_SIGN:
> + value_found = *((int16_t *)address);
> + break;
> + case STATSFS_U32:
> + value_found = *((uint32_t *)address);
> + break;
> + case STATSFS_U32 | STATSFS_SIGN:
> + value_found = *((int32_t *)address);
> + break;
> + case STATSFS_U64:
> + value_found = *((uint64_t *)address);
> + break;
> + case STATSFS_U64 | STATSFS_SIGN:
> + value_found = *((int64_t *)address);
> + break;
> + case STATSFS_BOOL:
> + value_found = *((uint8_t *)address);
> + break;
> + default:
> + value_found = 0;
> + break;
> + }
> +
> + return value_found;
> +}
> +
> +/* Called with rwsem held for reading */
> +static void clear_simple_value(struct statsfs_value_source *src,
> + struct statsfs_value *val)
> +{
> + void *address;
> +
> + address = src->base_addr + val->offset;
> +
> + switch (val->type) {
> + case STATSFS_U8:
> + *((uint8_t *)address) = 0;
> + break;
> + case STATSFS_U8 | STATSFS_SIGN:
> + *((int8_t *)address) = 0;
> + break;
> + case STATSFS_U16:
> + *((uint16_t *)address) = 0;
> + break;
> + case STATSFS_U16 | STATSFS_SIGN:
> + *((int16_t *)address) = 0;
> + break;
> + case STATSFS_U32:
> + *((uint32_t *)address) = 0;
> + break;
> + case STATSFS_U32 | STATSFS_SIGN:
> + *((int32_t *)address) = 0;
> + break;
> + case STATSFS_U64:
> + *((uint64_t *)address) = 0;
> + break;
> + case STATSFS_U64 | STATSFS_SIGN:
> + *((int64_t *)address) = 0;
> + break;
> + case STATSFS_BOOL:
> + *((uint8_t *)address) = 0;
> + break;
> + default:
> + break;
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static void search_all_simple_values(struct statsfs_source *src,
> + struct statsfs_value_source *ref_src_entry,
> + struct statsfs_value *val,
> + struct statsfs_aggregate_value *agg)
> +{
> + struct statsfs_value_source *src_entry;
> + uint64_t value_found;
> +
> + list_for_each_entry(src_entry, &src->values_head, list_element) {
> + /* skip aggregates */
> + if (src_entry->base_addr == NULL)
> + continue;
> +
> + /* useless to search here */
> + if (src_entry->values != ref_src_entry->values)
> + continue;
> +
> + /* must be here */
> + value_found = get_simple_value(src_entry, val);
> + agg->sum += value_found;
> + agg->count++;
> + agg->count_zero += (value_found == 0);
> +
> + if (is_val_signed(val)) {
> + agg->max = (((int64_t)value_found) >=
> + ((int64_t)agg->max)) ?
> + value_found :
> + agg->max;
> + agg->min = (((int64_t)value_found) <=
> + ((int64_t)agg->min)) ?
> + value_found :
> + agg->min;
> + } else {
> + agg->max = (value_found >= agg->max) ? value_found :
> + agg->max;
> + agg->min = (value_found <= agg->min) ? value_found :
> + agg->min;
> + }
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static void do_recursive_aggregation(struct statsfs_source *root,
> + struct statsfs_value_source *ref_src_entry,
> + struct statsfs_value *val,
> + struct statsfs_aggregate_value *agg)
> +{
> + struct statsfs_source *subordinate;
> +
> + /* search all simple values in this folder */
> + search_all_simple_values(root, ref_src_entry, val, agg);
> +
> + /* recursively search in all subfolders */
> + list_for_each_entry(subordinate, &root->subordinates_head,
> + list_element) {
> + down_read(&subordinate->rwsem);
> + do_recursive_aggregation(subordinate, ref_src_entry, val, agg);
> + up_read(&subordinate->rwsem);
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static void init_aggregate_value(struct statsfs_aggregate_value *agg,
> + struct statsfs_value *val)
> +{
> + agg->count = agg->count_zero = agg->sum = 0;
> + if (is_val_signed(val)) {
> + agg->max = S64_MIN;
> + agg->min = S64_MAX;
> + } else {
> + agg->max = 0;
> + agg->min = U64_MAX;
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static void store_final_value(struct statsfs_aggregate_value *agg,
> + struct statsfs_value *val, uint64_t *ret)
> +{
> + int operation;
> +
> + operation = val->aggr_kind | is_val_signed(val);
> +
> + switch (operation) {
> + case STATSFS_AVG:
> + *ret = agg->count ? agg->sum / agg->count : 0;
> + break;
> + case STATSFS_AVG | STATSFS_SIGN:
> + *ret = agg->count ? ((int64_t)agg->sum) / agg->count : 0;
> + break;
> + case STATSFS_SUM:
> + case STATSFS_SUM | STATSFS_SIGN:
> + *ret = agg->sum;
> + break;
> + case STATSFS_MIN:
> + case STATSFS_MIN | STATSFS_SIGN:
> + *ret = agg->min;
> + break;
> + case STATSFS_MAX:
> + case STATSFS_MAX | STATSFS_SIGN:
> + *ret = agg->max;
> + break;
> + case STATSFS_COUNT_ZERO:
> + case STATSFS_COUNT_ZERO | STATSFS_SIGN:
> + *ret = agg->count_zero;
> + break;
> + default:
> + break;
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static int statsfs_source_get_value_locked(struct statsfs_source *source,
> + struct statsfs_value *arg,
> + uint64_t *ret)
> +{
> + struct statsfs_value_source *src_entry;
> + struct statsfs_value *found;
> + struct statsfs_aggregate_value aggr;
> +
> + *ret = 0;
> +
> + if (!arg)
> + return -ENOENT;
> +
> + /* look in simple values */
> + found = search_value_in_source(source, arg, &src_entry);
> +
> + if (!found) {
> + printk(KERN_ERR "Statsfs: Value in source \"%s\" not found!\n",
> + source->name);
> + return -ENOENT;
> + }
> +
> + if (src_entry->base_addr != NULL) {
> + *ret = get_simple_value(src_entry, found);
> + return 0;
> + }
> +
> + /* look in aggregates */
> + init_aggregate_value(&aggr, found);
> + do_recursive_aggregation(source, src_entry, found, &aggr);
> + store_final_value(&aggr, found, ret);
> +
> + return 0;
> +}
> +
> +int statsfs_source_get_value(struct statsfs_source *source,
> + struct statsfs_value *arg, uint64_t *ret)
> +{
> + int retval;
> +
> + down_read(&source->rwsem);
> + retval = statsfs_source_get_value_locked(source, arg, ret);
> + up_read(&source->rwsem);
> +
> + return retval;
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_get_value);
> +
> +/* Called with rwsem held for reading */
> +static void set_all_simple_values(struct statsfs_source *src,
> + struct statsfs_value_source *ref_src_entry,
> + struct statsfs_value *val)
> +{
> + struct statsfs_value_source *src_entry;
> +
> + list_for_each_entry(src_entry, &src->values_head, list_element) {
> + /* skip aggregates */
> + if (src_entry->base_addr == NULL)
> + continue;
> +
> + /* wrong to search here */
> + if (src_entry->values != ref_src_entry->values)
> + continue;
> +
> + if (src_entry->base_addr &&
> + src_entry->values == ref_src_entry->values)
> + clear_simple_value(src_entry, val);
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static void do_recursive_clean(struct statsfs_source *root,
> + struct statsfs_value_source *ref_src_entry,
> + struct statsfs_value *val)
> +{
> + struct statsfs_source *subordinate;
> +
> + /* search all simple values in this folder */
> + set_all_simple_values(root, ref_src_entry, val);
> +
> + /* recursively search in all subfolders */
> + list_for_each_entry(subordinate, &root->subordinates_head,
> + list_element) {
> + down_read(&subordinate->rwsem);
> + do_recursive_clean(subordinate, ref_src_entry, val);
> + up_read(&subordinate->rwsem);
> + }
> +}
> +
> +/* Called with rwsem held for reading */
> +static int statsfs_source_clear_locked(struct statsfs_source *source,
> + struct statsfs_value *val)
> +{
> + struct statsfs_value_source *src_entry;
> + struct statsfs_value *found;
> +
> + if (!val)
> + return -ENOENT;
> +
> + /* look in simple values */
> + found = search_value_in_source(source, val, &src_entry);
> +
> + if (!found) {
> + printk(KERN_ERR "Statsfs: Value in source \"%s\" not found!\n",
> + source->name);
> + return -ENOENT;
> + }
> +
> + if (src_entry->base_addr != NULL) {
> + clear_simple_value(src_entry, found);
> + return 0;
> + }
> +
> + /* look in aggregates */
> + do_recursive_clean(source, src_entry, found);
> +
> + return 0;
> +}
> +
> +int statsfs_source_clear(struct statsfs_source *source,
> + struct statsfs_value *val)
> +{
> + int retval;
> +
> + down_read(&source->rwsem);
> + retval = statsfs_source_clear_locked(source, val);
> + up_read(&source->rwsem);
> +
> + return retval;
> +}
> +
> +/* Called with rwsem held for reading */
> +static struct statsfs_value *
> +find_value_by_name(struct statsfs_value_source *src, char *val)
> +{
> + struct statsfs_value *entry;
> +
> + for (entry = src->values; entry->name; entry++)
> + if (!strcmp(entry->name, val))
> + return entry;
> +
> + return NULL;
> +}
> +
> +/* Called with rwsem held for reading */
> +static struct statsfs_value *
> +search_in_source_by_name(struct statsfs_source *src, char *name)
> +{
> + struct statsfs_value *entry;
> + struct statsfs_value_source *src_entry;
> +
> + list_for_each_entry(src_entry, &src->values_head, list_element) {
> + entry = find_value_by_name(src_entry, name);
> + if (entry)
> + return entry;
> + }
> +
> + return NULL;
> +}
> +
> +int statsfs_source_get_value_by_name(struct statsfs_source *source, char *name,
> + uint64_t *ret)
> +{
> + struct statsfs_value *val;
> + int retval;
> +
> + down_read(&source->rwsem);
> + val = search_in_source_by_name(source, name);
> +
> + if (!val) {
> + *ret = 0;
> + up_read(&source->rwsem);
> + return -ENOENT;
> + }
> +
> + retval = statsfs_source_get_value_locked(source, val, ret);
> + up_read(&source->rwsem);
> +
> + return retval;
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_get_value_by_name);
> +
> +void statsfs_source_get(struct statsfs_source *source)
> +{
> + kref_get(&source->refcount);
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_get);
> +
> +void statsfs_source_revoke(struct statsfs_source *source)
> +{
> + struct list_head *it, *safe;
> + struct statsfs_value_source *val_src_entry;
> +
> + down_write(&source->rwsem);
> +
> + list_for_each_safe(it, safe, &source->values_head) {
> + val_src_entry = list_entry(it, struct statsfs_value_source,
> + list_element);
> + val_src_entry->base_addr = NULL;
> + }
> +
> + up_write(&source->rwsem);
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_revoke);
> +
> +/* Called with rwsem held for writing
> + *
> + * The refcount is 0 and the lock was taken before refcount
> + * went from 1 to 0
> + */
> +static void statsfs_source_destroy(struct kref *kref_source)
> +{
> + struct statsfs_value_source *val_src_entry;
> + struct list_head *it, *safe;
> + struct statsfs_source *child, *source;
> +
> + source = container_of(kref_source, struct statsfs_source, refcount);
> +
> + /* iterate through the values and delete them */
> + list_for_each_safe(it, safe, &source->values_head) {
> + val_src_entry = list_entry(it, struct statsfs_value_source,
> + list_element);
> + kfree(val_src_entry);
> + }
> +
> + /* iterate through the subordinates and delete them */
> + list_for_each_safe(it, safe, &source->subordinates_head) {
> + child = list_entry(it, struct statsfs_source, list_element);
> + statsfs_source_remove_subordinate_locked(source, child);
> + }
> +
> +
> + up_write(&source->rwsem);
> + kfree(source->name);
> + kfree(source);
> +}
> +
> +void statsfs_source_put(struct statsfs_source *source)
> +{
> + kref_put_rwsem(&source->refcount, statsfs_source_destroy,
> + &source->rwsem);
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_put);
> +
> +struct statsfs_source *statsfs_source_create(const char *fmt, ...)
> +{
> + va_list ap;
> + char buf[100];
> + struct statsfs_source *ret;
> + int char_needed;
> +
> + va_start(ap, fmt);
> + char_needed = vsnprintf(buf, 100, fmt, ap);
> + va_end(ap);
> +
> + ret = kzalloc(sizeof(struct statsfs_source), GFP_KERNEL);
> + if (!ret)
> + return ERR_PTR(-ENOMEM);
> +
> + ret->name = kstrdup(buf, GFP_KERNEL);
> + if (!ret->name) {
> + kfree(ret);
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + kref_init(&ret->refcount);
> + init_rwsem(&ret->rwsem);
> +
> + INIT_LIST_HEAD(&ret->values_head);
> + INIT_LIST_HEAD(&ret->subordinates_head);
> + INIT_LIST_HEAD(&ret->list_element);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(statsfs_source_create);
> diff --git a/include/linux/statsfs.h b/include/linux/statsfs.h
> new file mode 100644
> index 000000000000..3f01f094946d
> --- /dev/null
> +++ b/include/linux/statsfs.h
> @@ -0,0 +1,222 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * statsfs.h - a tiny little statistics file system
> + *
> + * Copyright (C) 2020 Emanuele Giuseppe Esposito
> + * Copyright (C) 2020 Redhat.
> + *
> + */
> +
> +#ifndef _STATSFS_H_
> +#define _STATSFS_H_
> +
> +#include <linux/list.h>
> +
> +/* Used to distinguish signed types */
> +#define STATSFS_SIGN 0x8000
> +
> +struct statsfs_source;
> +
> +enum stat_type {
> + STATSFS_U8 = 0,
> + STATSFS_U16 = 1,
> + STATSFS_U32 = 2,
> + STATSFS_U64 = 3,
> + STATSFS_BOOL = 4,
> + STATSFS_S8 = STATSFS_U8 | STATSFS_SIGN,
> + STATSFS_S16 = STATSFS_U16 | STATSFS_SIGN,
> + STATSFS_S32 = STATSFS_U32 | STATSFS_SIGN,
> + STATSFS_S64 = STATSFS_U64 | STATSFS_SIGN,
> +};
> +
> +enum stat_aggr {
> + STATSFS_NONE = 0,
> + STATSFS_SUM,
> + STATSFS_MIN,
> + STATSFS_MAX,
> + STATSFS_COUNT_ZERO,
> + STATSFS_AVG,
> +};
> +
> +struct statsfs_value {
> + /* Name of the stat */
> + char *name;
> +
> + /* Offset from base address to field containing the value */
> + int offset;
> +
> + /* Type of the stat BOOL,U64,... */
> + enum stat_type type;
> +
> + /* Aggregate type: MIN, MAX, SUM,... */
> + enum stat_aggr aggr_kind;
> +
> + /* File mode */
> + uint16_t mode;
> +};
> +
> +struct statsfs_source {
> + struct kref refcount;
> +
> + char *name;
> +
> + /* list of source statsfs_value_source*/
> + struct list_head values_head;
> +
> + /* list of struct statsfs_source for subordinate sources */
> + struct list_head subordinates_head;
> +
> + struct list_head list_element;
> +
> + struct rw_semaphore rwsem;
> +
> + struct dentry *source_dentry;
> +};
> +
> +/**
> + * statsfs_source_create - create a statsfs_source
> + * Creates a statsfs_source with the given name. This
> + * does not mean it will be backed by the filesystem yet, it will only
> + * be visible to the user once one of its parents (or itself) are
> + * registered in statsfs.
> + *
> + * Returns a pointer to a statsfs_source if it succeeds.
> + * This or one of the parents' pointer must be passed to the statsfs_put()
> + * function when the file is to be removed. If an error occurs,
> + * ERR_PTR(-ERROR) will be returned.
> + */
> +struct statsfs_source *statsfs_source_create(const char *fmt, ...);
> +
> +/**
> + * statsfs_source_add_values - adds values to the given source
> + * @source: a pointer to the source that will receive the values
> + * @val: a pointer to the NULL terminated statsfs_value array to add
> + * @base_ptr: a pointer to the base pointer used by these values
> + *
> + * In addition to adding values to the source, also create the
> + * files in the filesystem if the source already is backed up by a directory.
> + *
> + * Returns 0 it succeeds. If the value are already in the
> + * source and have the same base_ptr, -EEXIST is returned.
> + */
> +int statsfs_source_add_values(struct statsfs_source *source,
> + struct statsfs_value *val, void *base_ptr);
> +
> +/**
> + * statsfs_source_add_subordinate - adds a child to the given source
> + * @parent: a pointer to the parent source
> + * @child: a pointer to child source to add
> + *
> + * Recursively create all files in the statsfs filesystem
> + * only if the parent has already a dentry (created with
> + * statsfs_source_register).
> + * This avoids the case where this function is called before register.
> + */
> +void statsfs_source_add_subordinate(struct statsfs_source *parent,
> + struct statsfs_source *child);
> +
> +/**
> + * statsfs_source_remove_subordinate - removes a child from the given source
> + * @parent: a pointer to the parent source
> + * @child: a pointer to child source to remove
> + *
> + * Look if there is such child in the parent. If so,
> + * it will remove all its files and call statsfs_put on the child.
> + */
> +void statsfs_source_remove_subordinate(struct statsfs_source *parent,
> + struct statsfs_source *child);
> +
> +/**
> + * statsfs_source_get_value - search a value in the source (and
> + * subordinates)
> + * @source: a pointer to the source that will be searched
> + * @val: a pointer to the statsfs_value to search
> + * @ret: a pointer to the uint64_t that will hold the found value
> + *
> + * Look up in the source if a value with same value pointer
> + * exists.
> + * If not, it will return -ENOENT. If it exists and it's a simple value
> + * (not an aggregate), the value that it points to will be returned.
> + * If it exists and it's an aggregate (aggr_type != STATSFS_NONE), all
> + * subordinates will be recursively searched and every simple value match
> + * will be used to aggregate the final result. For example if it's a sum,
> + * all suboordinates having the same value will be sum together.
> + *
> + * This function will return 0 it succeeds.
> + */
> +int statsfs_source_get_value(struct statsfs_source *source,
> + struct statsfs_value *val, uint64_t *ret);
> +
> +/**
> + * statsfs_source_get_value_by_name - search a value in the source (and
> + * subordinates)
> + * @source: a pointer to the source that will be searched
> + * @name: a pointer to the string representing the value to search
> + * (for example "exits")
> + * @ret: a pointer to the uint64_t that will hold the found value
> + *
> + * Same as statsfs_source_get_value, but initially the name is used
> + * to search in the given source if there is a value with a matching
> + * name. If so, statsfs_source_get_value will be called with the found
> + * value, otherwise -ENOENT will be returned.
> + */
> +int statsfs_source_get_value_by_name(struct statsfs_source *source, char *name,
> + uint64_t *ret);
> +
> +/**
> + * statsfs_source_clear - search and clears a value in the source (and
> + * subordinates)
> + * @source: a pointer to the source that will be searched
> + * @val: a pointer to the statsfs_value to search
> + *
> + * Look up in the source if a value with same value pointer
> + * exists.
> + * If not, it will return -ENOENT. If it exists and it's a simple value
> + * (not an aggregate), the value that it points to will be set to 0.
> + * If it exists and it's an aggregate (aggr_type != STATSFS_NONE), all
> + * subordinates will be recursively searched and every simple value match
> + * will be set to 0.
> + *
> + * This function will return 0 it succeeds.
> + */
> +int statsfs_source_clear(struct statsfs_source *source,
> + struct statsfs_value *val);
> +
> +/**
> + * statsfs_source_revoke - disconnect the source from its backing data
> + * @source: a pointer to the source that will be revoked
> + *
> + * Ensure that statsfs will not access the data that were passed to
> + * statsfs_source_add_value for this source.
> + *
> + * Because open files increase the reference count for a statsfs_source,
> + * the source can end up living longer than the data that provides the
> + * values for the source. Calling statsfs_source_revoke just before the
> + * backing data is freed avoids accesses to freed data structures. The
> + * sources will return 0.
> + */
> +void statsfs_source_revoke(struct statsfs_source *source);
> +
> +/**
> + * statsfs_source_get - increases refcount of source
> + * @source: a pointer to the source whose refcount will be increased
> + */
> +void statsfs_source_get(struct statsfs_source *source);
> +
> +/**
> + * statsfs_source_put - decreases refcount of source and deletes if needed
> + * @source: a pointer to the source whose refcount will be decreased
> + *
> + * If refcount arrives to zero, take care of deleting
> + * and free the source resources and files, by firstly recursively calling
> + * statsfs_source_remove_subordinate to the child and then deleting
> + * its own files and allocations.
> + */
> +void statsfs_source_put(struct statsfs_source *source);
> +
> +/**
> + * statsfs_initialized - returns true if statsfs fs has been registered
> + */
> +bool statsfs_initialized(void);
> +
> +#endif
> --
> 2.25.2
>
Cheers, Andreas
[-- Attachment #2: Message signed with OpenPGP --]
[-- Type: application/pgp-signature, Size: 873 bytes --]
next prev parent reply other threads:[~2020-04-27 21:53 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-27 14:18 [RFC PATCH 0/5] Statsfs: a new ram-based file sytem for Linux kernel statistics Emanuele Giuseppe Esposito
2020-04-27 14:18 ` [RFC PATCH 1/5] refcount, kref: add dec-and-test wrappers for rw_semaphores Emanuele Giuseppe Esposito
2020-04-27 14:18 ` [RFC PATCH 2/5] statsfs API: create, add and remove statsfs sources and values Emanuele Giuseppe Esposito
2020-04-27 15:47 ` Matthew Wilcox
2020-04-27 16:48 ` Emanuele Giuseppe Esposito
2020-04-29 9:49 ` [RFC PATCH 2/5] statsfs API: create, add and remove statsfs Emanuele Giuseppe Esposito
2020-04-27 21:53 ` Andreas Dilger [this message]
2020-04-29 10:55 ` [RFC PATCH 2/5] statsfs API: create, add and remove statsfs sources and values Emanuele Giuseppe Esposito
2020-04-28 17:47 ` Randy Dunlap
2020-04-29 10:34 ` Paolo Bonzini
2020-04-27 14:18 ` [RFC PATCH 3/5] kunit: tests for statsfs API Emanuele Giuseppe Esposito
2020-04-28 17:50 ` Randy Dunlap
2020-04-27 14:18 ` [RFC PATCH 4/5] statsfs fs: virtual fs to show stats to the end-user Emanuele Giuseppe Esposito
2020-04-27 14:18 ` [RFC PATCH 5/5] kvm_main: replace debugfs with statsfs Emanuele Giuseppe Esposito
2020-04-28 17:56 ` Randy Dunlap
2020-04-29 10:34 ` Emanuele Giuseppe Esposito
2020-04-29 10:35 ` Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=97C10529-DFBF-47E3-9E51-A4C5A63535F3@dilger.ca \
--to=adilger@dilger.ca \
--cc=borntraeger@de.ibm.com \
--cc=eesposit@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).