Commit 5ddfdaad authored by Alexander Viro's avatar Alexander Viro Committed by James Bottomley

[PATCH] IO counters - per-disk part

parent 2103a00b
......@@ -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 @@ static int __make_request(request_queue_t *q, struct bio *bio)
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);
}
......
......@@ -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;
......
......@@ -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");
......
......@@ -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
......
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment