mm: scalable bdi statistics counters
Peter Zijlstra [Wed, 17 Oct 2007 06:25:47 +0000 (23:25 -0700)]
Provide scalable per backing_dev_info statistics counters.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

include/linux/backing-dev.h
mm/backing-dev.c

index 8f56634..f7677ff 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef _LINUX_BACKING_DEV_H
 #define _LINUX_BACKING_DEV_H
 
+#include <linux/percpu_counter.h>
+#include <linux/log2.h>
 #include <asm/atomic.h>
 
 struct page;
@@ -24,6 +26,12 @@ enum bdi_state {
 
 typedef int (congested_fn)(void *, int);
 
+enum bdi_stat_item {
+       NR_BDI_STAT_ITEMS
+};
+
+#define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids)))
+
 struct backing_dev_info {
        unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */
        unsigned long state;    /* Always use atomic bitops on this */
@@ -32,15 +40,86 @@ struct backing_dev_info {
        void *congested_data;   /* Pointer to aux data for congested func */
        void (*unplug_io_fn)(struct backing_dev_info *, struct page *);
        void *unplug_io_data;
+
+       struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];
 };
 
-static inline int bdi_init(struct backing_dev_info *bdi)
+int bdi_init(struct backing_dev_info *bdi);
+void bdi_destroy(struct backing_dev_info *bdi);
+
+static inline void __add_bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item, s64 amount)
 {
-       return 0;
+       __percpu_counter_add(&bdi->bdi_stat[item], amount, BDI_STAT_BATCH);
 }
 
-static inline void bdi_destroy(struct backing_dev_info *bdi)
+static inline void __inc_bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
 {
+       __add_bdi_stat(bdi, item, 1);
+}
+
+static inline void inc_bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __inc_bdi_stat(bdi, item);
+       local_irq_restore(flags);
+}
+
+static inline void __dec_bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       __add_bdi_stat(bdi, item, -1);
+}
+
+static inline void dec_bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __dec_bdi_stat(bdi, item);
+       local_irq_restore(flags);
+}
+
+static inline s64 bdi_stat(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       return percpu_counter_read_positive(&bdi->bdi_stat[item]);
+}
+
+static inline s64 __bdi_stat_sum(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       return percpu_counter_sum_positive(&bdi->bdi_stat[item]);
+}
+
+static inline s64 bdi_stat_sum(struct backing_dev_info *bdi,
+               enum bdi_stat_item item)
+{
+       s64 sum;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       sum = __bdi_stat_sum(bdi, item);
+       local_irq_restore(flags);
+
+       return sum;
+}
+
+/*
+ * maximal error of a stat counter.
+ */
+static inline unsigned long bdi_stat_error(struct backing_dev_info *bdi)
+{
+#ifdef CONFIG_SMP
+       return nr_cpu_ids * BDI_STAT_BATCH;
+#else
+       return 1;
+#endif
 }
 
 /*
index 841901a..a47065e 100644 (file)
@@ -5,6 +5,33 @@
 #include <linux/sched.h>
 #include <linux/module.h>
 
+int bdi_init(struct backing_dev_info *bdi)
+{
+       int i, j;
+       int err;
+
+       for (i = 0; i < NR_BDI_STAT_ITEMS; i++) {
+               err = percpu_counter_init_irq(&bdi->bdi_stat[i], 0);
+               if (err) {
+                       for (j = 0; j < i; j++)
+                               percpu_counter_destroy(&bdi->bdi_stat[i]);
+                       break;
+               }
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(bdi_init);
+
+void bdi_destroy(struct backing_dev_info *bdi)
+{
+       int i;
+
+       for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
+               percpu_counter_destroy(&bdi->bdi_stat[i]);
+}
+EXPORT_SYMBOL(bdi_destroy);
+
 static wait_queue_head_t congestion_wqh[2] = {
                __WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[0]),
                __WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[1])