[patch] Make diskstats per-cpu using kmalloc_percpu

From: Ravikiran G Thirumalai (kiran@in.ibm.com)
Date: Tue Feb 25 2003 - 02:36:54 EST


Hi Andrew,
Sometime back you'd asked for this on irc. This version makes
the disk stats on struct gendisk per-cpu. I am working on making the
per partition stats per-cpu too (struct hd_struct), Meanwhile, I was
wondering if this can get some testing on the -mm tree. Patch has been
tested on a PIII 4way in smp and up configurations. Applies on 2.5.63.

Thanks,
Kiran

 drivers/block/genhd.c | 23 +++++++---
 drivers/block/ll_rw_blk.c | 29 +++++++------
 drivers/md/md.c | 4 +
 fs/partitions/check.c | 8 ---
 include/linux/genhd.h | 97 ++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 124 insertions(+), 37 deletions(-)

diff -ruN -X dontdiff linux-2.5.62/drivers/block/genhd.c diskstat-2.5.62/drivers/block/genhd.c
--- linux-2.5.62/drivers/block/genhd.c Tue Feb 18 04:26:14 2003
+++ diskstat-2.5.62/drivers/block/genhd.c Wed Feb 19 15:44:47 2003
@@ -328,14 +328,16 @@
                 "%8u %8u %8llu %8u "
                 "%8u %8u %8u"
                 "\n",
- disk->reads, disk->read_merges,
- (unsigned long long)disk->read_sectors,
- jiffies_to_msec(disk->read_ticks),
- disk->writes, disk->write_merges,
- (unsigned long long)disk->write_sectors,
- jiffies_to_msec(disk->write_ticks),
- disk->in_flight, jiffies_to_msec(disk->io_ticks),
- jiffies_to_msec(disk->time_in_queue));
+ DISK_STAT_READ(disk, reads), DISK_STAT_READ(disk, read_merges),
+ (unsigned long long)DISK_STAT_READ(disk, read_sectors),
+ jiffies_to_msec(DISK_STAT_READ(disk, read_ticks)),
+ DISK_STAT_READ(disk, writes),
+ DISK_STAT_READ(disk, write_merges),
+ (unsigned long long)DISK_STAT_READ(disk, write_sectors),
+ jiffies_to_msec(DISK_STAT_READ(disk, write_ticks)),
+ DISK_STAT_READ(disk, in_flight),
+ jiffies_to_msec(DISK_STAT_READ(disk, io_ticks)),
+ jiffies_to_msec(DISK_STAT_READ(disk, time_in_queue)));
 }
 static struct disk_attribute disk_attr_dev = {
         .attr = {.name = "dev", .mode = S_IRUGO },
@@ -367,6 +369,7 @@
         struct gendisk *disk = to_disk(kobj);
         kfree(disk->random);
         kfree(disk->part);
+ free_disk_stats(disk);
         kfree(disk);
 }
 
