]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] IO counters - per-disk part
authorAlexander Viro <viro@math.psu.edu>
Mon, 28 Oct 2002 10:51:09 +0000 (02:51 -0800)
committerJames Bottomley <jejb@mulgrave.(none)>
Mon, 28 Oct 2002 10:51:09 +0000 (02:51 -0800)
drivers/block/ll_rw_blk.c
drivers/md/md.c
fs/partitions/check.c
include/linux/blkdev.h
include/linux/genhd.h

index adedc191c07a0199550be862c6ddfcfd90d00431..bcac819aa1b8b8b49de275393d7a7ecdaf3c9dfb 100644 (file)
@@ -1414,11 +1414,17 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io)
                return;
 
        if (rw == READ) {
-               rq->rq_disk->rio += new_io;
-               rq->rq_disk->reads += nr_sectors;
+               rq->rq_disk->read_sectors += nr_sectors;
+               if (!new_io)
+                       rq->rq_disk->read_merges++;
        } else if (rw == WRITE) {
-               rq->rq_disk->wio += new_io;
-               rq->rq_disk->writes += nr_sectors;
+               rq->rq_disk->write_sectors += nr_sectors;
+               if (!new_io)
+                       rq->rq_disk->write_merges++;
+       }
+       if (new_io) {
+               disk_round_stats(rq->rq_disk);
+               rq->rq_disk->in_flight++;
        }
 
        index = rq->rq_disk->first_minor >> rq->rq_disk->minor_shift;
@@ -1453,6 +1459,33 @@ static inline void add_request(request_queue_t * q, struct request * req,
         */
        __elv_add_request_pos(q, req, insert_here);
 }
