From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
To: Andreas Dilger <adilger@dilger.ca>
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: Wed, 29 Apr 2020 12:55:36 +0200 [thread overview]
Message-ID: <92ee87c6-1d94-947d-b8a6-5ce26b5d1ef2@redhat.com> (raw)
In-Reply-To: <97C10529-DFBF-47E3-9E51-A4C5A63535F3@dilger.ca>
On 4/27/20 11:53 PM, Andreas Dilger wrote:
> 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.
You're right, thanks for pointing that out. I am going to change all
functions and files into stats_fs. The filesystem name, however, will
still stay statsfs because it follows the same naming as
debugfs/tracecfs/securityfs and won't interfere or be confused with
statfs functions.
Thank you,
Emanuele
>
> 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
>
>
>
>
>
next prev parent reply other threads:[~2020-04-29 10:55 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 ` [RFC PATCH 2/5] statsfs API: create, add and remove statsfs sources and values Andreas Dilger
2020-04-29 10:55 ` Emanuele Giuseppe Esposito [this message]
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=92ee87c6-1d94-947d-b8a6-5ce26b5d1ef2@redhat.com \
--to=eesposit@redhat.com \
--cc=adilger@dilger.ca \
--cc=borntraeger@de.ibm.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).