@@ -386,6 +389,10 @@
         struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
         if (disk) {
                 memset(disk, 0, sizeof(struct gendisk));
+ if (!init_disk_stats(disk)) {
+ kfree(disk);
+ return NULL;
+ }
                 if (minors > 1) {
                         int size = (minors - 1) * sizeof(struct hd_struct);
                         disk->part = kmalloc(size, GFP_KERNEL);
diff -ruN -X dontdiff linux-2.5.62/drivers/block/ll_rw_blk.c diskstat-2.5.62/drivers/block/ll_rw_blk.c
--- linux-2.5.62/drivers/block/ll_rw_blk.c Tue Feb 18 04:25:53 2003
+++ diskstat-2.5.62/drivers/block/ll_rw_blk.c Thu Feb 20 12:07:18 2003
@@ -1474,17 +1474,17 @@
                 return;
 
         if (rw == READ) {
- rq->rq_disk->read_sectors += nr_sectors;
+ DISK_STAT_ADD(rq->rq_disk, read_sectors, nr_sectors);
                 if (!new_io)
- rq->rq_disk->read_merges++;
+ DISK_STAT_INC(rq->rq_disk, read_merges);
         } else if (rw == WRITE) {
- rq->rq_disk->write_sectors += nr_sectors;
+ DISK_STAT_ADD(rq->rq_disk, write_sectors, nr_sectors);
                 if (!new_io)
- rq->rq_disk->write_merges++;
+ DISK_STAT_INC(rq->rq_disk, write_merges);
         }
         if (new_io) {
                 disk_round_stats(rq->rq_disk);
- rq->rq_disk->in_flight++;
+ DISK_STAT_INC(rq->rq_disk, in_flight);
         }
 }
 
@@ -1524,11 +1524,12 @@
 {
         unsigned long now = jiffies;
 
- disk->time_in_queue += disk->in_flight * (now - disk->stamp);
+ DISK_STAT_ADD(disk, time_in_queue,
+ DISK_STAT_READ(disk, in_flight) * (now - disk->stamp));
         disk->stamp = now;
 
- if (disk->in_flight)
- disk->io_ticks += (now - disk->stamp_idle);
+ if (DISK_STAT_READ(disk, in_flight))
+ DISK_STAT_ADD(disk, io_ticks, (now - disk->stamp_idle));
         disk->stamp_idle = now;
 }
 
@@ -1646,7 +1647,7 @@
 
                 if (req->rq_disk) {
                         disk_round_stats(req->rq_disk);
- req->rq_disk->in_flight--;
+ DISK_STAT_DEC(req->rq_disk, in_flight);
                 }
 
                 __blk_put_request(q, next);
@@ -2198,16 +2199,16 @@
                 unsigned long duration = jiffies - req->start_time;
                 switch (rq_data_dir(req)) {
                     case WRITE:
- disk->writes++;
- disk->write_ticks += duration;
+ DISK_STAT_INC(disk, writes);
+ DISK_STAT_ADD(disk, write_ticks, duration);
                         break;
                     case READ:
- disk->reads++;
- disk->read_ticks += duration;
+ DISK_STAT_INC(disk, reads);
+ DISK_STAT_ADD(disk, read_ticks, duration);
                         break;
                 }
                 disk_round_stats(disk);
- disk->in_flight--;
+ DISK_STAT_DEC(disk, in_flight);
         }
         __blk_put_request(req->q, req);
 }