+/*
+ * disk_round_stats()  - Round off the performance stats on a struct
+ * disk_stats.
+ *
+ * The average IO queue length and utilisation statistics are maintained
+ * by observing the current state of the queue length and the amount of
+ * time it has been in this state for.
+ *
+ * Normally, that accounting is done on IO completion, but that can result
+ * in more than a second's worth of IO being accounted for within any one
+ * second, leading to >100% utilisation.  To deal with that, we call this
+ * function to do a round-off before returning the results when reading
+ * /proc/diskstats.  This accounts immediately for all queue usage up to
+ * the current jiffies and restarts the counters again.
+ */
+void disk_round_stats(struct gendisk *disk)
+{
+       unsigned long now = jiffies;
+
+       disk->time_in_queue += disk->in_flight * (now - disk->stamp);
+       disk->stamp = now;
+
+       if (disk->in_flight)
+               disk->io_ticks += (now - disk->stamp_idle);
+       disk->stamp_idle = now;
+}
 
 void __blk_put_request(request_queue_t *q, struct request *req)
 {
@@ -1567,6 +1600,11 @@ static void attempt_merge(request_queue_t *q, struct request *req,
 
                elv_merge_requests(q, req, next);
 
+               if (req->rq_disk) {
+                       disk_round_stats(req->rq_disk);
+                       req->rq_disk->in_flight--;
+               }
+
                blkdev_dequeue_request(next);
                __blk_put_request(q, next);
        }
@@ -1758,6 +1796,7 @@ get_rq:
        req->bio = req->biotail = bio;
        req->rq_dev = to_kdev_t(bio->bi_bdev->bd_dev);
        req->rq_disk = bio->bi_bdev->bd_disk;
+       req->start_time = jiffies;
        add_request(q, req, insert_here);
 out:
        if (freereq)
@@ -2096,9 +2135,25 @@ int end_that_request_chunk(struct request *req, int uptodate, int nr_bytes)
  */
 void end_that_request_last(struct request *req)
 {
+       struct gendisk *disk = req->rq_disk;
        if (req->waiting)
                complete(req->waiting);
 
+       if (disk) {
+               unsigned long duration = jiffies - req->start_time;
+               switch (rq_data_dir(req)) {
+                   case WRITE:
+                       disk->writes++;
+                       disk->write_ticks += duration;
+                       break;
+                   case READ:
+                       disk->reads++;
+                       disk->read_ticks += duration;
+                       break;
+               }
+               disk_round_stats(disk);
+               disk->in_flight--;
+       }
        __blk_put_request(req->q, req);
 }
 
index 23df6aa78556670466cd8f5a88d89050e9ede1cf..defa2e3828af4cfda4e97d3c5847fec53a861c24 100644 (file)
@@ -2768,7 +2768,7 @@ static int is_mddev_idle(mddev_t *mddev)
        idle = 1;
        ITERATE_RDEV(mddev,rdev,tmp) {
                struct gendisk *disk = rdev->bdev->bd_disk;
-               curr_events = disk->reads + disk->writes - disk->sync_io;
+               curr_events = disk->read_sectors + disk->write_sectors - disk->sync_io;
                if ((curr_events - rdev->last_events) > 32) {
                        rdev->last_events = curr_events;
                        idle = 0;
index d136c6d716dd2e8032dff25205dd0ad3c587a5c1..068559518ba56717e43626ae8034dd82dd5ef618 100644 (file)
@@ -302,9 +302,9 @@ static ssize_t part_stat_read(struct device *dev,
                        char *page, size_t count, loff_t off)
 {
        struct hd_struct *p = dev->driver_data;
-       return off ? 0 : sprintf(page, "%u %u %u %u\n",
-                               p->reads, p->read_sectors,
-                               p->writes, p->write_sectors);
+       return off ? 0 : sprintf(page, "%8u %8llu %8u %8llu\n",
+                               p->reads, (u64)p->read_sectors,
+                               p->writes, (u64)p->write_sectors);
 }
 static struct device_attribute part_attr_dev = {
        .attr = {.name = "dev", .mode = S_IRUGO },
@@ -393,6 +393,27 @@ static ssize_t disk_size_read(struct device *dev,
        struct gendisk *disk = dev->driver_data;
        return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk));
 }
+static inline unsigned MSEC(unsigned x)
+{
+       return x * 1000 / HZ;
+}
+static ssize_t disk_stat_read(struct device *dev,
+                       char *page, size_t count, loff_t off)
+{
+       struct gendisk *disk = dev->driver_data;
+       disk_round_stats(disk);
+       return off ? 0 : sprintf(page,
+               "%8u %8u %8llu %8u "
+               "%8u %8u %8llu %8u "
+               "%8u %8u %8u"
+               "\n",
+               disk->reads, disk->read_merges, (u64)disk->read_sectors,
+               MSEC(disk->read_ticks),
+               disk->writes, disk->write_merges, (u64)disk->write_sectors,
+               MSEC(disk->write_ticks),
+               disk->in_flight, MSEC(disk->io_ticks),
+               MSEC(disk->time_in_queue));
+}
 static struct device_attribute disk_attr_dev = {
        .attr = {.name = "dev", .mode = S_IRUGO },
        .show   = disk_dev_read
@@ -405,6 +426,10 @@ static struct device_attribute disk_attr_size = {
        .attr = {.name = "size", .mode = S_IRUGO },
        .show   = disk_size_read
 };
+static struct device_attribute disk_attr_stat = {
+       .attr = {.name = "stat", .mode = S_IRUGO },
+       .show   = disk_stat_read
+};
 
 static void disk_driverfs_symlinks(struct gendisk *disk)
 {
@@ -475,6 +500,7 @@ void register_disk(struct gendisk *disk)
        device_create_file(dev, &disk_attr_dev);
        device_create_file(dev, &disk_attr_range);
        device_create_file(dev, &disk_attr_size);
+       device_create_file(dev, &disk_attr_stat);
        disk_driverfs_symlinks(disk);
 
        if (disk->flags & GENHD_FL_CD)
@@ -585,10 +611,19 @@ void del_gendisk(struct gendisk *disk)
        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->stamp = disk->stamp_idle = 0;
        devfs_remove_partitions(disk);
        device_remove_file(&disk->disk_dev, &disk_attr_dev);
        device_remove_file(&disk->disk_dev, &disk_attr_range);
        device_remove_file(&disk->disk_dev, &disk_attr_size);
+       device_remove_file(&disk->disk_dev, &disk_attr_stat);
        driverfs_remove_file(&disk->disk_dev.dir, "device");
        if (disk->driverfs_dev) {
                driverfs_remove_file(&disk->driverfs_dev->dir, "block");
index c202131adc1f737c703712945651d32c5ab8d67a..cb3b342d41635ccfa9d59f9c5b3c7d8ab6bd2e1d 100644 (file)
@@ -39,6 +39,7 @@ struct request {
        struct gendisk *rq_disk;
        int errors;
        sector_t sector;
+       unsigned long start_time;
        unsigned long nr_sectors;
        sector_t hard_sector;           /* the hard_* are block layer
                                         * internals, no driver should
index 61e62a66e0fafbe52a1850742bd595cf6d2ffa2b..ff1885251c12c95dbfec89dbb4fc77d9e2117162 100644 (file)
@@ -99,10 +99,19 @@ struct gendisk {
        int policy;
 
        unsigned sync_io;               /* RAID */
+       unsigned read_sectors, write_sectors;
        unsigned reads, writes;
-       unsigned rio, wio;
+       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;
 };
 
+/* drivers/block/ll_rw_blk.c */
+extern void disk_round_stats(struct gendisk *disk);
+
 /* drivers/block/genhd.c */
 extern void add_disk(struct gendisk *disk);
 extern void del_gendisk(struct gendisk *gp);