lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:	Wed, 06 Jun 2007 23:33:43 +0200
From:	Martin Peschke <mp3@...ibm.com>
To:	linux-kernel@...r.kernel.org
Cc:	a.p.zijlstra@...llo.nl, jbaron@...hat.com, rostedt@...dmis.org,
	billh@...ppy.monkey.org, mingo@...e.hu, linux-s390@...r.kernel.org
Subject: [RFC] [Patch 3/4] statistics: sets of statistics in single file

This patch chips a structure describing a set of statistics from the
structure used to manage the user interface (files) for a set of
statistics. The benefit of this split and small API change is that
users can have multiple sets of statistics per file.

For example, a user which reports statistics data for many entities of
similar type can have them all represented in the same file now:

my_entity_1 some_statistic 0x1000 67
my_entity_1 some_statistic 0x4000 52
my_entity_1 some_statistic 0x5000 13
my_entity_1 some_statistic other 4
my_entity_1 another_statistic samples 83
my_entity_1 another_statistic minimum 185
my_entity_1 another_statistic average 2875.763
my_entity_1 another_statistic maximum 2837585
my_entity_1 another_statistic variance 298469.485
my_entity_2 some_statistic other 0
my_entity_2 another_statistic samples 0
my_entity_2 another_statistic minimum 0
my_entity_2 another_statistic average 0.000
my_entity_2 another_statistic maximum 0
my_entity_2 another_statistic variance 0.000


Signed-off-by: Martin Peschke <mp3@...ibm.com>
---

 include/linux/statistic.h |   47 ++++
 lib/statistic.c           |  444 ++++++++++++++++++++++++++++------------------
 2 files changed, 315 insertions(+), 176 deletions(-)

Index: linux/include/linux/statistic.h
===================================================================
--- linux.orig/include/linux/statistic.h
+++ linux/include/linux/statistic.h
@@ -108,13 +108,34 @@ struct statistic {
 };
 
 /**
- * struct statistic_ui - collection of statistics for an entity
+ * struct statistic_coll - collection of statistics for an entity
  * @stat: a struct statistic array
  * @info: a struct statistic_info array describing the struct statistic array
  * @number: number of entries in both arrays
- * @pull: an optional function called when user reads data from file
+ * @private: optional data pointer reserved for use by clients
  * @label: an optional function retrieving a label for each statistics entry
+ *
+ * Exploiters must setup a struct statistic_coll prior to calling
+ * statistic_attach().
+ */
+struct statistic_coll {
+/* private: */
+	struct list_head	 list;
+/* public: */
+#define STATISTIC_COLL_NAME_SIZE 64
+	char 			 name[STATISTIC_COLL_NAME_SIZE];
+	struct statistic	*stat;
+	struct statistic_info	*info;
+	int			 number;
+	void			*private;
+	void			(*label)(struct statistic_coll *coll, int i,
+					 void *key, char *buf, int size);
+};
+
+/**
+ * struct statistic_ui - user interface for statistics
  * @private: optional data pointer reserved for use by clients
+ * @pull: an optional function called when user reads data from file
  *
  * Exploiters must setup a struct statistic_ui prior to calling
  * statistic_create().
@@ -122,17 +143,13 @@ struct statistic {
 struct statistic_ui {
 /* private: */
 	struct list_head	 list;
+	struct list_head	 coll_list;
 	void			*dir;
 	void			*data;
 	void			*def;
 /* public: */
-	struct statistic	*stat;
-	struct statistic_info	*info;
-	int			 number;
-	void			(*pull)(struct statistic_ui *ui);
-	void			(*label)(struct statistic_ui *ui,
-					 int i, void *key, char *buf, int size);
 	void			*private;
+	void			(*pull)(struct statistic_ui *ui);
 };
 
 #ifdef CONFIG_STATISTICS
