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;
*/
__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)
{
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);
}
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)
*/
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);
}
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;
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 },
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
.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)
{
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)
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");
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
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);