diff -ruN -X dontdiff linux-2.5.62/drivers/md/md.c diskstat-2.5.62/drivers/md/md.c
--- linux-2.5.62/drivers/md/md.c Tue Feb 18 04:26:10 2003
+++ diskstat-2.5.62/drivers/md/md.c Wed Feb 19 15:56:38 2003
@@ -2771,7 +2771,9 @@
         idle = 1;
         ITERATE_RDEV(mddev,rdev,tmp) {
                 struct gendisk *disk = rdev->bdev->bd_contains->bd_disk;
- curr_events = disk->read_sectors + disk->write_sectors - disk->sync_io;
+ curr_events = DISK_STAT_READ(disk, read_sectors) +
+ DISK_STAT_READ(disk, write_sectors) -
+ disk->sync_io;
                 if ((curr_events - rdev->last_events) > 32) {
                         rdev->last_events = curr_events;
                         idle = 0;
diff -ruN -X dontdiff linux-2.5.62/fs/partitions/check.c diskstat-2.5.62/fs/partitions/check.c
--- linux-2.5.62/fs/partitions/check.c Tue Feb 18 04:26:56 2003
+++ diskstat-2.5.62/fs/partitions/check.c Wed Feb 19 16:48:13 2003
@@ -503,13 +503,7 @@
         disk->capacity = 0;
         disk->flags &= ~GENHD_FL_UP;
         unlink_gendisk(disk);
- disk->reads = disk->writes = 0;
- disk->read_sectors = disk->write_sectors = 0;
- disk->read_merges = disk->write_merges = 0;
- disk->read_ticks = disk->write_ticks = 0;
- disk->in_flight = 0;
- disk->io_ticks = 0;
- disk->time_in_queue = 0;
+ disk_stat_set_all(disk, 0);
         disk->stamp = disk->stamp_idle = 0;
         devfs_remove_partitions(disk);
         if (disk->driverfs_dev) {
diff -ruN -X dontdiff linux-2.5.62/include/linux/genhd.h diskstat-2.5.62/include/linux/genhd.h
--- linux-2.5.62/include/linux/genhd.h Tue Feb 18 04:25:57 2003
+++ diskstat-2.5.62/include/linux/genhd.h Thu Feb 20 12:05:14 2003
@@ -73,6 +73,16 @@
 #define GENHD_FL_CD 8
 #define GENHD_FL_UP 16
 
+struct disk_stats {
+ unsigned read_sectors, write_sectors;
+ unsigned reads, writes;
+ unsigned read_merges, write_merges;
+ unsigned read_ticks, write_ticks;
+ unsigned io_ticks;
+ int in_flight;
+ unsigned time_in_queue;
+};
+
 struct gendisk {
         int major; /* major number of driver */
         int first_minor;
@@ -97,16 +107,89 @@
         int policy;
 
         unsigned sync_io; /* RAID */
- unsigned read_sectors, write_sectors;
- unsigned reads, writes;
- unsigned read_merges, write_merges;
- unsigned read_ticks, write_ticks;
- unsigned io_ticks;
- int in_flight;
         unsigned long stamp, stamp_idle;
- unsigned time_in_queue;
+#ifdef CONFIG_SMP
+ struct disk_stats *dkstats;
+#else
+ struct disk_stats dkstats;
+#endif
 };
 
+/*
+ * Macros to operate on percpu disk statistics:
+ * Since writes to disk_stats are serialised through the queue_lock,
+ * smp_processor_id() should be enough to get to the per_cpu versions
+ * of statistics counters
+ */
+#ifdef CONFIG_SMP
+#define DISK_STAT_INC(gendiskp, field) \
+ (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field++)
+#define DISK_STAT_DEC(gendiskp, field) \
+ (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field--)
+#define DISK_STAT_ADD(gendiskp, field, addnd) \
+ (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd)
+#define DISK_STAT_SUB(gendiskp, field, subnd) \
+ (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field -= subnd)
+#define DISK_STAT_READ(gendiskp, field) \
+({ \
+ typeof(gendiskp->dkstats->field) res = 0; \
+ int i; \
+ for (i=0; i < NR_CPUS; i++) { \
+ if (!cpu_possible(i)) \
+ continue; \
+ res += per_cpu_ptr(gendiskp->dkstats, i)->field; \
+ } \
+ res; \
+})
+
+static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
+ int i;
+ for (i=0; i < NR_CPUS; i++) {
+ if (cpu_possible(i)) {
+ memset(per_cpu_ptr(gendiskp->dkstats, i), value,
+ sizeof (struct disk_stats));
+ }
+ }
+}
+
+#else
+#define DISK_STAT_INC(gendiskp, field) (gendiskp->dkstats.field++)
+#define DISK_STAT_DEC(gendiskp, field) (gendiskp->dkstats.field--)
+#define DISK_STAT_ADD(gendiskp, field, addnd) (gendiskp->dkstats.field += addnd)
+#define DISK_STAT_SUB(gendiskp, field, subnd) (gendiskp->dkstats.field -= subnd)
+#define DISK_STAT_READ(gendiskp, field) (gendiskp->dkstats.field)
+
+static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) {
+ memset(&gendiskp->dkstats, value, sizeof (struct disk_stats));
+}
+#endif
+
+/* Inlines to alloc and free disk stats in struct gendisk */
+#ifdef CONFIG_SMP
+static inline int init_disk_stats(struct gendisk *disk)
+{
+ disk->dkstats = kmalloc_percpu(sizeof (struct disk_stats), GFP_KERNEL);
+ if (!disk->dkstats)
+ return 0;
+ disk_stat_set_all(disk, 0);
+ return 1;
+}
+
+static inline void free_disk_stats(struct gendisk *disk)
+{
+ kfree_percpu(disk->dkstats);
+}
+#else /* CONFIG_SMP */
+static inline int init_disk_stats(struct gendisk *disk)
+{
+ return 1;
+}
+
+static inline void free_disk_stats(struct gendisk *disk)
+{
+}
+#endif /* CONFIG_SMP */
+
 /* drivers/block/ll_rw_blk.c */
 extern void disk_round_stats(struct gendisk *disk);
 
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Feb 28 2003 - 22:00:27 EST