@@ -140,6 +157,9 @@ struct statistic_ui {
 extern int statistic_create(struct statistic_ui *, const char *);
 extern int statistic_remove(struct statistic_ui *);
 
+extern int statistic_attach(struct statistic_coll *, struct statistic_ui *);
+extern int statistic_detach(struct statistic_coll *);
+
 extern void statistic_set(struct statistic *, int, s64, u64);
 extern void statistic_set_key(struct statistic *, int, void *, u64);
 
@@ -323,6 +343,17 @@ static inline int statistic_remove(struc
 	return 0;
 }
 
+static inline int statistic_attach(struct statistic_coll *coll,
+				   struct statistic_ui *ui)
+{
+	return 0;
+}
+
+static inline int statistic_detach(struct statistic_coll *coll)
+{
+	return 0;
+}
+
 static inline void statistic_set(struct statistic *stat, int i,
 				 s64 value, u64 total)
 {
Index: linux/lib/statistic.c
===================================================================
--- linux.orig/lib/statistic.c
+++ linux/lib/statistic.c
@@ -93,8 +93,7 @@ struct statistic_discipline {
 	void (*reset)(struct statistic *stat, void *ptr);
 	void (*merge)(struct statistic *stat, void *dst, void *src);
 	void (*def)(struct statistic *stat, struct seq_file *seq);
-	void (*data)(struct statistic *stat, struct seq_file *seq,
-		     struct statistic_ui *ui, int i);
+	void (*data)(struct statistic_coll *coll, int i, struct seq_file *seq);
 	void (*add)(struct statistic *stat, s64 value, u64 incr);
 	void (*set)(struct statistic *stat, s64 value, u64 total);
 	void (*kadd)(struct statistic *stat, void *key, u64 incr);
@@ -256,10 +255,9 @@ static void _statistic_merge(void *__mpr
 	spin_unlock(&mpriv->lock);
 }
 
-static void *statistic_merge(struct statistic_ui *ui, int i)
+static void *statistic_merge(struct statistic *stat,
+			     struct statistic_info *info)
 {
-	struct statistic *stat = &ui->stat[i];
-	struct statistic_info *info = &ui->info[i];
 	struct statistic_discipline *disc = &statistic_discs[stat->type];
 	struct statistic_merge_private mpriv;
 	size_t size = disc->size(stat);
@@ -280,11 +278,11 @@ static void *statistic_merge(struct stat
 
 /* cpu hotplug handling for per-cpu data */
 
-static int _statistic_hotcpu(struct statistic_ui *ui, int i,
+static int _statistic_hotcpu(struct statistic_coll *coll, int i,
 			     unsigned long action, int cpu)
 {
-	struct statistic *stat = &ui->stat[i];
-	struct statistic_info *info = &ui->info[i];
+	struct statistic *stat = &coll->stat[i];
+	struct statistic_info *info = &coll->info[i];
 	struct statistic_discipline *disc = &statistic_discs[stat->type];
 	void *src, *dst;
 	size_t size;
@@ -323,18 +321,20 @@ static struct list_head statistic_list;
 static struct mutex statistic_list_mutex;
 
 static int __cpuinit statistic_hotcpu(struct notifier_block *notifier,
-				      unsigned long action, void *__cpu)
+				      unsigned long act, void *__cpu)
 {
 	int cpu = (unsigned long)__cpu, i, retval = NOTIFY_OK;
 	struct statistic_ui *ui;
+	struct statistic_coll *coll;
 
 	mutex_lock(&statistic_list_mutex);
 	list_for_each_entry(ui, &statistic_list, list)
-		for (i = 0; i < ui->number; i++) {
-			retval = _statistic_hotcpu(ui, i, action, cpu);
-			if (retval == NOTIFY_BAD)
-				goto unlock;
-		}
+		list_for_each_entry(coll, &ui->coll_list, list)
+			for (i = 0; i < coll->number; i++) {
+				retval = _statistic_hotcpu(coll, i, act, cpu);
+				if (retval == NOTIFY_BAD)
+					goto unlock;
+			}
 unlock:
 	mutex_unlock(&statistic_list_mutex);
 	return retval;
@@ -439,8 +439,9 @@ static void statistic_parse_line(struct 
 	substring_t args[MAX_OPT_ARGS], sub;
 	int token, reset = 0, defaults = 0, i;
 	int state = STATISTIC_STATE_INVALID;
-	struct statistic *stat = ui->stat;
-	struct statistic_info *info = ui->info;
+	struct statistic_coll *coll;
+	struct statistic_info *info;
+	struct statistic *stat;
 
 	if (unlikely(!def))
 		return;
@@ -465,16 +466,20 @@ static void statistic_parse_line(struct 
 			break;
 		}
 	}
-	for (i = 0; i < ui->number; i++, stat++, info++) {
-		if (!name || (name && !strcmp(name, info->name))) {
-			if (defaults)
-				statistic_parse_match(stat, info, NULL);
-			if (name)
-				statistic_parse_match(stat, info, def);
-			if (state != STATISTIC_STATE_INVALID)
-				statistic_transition(stat, info, state);
-			if (reset)
-				statistic_reset(stat, info);
+	list_for_each_entry(coll, &ui->coll_list, list) {
+		for (i = 0; i < coll->number; i++) {
+			info = &coll->info[i];
+			stat = &coll->stat[i];
+			if (!name || (name && !strcmp(name, info->name))) {
+				if (defaults)
+					statistic_parse_match(stat, info, NULL);
+				if (name)
+					statistic_parse_match(stat, info, def);
+				if (state != STATISTIC_STATE_INVALID)
+					statistic_transition(stat, info, state);
+				if (reset)
+					statistic_reset(stat, info);
+			}
 		}
 	}
 	kfree(name);
@@ -483,10 +488,10 @@ static void statistic_parse_line(struct 
 /* sequential files comprising user interface */
 
 #define STATISTIC_LABEL_SIZE	128
-static void statistic_label(struct statistic_ui *ui, int i, void *key,
+static void statistic_label(struct statistic_coll *coll, int i, void *key,
 			    struct seq_file *seq)
 {
-	struct statistic_info *info = &ui->info[i];
+	struct statistic_info *info = &coll->info[i];
 	char *buf;
 
 	if (!(info->flags & STATISTIC_FLAGS_LABEL))
@@ -494,37 +499,123 @@ static void statistic_label(struct stati
 	buf = kzalloc(STATISTIC_LABEL_SIZE, GFP_ATOMIC);
 	if (!buf)
 		return;
-	ui->label(ui, i, key, buf, STATISTIC_LABEL_SIZE - 1);
+	coll->label(coll, i, key, buf, STATISTIC_LABEL_SIZE - 1);
 	seq_printf(seq, "%s", buf);
 	kfree(buf);
 }
 
 struct statistic_seq_private {
 	struct statistic_ui *ui;
+	struct list_head coll_list;
+	struct statistic_coll *coll;
 	int i;
 	size_t w_offset;
 	char *w_buf;
-	struct statistic *stat;
 };
 
-static void *statistic_seq_traverse(struct seq_file *seq, loff_t *pos, int inc,
-				    struct statistic_seq_private *seq_priv)
+static void statistic_snapshot_free(struct statistic_seq_private *seq_priv,
+				    int with_data)
+{
+	struct statistic_coll *coll, *next;
+	struct statistic_discipline *disc;
+	struct statistic *stat;
+	int i;
+
+	list_for_each_entry_safe(coll, next, &seq_priv->coll_list, list) {
+		if (with_data) {
+			for (i = 0; i < coll->number; i++) {
+				stat = &coll->stat[i];
+				if (stat->state < STATISTIC_STATE_OFF ||
+				    !stat->data)
+					continue;
+				disc = &statistic_discs[stat->type];
+				disc->reset(stat, stat->data);
+				kfree(stat->data);
+			}
+		}
+		list_del(&coll->list);
+		kfree(coll->stat);
+		kfree(coll);
+	}
+	kfree(seq_priv);
+}
+
+static struct statistic_seq_private *statistic_snapshot(struct statistic_ui *ui,
+							int with_data)
 {
-	*pos += inc;
-	if (*pos >= seq_priv->ui->number)
+	struct statistic_seq_private *seq_priv;
+	struct statistic_coll *coll_src, *coll_dst;
+	struct statistic_info *info;
+	struct statistic *src, *dst;
+	int size, i;
+
+	seq_priv = kzalloc(sizeof(*seq_priv), GFP_KERNEL);
+	if (!seq_priv)
 		return NULL;
-	seq_priv->i = *pos;
+
+	seq_priv->ui = ui;
+	INIT_LIST_HEAD(&seq_priv->coll_list);
+
+	list_for_each_entry(coll_src, &ui->coll_list, list) {
+		coll_dst = kmalloc(sizeof(*coll_dst), GFP_KERNEL);
+		if (!coll_dst)
+			goto failed;
+
+		memcpy(coll_dst, coll_src, sizeof(*coll_src));
+		list_add_tail(&coll_dst->list, &seq_priv->coll_list);
+		size = coll_src->number * sizeof(struct statistic);
+		coll_dst->stat = kzalloc(size, GFP_KERNEL);
+		if (!coll_dst->stat)
+			goto failed;
+
+		memcpy(coll_dst->stat, coll_src->stat, size);
+		if (with_data) {
+			for (i = 0; i < coll_dst->number; i++) {
+				src = &coll_src->stat[i];
+				dst = &coll_dst->stat[i];
+				info = &coll_dst->info[i];
+				if (src->state < STATISTIC_STATE_OFF)
+					continue;
+				dst->data = statistic_merge(src, info);
+				if (!dst->data)
+					goto failed;
+			}
+		}
+	}
 	return seq_priv;
+failed:
+	statistic_snapshot_free(seq_priv, with_data);
+	return NULL;
 }
 
 static void *statistic_seq_start(struct seq_file *seq, loff_t *pos)
 {
-	return statistic_seq_traverse(seq, pos, 0, seq->private);
+	struct statistic_seq_private *seq_priv = seq->private;
+	loff_t off = 0;
+
+	list_for_each_entry(seq_priv->coll, &seq_priv->coll_list, list)
+		for (seq_priv->i = 0; seq_priv->i < seq_priv->coll->number;
+		     seq_priv->i++, off++)
+			if (off == *pos)
+				return seq_priv;
+	return NULL;
 }
 
 static void *statistic_seq_next(struct seq_file *seq, void *_seq_priv, loff_t *pos)
 {
-	return statistic_seq_traverse(seq, pos, 1, _seq_priv);
+	struct statistic_seq_private *seq_priv = _seq_priv;
+
+	++*pos;
+	++seq_priv->i;
+	if (seq_priv->i >= seq_priv->coll->number) {
+		if (list_is_last(&seq_priv->coll->list, &seq_priv->coll_list))
+			return NULL;
+		list_for_each_entry_continue(
+				seq_priv->coll, &seq_priv->coll_list, list)
+			break;
+		seq_priv->i = 0;
+	}
+	return seq_priv;
 }
 
 static void statistic_seq_stop(struct seq_file *seq, void *_seq_priv)
@@ -542,13 +633,14 @@ static char *statistic_state_strings[] =
 static int statistic_seq_show_def(struct seq_file *seq, void *_seq_priv)
 {
 	struct statistic_seq_private *seq_priv = _seq_priv;
-	struct statistic_ui *ui = seq_priv->ui;
-	struct statistic *stat = &ui->stat[seq_priv->i];
-	struct statistic_info *info = &ui->info[seq_priv->i];
+	struct statistic_coll *coll = seq_priv->coll;
+	struct statistic_info *info = &coll->info[seq_priv->i];
+	struct statistic *stat = &coll->stat[seq_priv->i];
 	struct statistic_discipline *disc = &statistic_discs[stat->type];
 	char t0[TIMESTAMP_SIZE], t1[TIMESTAMP_SIZE], t2[TIMESTAMP_SIZE];
 
-	seq_printf(seq, "name=%s", info->name);
+	seq_printf(seq, "collection=%s", coll->name);
+	seq_printf(seq, " name=%s", info->name);
 	seq_printf(seq, " state=%s", statistic_state_strings[stat->state]);
 	seq_printf(seq, " units=%s/%s", info->x_unit, info->y_unit);
 
@@ -579,11 +671,12 @@ static int statistic_seq_show_def(struct
 static int statistic_seq_show_data(struct seq_file *seq, void *_seq_priv)
 {
 	struct statistic_seq_private *seq_priv = _seq_priv;
-	struct statistic *stat = &seq_priv->stat[seq_priv->i];
+	struct statistic_coll *coll = seq_priv->coll;
+	struct statistic *stat = &coll->stat[seq_priv->i];
 	struct statistic_discipline *disc = &statistic_discs[stat->type];
 
 	if (stat->state >= STATISTIC_STATE_OFF)
-		disc->data(stat, seq, seq_priv->ui, seq_priv->i);
+		disc->data(coll, seq_priv->i, seq);
 	return 0;
 }
 
@@ -603,21 +696,24 @@ static struct seq_operations statistic_s
 
 static int statistic_open_def(struct inode *inode, struct file *file)
 {
+	struct statistic_ui *ui = inode->i_private;
 	struct statistic_seq_private *seq_priv;
 	struct seq_file *seq;
 	int retval;
 
-	seq_priv = kzalloc(sizeof(struct statistic_seq_private), GFP_KERNEL);
+	seq_priv = statistic_snapshot(ui, 0);
 	if (!seq_priv)
 		return -ENOMEM;
-	seq_priv->ui = inode->i_private;
+
 	retval = seq_open(file, &statistic_seq_ops_def);
-	if (!retval) {
-		seq = file->private_data;
-		seq->private = seq_priv;
-	} else
-		kfree(seq_priv);
-	return retval;
+	if (retval) {
+		statistic_snapshot_free(seq_priv, 0);
+		return retval;
+	}
+
+	seq = file->private_data;
+	seq->private = seq_priv;
+	return 0;
 }
 
 static int statistic_release_def(struct inode *inode, struct file *file)
@@ -632,7 +728,8 @@ static int statistic_release_def(struct 
 			statistic_parse_line(ui, p);
 		kfree(seq_priv->w_buf);
 	}
-	return seq_release_private(inode, file);
+	statistic_snapshot_free(seq_priv, 0);
+	return seq_release(inode, file);
 }
 
 static ssize_t statistic_write_def(struct file *file, const char __user *buf,
@@ -674,76 +771,35 @@ static struct file_operations statistic_
 static int statistic_open_data(struct inode *inode, struct file *file)
 {
 	struct statistic_ui *ui = inode->i_private;
-	struct statistic *stat;
-	struct statistic_discipline *disc;
 	struct statistic_seq_private *seq_priv;
 	struct seq_file *seq;
-	int size, i, j, retval = -ENOMEM;
+	int retval;
 
 	if (ui->pull)
 		ui->pull(ui);
 
-	size = ui->number * sizeof(struct statistic);
-	seq_priv = kzalloc(sizeof(struct statistic_seq_private), GFP_KERNEL);
+	seq_priv = statistic_snapshot(ui, 1);
 	if (!seq_priv)
-		goto failed_priv;
-
-	seq_priv->ui = ui;
-	seq_priv->stat = stat = kmalloc(size, GFP_KERNEL);
-	if (!stat)
-		goto failed_stat;
-
-	memcpy(stat, ui->stat, size);
-	for (i = 0; i < ui->number; i++) {
-		if (stat[i].state < STATISTIC_STATE_OFF)
-			continue;
-		stat[i].data = statistic_merge(ui, i);
-		if (!stat[i].data)
-			goto failed_merge;
-	}
+		return -ENOMEM;
 
 	retval = seq_open(file, &statistic_seq_ops_data);
-	if (retval)
-		goto failed_open;
+	if (retval) {
+		statistic_snapshot_free(seq_priv, 1);
+		return retval;
+	}
 
 	seq = file->private_data;
 	seq->private = seq_priv;
 	return 0;
-
-failed_open:
-	for (j = 0; j < i; j++) {
-		if (stat[j].state < STATISTIC_STATE_OFF)
-			continue;
-		disc = &statistic_discs[stat[j].type];
-		disc->reset(&stat[j], stat[j].data);
-		kfree(stat[j].data);
-	}
-failed_merge:
-	kfree(stat);
-failed_stat:
-	kfree(seq_priv);
-failed_priv:
-	return retval;
 }
 
 static int statistic_release_data(struct inode *inode, struct file *file)
 {
 	struct seq_file *seq = file->private_data;
 	struct statistic_seq_private *seq_priv = seq->private;
-	struct statistic_ui *ui = seq_priv->ui;
-	struct statistic *stat = seq_priv->stat;
-	struct statistic_discipline *disc;
-	int i;
 
-	for (i = 0; i < ui->number; i++) {
-		if (stat[i].state < STATISTIC_STATE_OFF)
-			continue;
-		disc = &statistic_discs[stat[i].type];
-		disc->reset(&stat[i], stat[i].data);
-		kfree(stat[i].data);
-	}
-	kfree(stat);
-	return seq_release_private(inode, file);
+	statistic_snapshot_free(seq_priv, 1);
+	return seq_release(inode, file);
 }
 
 static struct file_operations statistic_data_fops = {
@@ -800,13 +856,14 @@ static void statistic_merge_counter(stru
 	*(u64*)dst += *(u64*)src;
 }
 
-static void statistic_data_counter(struct statistic *stat, struct seq_file *seq,
-				   struct statistic_ui *ui, int i)
+static void statistic_data_counter(struct statistic_coll *coll, int i,
+				   struct seq_file *seq)
 {
-	struct statistic_info *info = &ui->info[i];
+	struct statistic_info *info = &coll->info[i];
+	struct statistic *stat = &coll->stat[i];
 
-	seq_printf(seq, "%s %Lu\n",
-		   info->name, *(unsigned long long *)stat->data);
+	seq_printf(seq, "%s %s %Lu\n", coll->name, info->name,
+		   *(unsigned long long *)stat->data);
 }
 
 /* code concerned with utilisation indicator statistic */
@@ -889,10 +946,11 @@ static int statistic_div(signed long lon
 	return 0;
 }
 
-static void statistic_data_util(struct statistic *stat, struct seq_file *seq,
-				struct statistic_ui *ui, int i)
+static void statistic_data_util(struct statistic_coll *coll, int i,
+				struct seq_file *seq)
 {
-	struct statistic_info *info = &ui->info[i];
+	struct statistic_info *info = &coll->info[i];
+	struct statistic *stat = &coll->stat[i];
 	struct statistic_entry_util *util = stat->data;
 	unsigned long long mean_w = 0, mean_d = 0, var_w = 0, var_d = 0,
 			   num = util->num, acc = util->acc, sqr = util->sqr;
@@ -901,14 +959,14 @@ static void statistic_data_util(struct s
 
 	statistic_div(&mean_w, &mean_d, acc, num, 3);
 	statistic_div(&var_w, &var_d, sqr - mean_w * mean_w, num, 3);
-	seq_printf(seq, "%s samples %Lu\n%s minimum %Ld\n"
-		   "%s average %Ld.%03Ld\n%s maximum %Ld\n"
-		   "%s variance %Ld.%03Ld\n",
-		   info->name, num,
-		   info->name, min,
-		   info->name, mean_w, mean_d,
-		   info->name, max,
-		   info->name, var_w, var_d);
+	seq_printf(seq, "%s %s samples %Lu\n%s %s minimum %Ld\n"
+		   "%s %s average %Ld.%03Ld\n%s %s maximum %Ld\n"
+		   "%s %s variance %Ld.%03Ld\n",
+		   coll->name, info->name, num,
+		   coll->name, info->name, min,
+		   coll->name, info->name, mean_w, mean_d,
+		   coll->name, info->name, max,
+		   coll->name, info->name, var_w, var_d);
 }
 
 /* code concerned with histogram statistics */
@@ -1006,29 +1064,30 @@ static void statistic_merge_histogram(st
 static void _statistic_data_histogram(struct seq_file *seq, const char *prefix,
 				      signed long long bound,
 				      unsigned long long hits,
-				      struct statistic_info *info,
-				      struct statistic_ui *ui, int i)
+				      struct statistic_coll *coll, int i)
 {
-	seq_printf(seq, "%s %s%Ld %Lu ", info->name, prefix, bound, hits);
-	statistic_label(ui, i, &bound, seq);
+	struct statistic_info *info = &coll->info[i];
+
+	seq_printf(seq, "%s %s %s%Ld %Lu ", coll->name, info->name, prefix,
+		   bound, hits);
+	statistic_label(coll, i, &bound, seq);
 	seq_printf(seq, "\n");
 }
 
-static void statistic_data_histogram(struct statistic *stat,
-				     struct seq_file *seq,
-				     struct statistic_ui *ui, int i)
+static void statistic_data_histogram(struct statistic_coll *coll, int i,
+				     struct seq_file *seq)
 {
-	struct statistic_info *info = &ui->info[i];
-	int j;
+	struct statistic *stat = &coll->stat[i];
 	signed long long bound = 0;
 	unsigned long long hits = 0;
+	int j;
 
 	for (j = 0; j < (stat->u.histogram.last_index); j++) {
 		bound = statistic_histogram_calc_value(stat, j);
 		hits = ((u64*)stat->data)[j];
-		_statistic_data_histogram(seq, "<=", bound, hits, info, ui, i);
+		_statistic_data_histogram(seq, "<=", bound, hits, coll, i);
 	}
-	_statistic_data_histogram(seq, ">", bound, hits, info, ui, i);
+	_statistic_data_histogram(seq, ">", bound, hits, coll, i);
 }
 
 static void statistic_def_histogram(struct statistic *stat,
@@ -1213,25 +1272,26 @@ static void statistic_merge_sparse(struc
 				    entry->key, entry->hits);
 }
 
-static void statistic_data_sparse(struct statistic *stat, struct seq_file *seq,
-				  struct statistic_ui *ui, int i)
+static void statistic_data_sparse(struct statistic_coll *coll, int i,
+				  struct seq_file *seq)
 {
-	struct statistic_info *info = &ui->info[i];
+	struct statistic_info *info = &coll->info[i];
+	struct statistic *stat  =&coll->stat[i];
 	struct statistic_sparse_list *slist = stat->data;
 	struct statistic_entry_sparse *entry;
 
 	list_for_each_entry(entry, &slist->entry_lh, list) {
-		seq_printf(seq, "%s ", info->name);
+		seq_printf(seq, "%s %s ", coll->name, info->name);
 		if (info->flags & _STATISTIC_FLAGS_KEY)
 			seq_printf(seq, "- ");
 		else
 			seq_printf(seq, "0x%Lx ",
 				   *(signed long long *)entry->key);
 		seq_printf(seq, "%Lu ", (unsigned long long)entry->hits);
-		statistic_label(ui, i, entry->key, seq);
+		statistic_label(coll, i, entry->key, seq);
 		seq_printf(seq, "\n");
 	}
-	seq_printf(seq, "%s other %Lu\n", info->name,
+	seq_printf(seq, "%s %s other %Lu\n", coll->name, info->name,
 		   (unsigned long long)slist->hits_missed);
 }
 
@@ -1332,49 +1392,34 @@ static struct statistic_discipline stati
 /* programming interface functions */
 
 /**
- * statistic_create - setup statistics and create debugfs files
- * @ui: struct statistic_ui provided by client
- * @name: name of debugfs directory to be created
+ * statistic_attach - stuff more statistics into a user interface
+ * @coll: struct statistic_coll provided by client
+ * @ui: struct statistic_ui to attach to
  *
- * Creates a debugfs directory in "statistics" as well as the "data" and
- * "definition" files. Then we attach setup statistics according to the
- * definition provided by client through struct statistic_ui.
- *
- * struct statistic_ui must have been set up prior to calling this.
+ * Attach statistics to user interface.
+ * Setup statistics according to default definition.
  *
  * On success, 0 is returned.
  *
  * If some required memory could not be allocated, or the creation
  * of debugfs entries failed, this routine fails, and -ENOMEM is returned.
  */
-int statistic_create(struct statistic_ui *ui, const char *name)
+int statistic_attach(struct statistic_coll *coll, struct statistic_ui *ui)
 {
-	struct statistic *stat = ui->stat;
-	struct statistic_info *info = ui->info;
+	struct statistic *stat = coll->stat;
+	struct statistic_info *info = coll->info;
 	int i;
 
-	BUG_ON(!stat || !info || !ui->number);
+	BUG_ON(!stat || !info || !coll->number);
 
-	ui->dir = debugfs_create_dir(name, statistic_root_dir);
 	if (unlikely(!ui->dir))
 		return -ENOMEM;
 
-	ui->data = debugfs_create_file("data", S_IFREG | S_IRUSR, ui->dir,
-				       (void*)ui, &statistic_data_fops);
-	if (unlikely(!ui->data)) {
-		debugfs_remove(ui->dir);
-		return -ENOMEM;
-	}
-
-	ui->def = debugfs_create_file("definition", S_IFREG | S_IRUSR | S_IWUSR,
-				      ui->dir, (void*)ui, &statistic_def_fops);
-	if (unlikely(!ui->def)) {
-		debugfs_remove(ui->data);
-		debugfs_remove(ui->dir);
-		return -ENOMEM;
-	}
+	mutex_lock(&statistic_list_mutex);
+	list_add_tail(&coll->list, &ui->coll_list);
+	mutex_unlock(&statistic_list_mutex);
 
-	for (i = 0; i < ui->number; i++, stat++, info++) {
+	for (i = 0; i < coll->number; i++, stat++, info++) {
 		if (info->flags & _STATISTIC_FLAGS_KEY)
 			stat->key_size = info->key_size;
 		else
@@ -1382,11 +1427,76 @@ int statistic_create(struct statistic_ui
 		statistic_transition(stat, info, STATISTIC_STATE_UNCONFIGURED);
 		statistic_parse_match(stat, info, NULL);
 	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(statistic_attach);
+
+/**
+ * statistic_detach - rip statistics out from user interface
+ * @coll: struct statistic_coll to clean up
+ *
+ * The client must have ceased reporting statistic data.
+ *
+ * Returns -EINVAL for attempted double removal, 0 otherwise.
+ */
+int statistic_detach(struct statistic_coll *coll)
+{
+	struct statistic *stat = coll->stat;
+	struct statistic_info *info = coll->info;
+	int i;
+
+	mutex_lock(&statistic_list_mutex);
+	list_del(&coll->list);
+	mutex_unlock(&statistic_list_mutex);
+	for (i = 0; i < coll->number; i++, stat++, info++)
+		statistic_transition(stat, info, STATISTIC_STATE_INVALID);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(statistic_detach);
+
+/**
+ * statistic_create - create debugfs files
+ * @ui: struct statistic_ui provided by client
+ * @name: name of debugfs directory to be created
+ *
+ * Creates a debugfs directory in "statistics" as well as the "data" and
+ * "definition" files.
+ *
+ * struct statistic_ui must have been set up prior to calling this.
+ *
+ * On success, 0 is returned.
+ *
+ * If some required memory could not be allocated, or the creation
+ * of debugfs entries failed, this routine fails, and -ENOMEM is returned.
+ */
+int statistic_create(struct statistic_ui *ui, const char *name)
+{
+	ui->dir = debugfs_create_dir(name, statistic_root_dir);
+	if (unlikely(!ui->dir))
+		goto failed_dir;
 
+	ui->data = debugfs_create_file("data", S_IFREG | S_IRUSR, ui->dir,
+				       (void*)ui, &statistic_data_fops);
+	if (unlikely(!ui->data))
+		goto failed_data;
+
+	ui->def = debugfs_create_file("definition", S_IFREG | S_IRUSR | S_IWUSR,
+				      ui->dir, (void*)ui, &statistic_def_fops);
+	if (unlikely(!ui->def))
+		goto failed_def;
+
+	INIT_LIST_HEAD(&ui->coll_list);
 	mutex_lock(&statistic_list_mutex);
 	list_add(&ui->list, &statistic_list);
 	mutex_unlock(&statistic_list_mutex);
 	return 0;
+
+failed_def:
+	debugfs_remove(ui->data);
+failed_data:
+	debugfs_remove(ui->dir);
+failed_dir:
+	return -ENOMEM;
 }
 EXPORT_SYMBOL_GPL(statistic_create);
 
@@ -1404,17 +1514,15 @@ EXPORT_SYMBOL_GPL(statistic_create);
  */
 int statistic_remove(struct statistic_ui *ui)
 {
-	struct statistic *stat = ui->stat;
-	struct statistic_info *info = ui->info;
-	int i;
+	struct statistic_coll *coll, *next;
 
 	if (unlikely(!ui->dir))
 		return -EINVAL;
 	mutex_lock(&statistic_list_mutex);
 	list_del(&ui->list);
 	mutex_unlock(&statistic_list_mutex);
-	for (i = 0; i < ui->number; i++, stat++, info++)
-		statistic_transition(stat, info, STATISTIC_STATE_INVALID);
+	list_for_each_entry_safe(coll, next, &ui->coll_list, list)
+		statistic_detach(coll);
 	debugfs_remove(ui->data);
 	debugfs_remove(ui->def);
 	debugfs_remove(ui->dir);


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

Powered by blists - more mailing lists