All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] [Patch 3/4] statistics: sets of statistics in single file
@ 2007-06-06 21:33 Martin Peschke
  0 siblings, 0 replies; only message in thread
From: Martin Peschke @ 2007-06-06 21:33 UTC (permalink / raw)
  To: linux-kernel; +Cc: a.p.zijlstra, jbaron, rostedt, billh, mingo, linux-s390

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@de.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);

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2007-06-06 21:33 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-06-06 21:33 [RFC] [Patch 3/4] statistics: sets of statistics in single file Martin Peschke

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.