diff options
-rw-r--r-- | Documentation/ABI/testing/procfs-diskstats | 22 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-block | 28 | ||||
-rw-r--r-- | Documentation/iostats.txt | 15 | ||||
-rw-r--r-- | block/blk-core.c | 61 | ||||
-rw-r--r-- | block/blk-merge.c | 6 | ||||
-rw-r--r-- | block/genhd.c | 28 | ||||
-rw-r--r-- | drivers/block/aoe/aoecmd.c | 12 | ||||
-rw-r--r-- | fs/partitions/check.c | 31 | ||||
-rw-r--r-- | include/linux/blkdev.h | 4 | ||||
-rw-r--r-- | include/linux/genhd.h | 153 |
10 files changed, 316 insertions, 44 deletions
diff --git a/Documentation/ABI/testing/procfs-diskstats b/Documentation/ABI/testing/procfs-diskstats new file mode 100644 index 000000000000..99233902e09e --- /dev/null +++ b/Documentation/ABI/testing/procfs-diskstats | |||
@@ -0,0 +1,22 @@ | |||
1 | What: /proc/diskstats | ||
2 | Date: February 2008 | ||
3 | Contact: Jerome Marchand <jmarchan@redhat.com> | ||
4 | Description: | ||
5 | The /proc/diskstats file displays the I/O statistics | ||
6 | of block devices. Each line contains the following 14 | ||
7 | fields: | ||
8 | 1 - major number | ||
9 | 2 - minor mumber | ||
10 | 3 - device name | ||
11 | 4 - reads completed succesfully | ||
12 | 5 - reads merged | ||
13 | 6 - sectors read | ||
14 | 7 - time spent reading (ms) | ||
15 | 8 - writes completed | ||
16 | 9 - writes merged | ||
17 | 10 - sectors written | ||
18 | 11 - time spent writing (ms) | ||
19 | 12 - I/Os currently in progress | ||
20 | 13 - time spent doing I/Os (ms) | ||
21 | 14 - weighted time spent doing I/Os (ms) | ||
22 | For more details refer to Documentation/iostats.txt | ||
diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block new file mode 100644 index 000000000000..4bd9ea539129 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-block | |||
@@ -0,0 +1,28 @@ | |||
1 | What: /sys/block/<disk>/stat | ||
2 | Date: February 2008 | ||
3 | Contact: Jerome Marchand <jmarchan@redhat.com> | ||
4 | Description: | ||
5 | The /sys/block/<disk>/stat files displays the I/O | ||
6 | statistics of disk <disk>. They contain 11 fields: | ||
7 | 1 - reads completed succesfully | ||
8 | 2 - reads merged | ||
9 | 3 - sectors read | ||
10 | 4 - time spent reading (ms) | ||
11 | 5 - writes completed | ||
12 | 6 - writes merged | ||
13 | 7 - sectors written | ||
14 | 8 - time spent writing (ms) | ||
15 | 9 - I/Os currently in progress | ||
16 | 10 - time spent doing I/Os (ms) | ||
17 | 11 - weighted time spent doing I/Os (ms) | ||
18 | For more details refer Documentation/iostats.txt | ||
19 | |||
20 | |||
21 | What: /sys/block/<disk>/<part>/stat | ||
22 | Date: February 2008 | ||
23 | Contact: Jerome Marchand <jmarchan@redhat.com> | ||
24 | Description: | ||
25 | The /sys/block/<disk>/<part>/stat files display the | ||
26 | I/O statistics of partition <part>. The format is the | ||
27 | same as the above-written /sys/block/<disk>/stat | ||
28 | format. | ||
diff --git a/Documentation/iostats.txt b/Documentation/iostats.txt index b963c3b4afa5..5925c3cd030d 100644 --- a/Documentation/iostats.txt +++ b/Documentation/iostats.txt | |||
@@ -58,7 +58,7 @@ they should not wrap twice before you notice them. | |||
58 | Each set of stats only applies to the indicated device; if you want | 58 | Each set of stats only applies to the indicated device; if you want |
59 | system-wide stats you'll have to find all the devices and sum them all up. | 59 | system-wide stats you'll have to find all the devices and sum them all up. |
60 | 60 | ||
61 | Field 1 -- # of reads issued | 61 | Field 1 -- # of reads completed |
62 | This is the total number of reads completed successfully. | 62 | This is the total number of reads completed successfully. |
63 | Field 2 -- # of reads merged, field 6 -- # of writes merged | 63 | Field 2 -- # of reads merged, field 6 -- # of writes merged |
64 | Reads and writes which are adjacent to each other may be merged for | 64 | Reads and writes which are adjacent to each other may be merged for |
@@ -132,6 +132,19 @@ words, the number of reads for partitions is counted slightly before time | |||
132 | of queuing for partitions, and at completion for whole disks. This is | 132 | of queuing for partitions, and at completion for whole disks. This is |
133 | a subtle distinction that is probably uninteresting for most cases. | 133 | a subtle distinction that is probably uninteresting for most cases. |
134 | 134 | ||
135 | More significant is the error induced by counting the numbers of | ||
136 | reads/writes before merges for partitions and after for disks. Since a | ||
137 | typical workload usually contains a lot of successive and adjacent requests, | ||
138 | the number of reads/writes issued can be several times higher than the | ||
139 | number of reads/writes completed. | ||
140 | |||
141 | In 2.6.25, the full statistic set is again available for partitions and | ||
142 | disk and partition statistics are consistent again. Since we still don't | ||
143 | keep record of the partition-relative address, an operation is attributed to | ||
144 | the partition which contains the first sector of the request after the | ||
145 | eventual merges. As requests can be merged across partition, this could lead | ||
146 | to some (probably insignificant) innacuracy. | ||
147 | |||
135 | Additional notes | 148 | Additional notes |
136 | ---------------- | 149 | ---------------- |
137 | 150 | ||
diff --git a/block/blk-core.c b/block/blk-core.c index 4afb39c82339..e9754dc98ec4 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -60,10 +60,15 @@ static void drive_stat_acct(struct request *rq, int new_io) | |||
60 | return; | 60 | return; |
61 | 61 | ||
62 | if (!new_io) { | 62 | if (!new_io) { |
63 | __disk_stat_inc(rq->rq_disk, merges[rw]); | 63 | __all_stat_inc(rq->rq_disk, merges[rw], rq->sector); |
64 | } else { | 64 | } else { |
65 | struct hd_struct *part = get_part(rq->rq_disk, rq->sector); | ||
65 | disk_round_stats(rq->rq_disk); | 66 | disk_round_stats(rq->rq_disk); |
66 | rq->rq_disk->in_flight++; | 67 | rq->rq_disk->in_flight++; |
68 | if (part) { | ||
69 | part_round_stats(part); | ||
70 | part->in_flight++; | ||
71 | } | ||
67 | } | 72 | } |
68 | } | 73 | } |
69 | 74 | ||
@@ -102,27 +107,38 @@ struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev) | |||
102 | } | 107 | } |
103 | EXPORT_SYMBOL(blk_get_backing_dev_info); | 108 | EXPORT_SYMBOL(blk_get_backing_dev_info); |
104 | 109 | ||
110 | /* | ||
111 | * We can't just memset() the structure, since the allocation path | ||
112 | * already stored some information in the request. | ||
113 | */ | ||
105 | void rq_init(struct request_queue *q, struct request *rq) | 114 | void rq_init(struct request_queue *q, struct request *rq) |
106 | { | 115 | { |
107 | INIT_LIST_HEAD(&rq->queuelist); | 116 | INIT_LIST_HEAD(&rq->queuelist); |
108 | INIT_LIST_HEAD(&rq->donelist); | 117 | INIT_LIST_HEAD(&rq->donelist); |
109 | 118 | rq->q = q; | |
110 | rq->errors = 0; | 119 | rq->sector = rq->hard_sector = (sector_t) -1; |
120 | rq->nr_sectors = rq->hard_nr_sectors = 0; | ||
121 | rq->current_nr_sectors = rq->hard_cur_sectors = 0; | ||
111 | rq->bio = rq->biotail = NULL; | 122 | rq->bio = rq->biotail = NULL; |
112 | INIT_HLIST_NODE(&rq->hash); | 123 | INIT_HLIST_NODE(&rq->hash); |
113 | RB_CLEAR_NODE(&rq->rb_node); | 124 | RB_CLEAR_NODE(&rq->rb_node); |
125 | rq->rq_disk = NULL; | ||
126 | rq->nr_phys_segments = 0; | ||
127 | rq->nr_hw_segments = 0; | ||
114 | rq->ioprio = 0; | 128 | rq->ioprio = 0; |
129 | rq->special = NULL; | ||
115 | rq->buffer = NULL; | 130 | rq->buffer = NULL; |
131 | rq->tag = -1; | ||
132 | rq->errors = 0; | ||
116 | rq->ref_count = 1; | 133 | rq->ref_count = 1; |
117 | rq->q = q; | 134 | rq->cmd_len = 0; |
118 | rq->special = NULL; | 135 | memset(rq->cmd, 0, sizeof(rq->cmd)); |
119 | rq->data_len = 0; | 136 | rq->data_len = 0; |
137 | rq->sense_len = 0; | ||
120 | rq->data = NULL; | 138 | rq->data = NULL; |
121 | rq->nr_phys_segments = 0; | ||
122 | rq->sense = NULL; | 139 | rq->sense = NULL; |
123 | rq->end_io = NULL; | 140 | rq->end_io = NULL; |
124 | rq->end_io_data = NULL; | 141 | rq->end_io_data = NULL; |
125 | rq->completion_data = NULL; | ||
126 | rq->next_rq = NULL; | 142 | rq->next_rq = NULL; |
127 | } | 143 | } |
128 | 144 | ||
@@ -986,6 +1002,21 @@ void disk_round_stats(struct gendisk *disk) | |||
986 | } | 1002 | } |
987 | EXPORT_SYMBOL_GPL(disk_round_stats); | 1003 | EXPORT_SYMBOL_GPL(disk_round_stats); |
988 | 1004 | ||
1005 | void part_round_stats(struct hd_struct *part) | ||
1006 | { | ||
1007 | unsigned long now = jiffies; | ||
1008 | |||
1009 | if (now == part->stamp) | ||
1010 | return; | ||
1011 | |||
1012 | if (part->in_flight) { | ||
1013 | __part_stat_add(part, time_in_queue, | ||
1014 | part->in_flight * (now - part->stamp)); | ||
1015 | __part_stat_add(part, io_ticks, (now - part->stamp)); | ||
1016 | } | ||
1017 | part->stamp = now; | ||
1018 | } | ||
1019 | |||
989 | /* | 1020 | /* |
990 | * queue lock must be held | 1021 | * queue lock must be held |
991 | */ | 1022 | */ |
@@ -1188,10 +1219,6 @@ static inline void blk_partition_remap(struct bio *bio) | |||
1188 | 1219 | ||
1189 | if (bio_sectors(bio) && bdev != bdev->bd_contains) { | 1220 | if (bio_sectors(bio) && bdev != bdev->bd_contains) { |
1190 | struct hd_struct *p = bdev->bd_part; | 1221 | struct hd_struct *p = bdev->bd_part; |
1191 | const int rw = bio_data_dir(bio); | ||
1192 | |||
1193 | p->sectors[rw] += bio_sectors(bio); | ||
1194 | p->ios[rw]++; | ||
1195 | 1222 | ||
1196 | bio->bi_sector += p->start_sect; | 1223 | bio->bi_sector += p->start_sect; |
1197 | bio->bi_bdev = bdev->bd_contains; | 1224 | bio->bi_bdev = bdev->bd_contains; |
@@ -1519,7 +1546,8 @@ static int __end_that_request_first(struct request *req, int error, | |||
1519 | if (blk_fs_request(req) && req->rq_disk) { | 1546 | if (blk_fs_request(req) && req->rq_disk) { |
1520 | const int rw = rq_data_dir(req); | 1547 | const int rw = rq_data_dir(req); |
1521 | 1548 | ||
1522 | disk_stat_add(req->rq_disk, sectors[rw], nr_bytes >> 9); | 1549 | all_stat_add(req->rq_disk, sectors[rw], |
1550 | nr_bytes >> 9, req->sector); | ||
1523 | } | 1551 | } |
1524 | 1552 | ||
1525 | total_bytes = bio_nbytes = 0; | 1553 | total_bytes = bio_nbytes = 0; |
@@ -1704,11 +1732,16 @@ static void end_that_request_last(struct request *req, int error) | |||
1704 | if (disk && blk_fs_request(req) && req != &req->q->bar_rq) { | 1732 | if (disk && blk_fs_request(req) && req != &req->q->bar_rq) { |
1705 | unsigned long duration = jiffies - req->start_time; | 1733 | unsigned long duration = jiffies - req->start_time; |
1706 | const int rw = rq_data_dir(req); | 1734 | const int rw = rq_data_dir(req); |
1735 | struct hd_struct *part = get_part(disk, req->sector); | ||
1707 | 1736 | ||
1708 | __disk_stat_inc(disk, ios[rw]); | 1737 | __all_stat_inc(disk, ios[rw], req->sector); |
1709 | __disk_stat_add(disk, ticks[rw], duration); | 1738 | __all_stat_add(disk, ticks[rw], duration, req->sector); |
1710 | disk_round_stats(disk); | 1739 | disk_round_stats(disk); |
1711 | disk->in_flight--; | 1740 | disk->in_flight--; |
1741 | if (part) { | ||
1742 | part_round_stats(part); | ||
1743 | part->in_flight--; | ||
1744 | } | ||
1712 | } | 1745 | } |
1713 | 1746 | ||
1714 | if (req->end_io) | 1747 | if (req->end_io) |
diff --git a/block/blk-merge.c b/block/blk-merge.c index 845ef8131108..d3b84bbb776a 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c | |||
@@ -454,8 +454,14 @@ static int attempt_merge(struct request_queue *q, struct request *req, | |||
454 | elv_merge_requests(q, req, next); | 454 | elv_merge_requests(q, req, next); |
455 | 455 | ||
456 | if (req->rq_disk) { | 456 | if (req->rq_disk) { |
457 | struct hd_struct *part | ||
458 | = get_part(req->rq_disk, req->sector); | ||
457 | disk_round_stats(req->rq_disk); | 459 | disk_round_stats(req->rq_disk); |
458 | req->rq_disk->in_flight--; | 460 | req->rq_disk->in_flight--; |
461 | if (part) { | ||
462 | part_round_stats(part); | ||
463 | part->in_flight--; | ||
464 | } | ||
459 | } | 465 | } |
460 | 466 | ||
461 | req->ioprio = ioprio_best(req->ioprio, next->ioprio); | 467 | req->ioprio = ioprio_best(req->ioprio, next->ioprio); |
diff --git a/block/genhd.c b/block/genhd.c index de2ebb2fab43..53f2238e69c8 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -584,12 +584,28 @@ static int diskstats_show(struct seq_file *s, void *v) | |||
584 | for (n = 0; n < gp->minors - 1; n++) { | 584 | for (n = 0; n < gp->minors - 1; n++) { |
585 | struct hd_struct *hd = gp->part[n]; | 585 | struct hd_struct *hd = gp->part[n]; |
586 | 586 | ||
587 | if (hd && hd->nr_sects) | 587 | if (!hd || !hd->nr_sects) |
588 | seq_printf(s, "%4d %4d %s %u %u %u %u\n", | 588 | continue; |
589 | gp->major, n + gp->first_minor + 1, | 589 | |
590 | disk_name(gp, n + 1, buf), | 590 | preempt_disable(); |
591 | hd->ios[0], hd->sectors[0], | 591 | part_round_stats(hd); |
592 | hd->ios[1], hd->sectors[1]); | 592 | preempt_enable(); |
593 | seq_printf(s, "%4d %4d %s %lu %lu %llu " | ||
594 | "%u %lu %lu %llu %u %u %u %u\n", | ||
595 | gp->major, n + gp->first_minor + 1, | ||
596 | disk_name(gp, n + 1, buf), | ||
597 | part_stat_read(hd, ios[0]), | ||
598 | part_stat_read(hd, merges[0]), | ||
599 | (unsigned long long)part_stat_read(hd, sectors[0]), | ||
600 | jiffies_to_msecs(part_stat_read(hd, ticks[0])), | ||
601 | part_stat_read(hd, ios[1]), | ||
602 | part_stat_read(hd, merges[1]), | ||
603 | (unsigned long long)part_stat_read(hd, sectors[1]), | ||
604 | jiffies_to_msecs(part_stat_read(hd, ticks[1])), | ||
605 | hd->in_flight, | ||
606 | jiffies_to_msecs(part_stat_read(hd, io_ticks)), | ||
607 | jiffies_to_msecs(part_stat_read(hd, time_in_queue)) | ||
608 | ); | ||
593 | } | 609 | } |
594 | 610 | ||
595 | return 0; | 611 | return 0; |
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 44beb17e8090..d00293ba3b45 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c | |||
@@ -751,15 +751,15 @@ gettgt(struct aoedev *d, char *addr) | |||
751 | } | 751 | } |
752 | 752 | ||
753 | static inline void | 753 | static inline void |
754 | diskstats(struct gendisk *disk, struct bio *bio, ulong duration) | 754 | diskstats(struct gendisk *disk, struct bio *bio, ulong duration, sector_t sector) |
755 | { | 755 | { |
756 | unsigned long n_sect = bio->bi_size >> 9; | 756 | unsigned long n_sect = bio->bi_size >> 9; |
757 | const int rw = bio_data_dir(bio); | 757 | const int rw = bio_data_dir(bio); |
758 | 758 | ||
759 | disk_stat_inc(disk, ios[rw]); | 759 | all_stat_inc(disk, ios[rw], sector); |
760 | disk_stat_add(disk, ticks[rw], duration); | 760 | all_stat_add(disk, ticks[rw], duration, sector); |
761 | disk_stat_add(disk, sectors[rw], n_sect); | 761 | all_stat_add(disk, sectors[rw], n_sect, sector); |
762 | disk_stat_add(disk, io_ticks, duration); | 762 | all_stat_add(disk, io_ticks, duration, sector); |
763 | } | 763 | } |
764 | 764 | ||
765 | void | 765 | void |
@@ -879,7 +879,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) | |||
879 | } | 879 | } |
880 | 880 | ||
881 | if (buf && --buf->nframesout == 0 && buf->resid == 0) { | 881 | if (buf && --buf->nframesout == 0 && buf->resid == 0) { |
882 | diskstats(d->gd, buf->bio, jiffies - buf->stime); | 882 | diskstats(d->gd, buf->bio, jiffies - buf->stime, buf->sector); |
883 | n = (buf->flags & BUFFL_FAIL) ? -EIO : 0; | 883 | n = (buf->flags & BUFFL_FAIL) ? -EIO : 0; |
884 | bio_endio(buf->bio, n); | 884 | bio_endio(buf->bio, n); |
885 | mempool_free(buf, d->bufpool); | 885 | mempool_free(buf, d->bufpool); |
diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 9a64045ff845..03f808c5b79d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <linux/kmod.h> | 19 | #include <linux/kmod.h> |
20 | #include <linux/ctype.h> | 20 | #include <linux/ctype.h> |
21 | #include <linux/genhd.h> | ||
21 | 22 | ||
22 | #include "check.h" | 23 | #include "check.h" |
23 | 24 | ||
@@ -215,9 +216,25 @@ static ssize_t part_stat_show(struct device *dev, | |||
215 | { | 216 | { |
216 | struct hd_struct *p = dev_to_part(dev); | 217 | struct hd_struct *p = dev_to_part(dev); |
217 | 218 | ||
218 | return sprintf(buf, "%8u %8llu %8u %8llu\n", | 219 | preempt_disable(); |
219 | p->ios[0], (unsigned long long)p->sectors[0], | 220 | part_round_stats(p); |
220 | p->ios[1], (unsigned long long)p->sectors[1]); | 221 | preempt_enable(); |
222 | return sprintf(buf, | ||
223 | "%8lu %8lu %8llu %8u " | ||
224 | "%8lu %8lu %8llu %8u " | ||
225 | "%8u %8u %8u" | ||
226 | "\n", | ||
227 | part_stat_read(p, ios[READ]), | ||
228 | part_stat_read(p, merges[READ]), | ||
229 | (unsigned long long)part_stat_read(p, sectors[READ]), | ||
230 | jiffies_to_msecs(part_stat_read(p, ticks[READ])), | ||
231 | part_stat_read(p, ios[WRITE]), | ||
232 | part_stat_read(p, merges[WRITE]), | ||
233 | (unsigned long long)part_stat_read(p, sectors[WRITE]), | ||
234 | jiffies_to_msecs(part_stat_read(p, ticks[WRITE])), | ||
235 | p->in_flight, | ||
236 | jiffies_to_msecs(part_stat_read(p, io_ticks)), | ||
237 | jiffies_to_msecs(part_stat_read(p, time_in_queue))); | ||
221 | } | 238 | } |
222 | 239 | ||
223 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 240 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
@@ -273,6 +290,7 @@ static struct attribute_group *part_attr_groups[] = { | |||
273 | static void part_release(struct device *dev) | 290 | static void part_release(struct device *dev) |
274 | { | 291 | { |
275 | struct hd_struct *p = dev_to_part(dev); | 292 | struct hd_struct *p = dev_to_part(dev); |
293 | free_part_stats(p); | ||
276 | kfree(p); | 294 | kfree(p); |
277 | } | 295 | } |
278 | 296 | ||
@@ -312,8 +330,7 @@ void delete_partition(struct gendisk *disk, int part) | |||
312 | disk->part[part-1] = NULL; | 330 | disk->part[part-1] = NULL; |
313 | p->start_sect = 0; | 331 | p->start_sect = 0; |
314 | p->nr_sects = 0; | 332 | p->nr_sects = 0; |
315 | p->ios[0] = p->ios[1] = 0; | 333 | part_stat_set_all(p, 0); |
316 | p->sectors[0] = p->sectors[1] = 0; | ||
317 | kobject_put(p->holder_dir); | 334 | kobject_put(p->holder_dir); |
318 | device_del(&p->dev); | 335 | device_del(&p->dev); |
319 | put_device(&p->dev); | 336 | put_device(&p->dev); |
@@ -336,6 +353,10 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, | |||
336 | if (!p) | 353 | if (!p) |
337 | return; | 354 | return; |
338 | 355 | ||
356 | if (!init_part_stats(p)) { | ||
357 | kfree(p); | ||
358 | return; | ||
359 | } | ||
339 | p->start_sect = start; | 360 | p->start_sect = start; |
340 | p->nr_sects = len; | 361 | p->nr_sects = len; |
341 | p->partno = part; | 362 | p->partno = part; |
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 90392a9d7a9c..e1888cc5b8ae 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h | |||
@@ -137,7 +137,9 @@ enum rq_flag_bits { | |||
137 | #define BLK_MAX_CDB 16 | 137 | #define BLK_MAX_CDB 16 |
138 | 138 | ||
139 | /* | 139 | /* |
140 | * try to put the fields that are referenced together in the same cacheline | 140 | * try to put the fields that are referenced together in the same cacheline. |
141 | * if you modify this structure, be sure to check block/blk-core.c:rq_init() | ||
142 | * as well! | ||
141 | */ | 143 | */ |
142 | struct request { | 144 | struct request { |
143 | struct list_head queuelist; | 145 | struct list_head queuelist; |
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 1dbea0ac5693..09a3b18918c7 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h | |||
@@ -91,16 +91,31 @@ struct partition { | |||
91 | __le32 nr_sects; /* nr of sectors in partition */ | 91 | __le32 nr_sects; /* nr of sectors in partition */ |
92 | } __attribute__((packed)); | 92 | } __attribute__((packed)); |
93 | 93 | ||
94 | struct disk_stats { | ||
95 | unsigned long sectors[2]; /* READs and WRITEs */ | ||
96 | unsigned long ios[2]; | ||
97 | unsigned long merges[2]; | ||
98 | unsigned long ticks[2]; | ||
99 | unsigned long io_ticks; | ||
100 | unsigned long time_in_queue; | ||
101 | }; | ||
102 | |||
94 | struct hd_struct { | 103 | struct hd_struct { |
95 | sector_t start_sect; | 104 | sector_t start_sect; |
96 | sector_t nr_sects; | 105 | sector_t nr_sects; |
97 | struct device dev; | 106 | struct device dev; |
98 | struct kobject *holder_dir; | 107 | struct kobject *holder_dir; |
99 | unsigned ios[2], sectors[2]; /* READs and WRITEs */ | ||
100 | int policy, partno; | 108 | int policy, partno; |
101 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 109 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
102 | int make_it_fail; | 110 | int make_it_fail; |
103 | #endif | 111 | #endif |
112 | unsigned long stamp; | ||
113 | int in_flight; | ||
114 | #ifdef CONFIG_SMP | ||
115 | struct disk_stats *dkstats; | ||
116 | #else | ||
117 | struct disk_stats dkstats; | ||
118 | #endif | ||
104 | }; | 119 | }; |
105 | 120 | ||
106 | #define GENHD_FL_REMOVABLE 1 | 121 | #define GENHD_FL_REMOVABLE 1 |
@@ -111,15 +126,7 @@ struct hd_struct { | |||
111 | #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 | 126 | #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 |
112 | #define GENHD_FL_FAIL 64 | 127 | #define GENHD_FL_FAIL 64 |
113 | 128 | ||
114 | struct disk_stats { | 129 | |
115 | unsigned long sectors[2]; /* READs and WRITEs */ | ||
116 | unsigned long ios[2]; | ||
117 | unsigned long merges[2]; | ||
118 | unsigned long ticks[2]; | ||
119 | unsigned long io_ticks; | ||
120 | unsigned long time_in_queue; | ||
121 | }; | ||
122 | |||
123 | struct gendisk { | 130 | struct gendisk { |
124 | int major; /* major number of driver */ | 131 | int major; /* major number of driver */ |
125 | int first_minor; | 132 | int first_minor; |
@@ -158,6 +165,20 @@ struct gendisk { | |||
158 | * The __ variants should only be called in critical sections. The full | 165 | * The __ variants should only be called in critical sections. The full |
159 | * variants disable/enable preemption. | 166 | * variants disable/enable preemption. |
160 | */ | 167 | */ |
168 | static inline struct hd_struct *get_part(struct gendisk *gendiskp, | ||
169 | sector_t sector) | ||
170 | { | ||
171 | struct hd_struct *part; | ||
172 | int i; | ||
173 | for (i = 0; i < gendiskp->minors - 1; i++) { | ||
174 | part = gendiskp->part[i]; | ||
175 | if (part && part->start_sect <= sector | ||
176 | && sector < part->start_sect + part->nr_sects) | ||
177 | return part; | ||
178 | } | ||
179 | return NULL; | ||
180 | } | ||
181 | |||
161 | #ifdef CONFIG_SMP | 182 | #ifdef CONFIG_SMP |
162 | #define __disk_stat_add(gendiskp, field, addnd) \ | 183 | #define __disk_stat_add(gendiskp, field, addnd) \ |
163 | (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) | 184 | (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) |
@@ -177,15 +198,62 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { | |||
177 | memset(per_cpu_ptr(gendiskp->dkstats, i), value, | 198 | memset(per_cpu_ptr(gendiskp->dkstats, i), value, |
178 | sizeof (struct disk_stats)); | 199 | sizeof (struct disk_stats)); |
179 | } | 200 | } |
201 | |||
202 | #define __part_stat_add(part, field, addnd) \ | ||
203 | (per_cpu_ptr(part->dkstats, smp_processor_id())->field += addnd) | ||
204 | |||
205 | #define __all_stat_add(gendiskp, field, addnd, sector) \ | ||
206 | ({ \ | ||
207 | struct hd_struct *part = get_part(gendiskp, sector); \ | ||
208 | if (part) \ | ||
209 | __part_stat_add(part, field, addnd); \ | ||
210 | __disk_stat_add(gendiskp, field, addnd); \ | ||
211 | }) | ||
212 | |||
213 | #define part_stat_read(part, field) \ | ||
214 | ({ \ | ||
215 | typeof(part->dkstats->field) res = 0; \ | ||
216 | int i; \ | ||
217 | for_each_possible_cpu(i) \ | ||
218 | res += per_cpu_ptr(part->dkstats, i)->field; \ | ||
219 | res; \ | ||
220 | }) | ||
221 | |||
222 | static inline void part_stat_set_all(struct hd_struct *part, int value) { | ||
223 | int i; | ||
224 | for_each_possible_cpu(i) | ||
225 | memset(per_cpu_ptr(part->dkstats, i), value, | ||
226 | sizeof(struct disk_stats)); | ||
227 | } | ||
180 | 228 | ||
181 | #else | 229 | #else |
182 | #define __disk_stat_add(gendiskp, field, addnd) \ | 230 | #define __disk_stat_add(gendiskp, field, addnd) \ |
183 | (gendiskp->dkstats.field += addnd) | 231 | (gendiskp->dkstats.field += addnd) |
184 | #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) | 232 | #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) |
185 | 233 | ||
186 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { | 234 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) |
235 | { | ||
187 | memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); | 236 | memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); |
188 | } | 237 | } |
238 | |||
239 | #define __part_stat_add(part, field, addnd) \ | ||
240 | (part->dkstats.field += addnd) | ||
241 | |||
242 | #define __all_stat_add(gendiskp, field, addnd, sector) \ | ||
243 | ({ \ | ||
244 | struct hd_struct *part = get_part(gendiskp, sector); \ | ||
245 | if (part) \ | ||
246 | part->dkstats.field += addnd; \ | ||
247 | __disk_stat_add(gendiskp, field, addnd); \ | ||
248 | }) | ||
249 | |||
250 | #define part_stat_read(part, field) (part->dkstats.field) | ||
251 | |||
252 | static inline void part_stat_set_all(struct hd_struct *part, int value) | ||
253 | { | ||
254 | memset(&part->dkstats, value, sizeof(struct disk_stats)); | ||
255 | } | ||
256 | |||
189 | #endif | 257 | #endif |
190 | 258 | ||
191 | #define disk_stat_add(gendiskp, field, addnd) \ | 259 | #define disk_stat_add(gendiskp, field, addnd) \ |
@@ -206,6 +274,45 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { | |||
206 | #define disk_stat_sub(gendiskp, field, subnd) \ | 274 | #define disk_stat_sub(gendiskp, field, subnd) \ |
207 | disk_stat_add(gendiskp, field, -subnd) | 275 | disk_stat_add(gendiskp, field, -subnd) |
208 | 276 | ||
277 | #define part_stat_add(gendiskp, field, addnd) \ | ||
278 | do { \ | ||
279 | preempt_disable(); \ | ||
280 | __part_stat_add(gendiskp, field, addnd);\ | ||
281 | preempt_enable(); \ | ||
282 | } while (0) | ||
283 | |||
284 | #define __part_stat_dec(gendiskp, field) __part_stat_add(gendiskp, field, -1) | ||
285 | #define part_stat_dec(gendiskp, field) part_stat_add(gendiskp, field, -1) | ||
286 | |||
287 | #define __part_stat_inc(gendiskp, field) __part_stat_add(gendiskp, field, 1) | ||
288 | #define part_stat_inc(gendiskp, field) part_stat_add(gendiskp, field, 1) | ||
289 | |||
290 | #define __part_stat_sub(gendiskp, field, subnd) \ | ||
291 | __part_stat_add(gendiskp, field, -subnd) | ||
292 | #define part_stat_sub(gendiskp, field, subnd) \ | ||
293 | part_stat_add(gendiskp, field, -subnd) | ||
294 | |||
295 | #define all_stat_add(gendiskp, field, addnd, sector) \ | ||
296 | do { \ | ||
297 | preempt_disable(); \ | ||
298 | __all_stat_add(gendiskp, field, addnd, sector); \ | ||
299 | preempt_enable(); \ | ||
300 | } while (0) | ||
301 | |||
302 | #define __all_stat_dec(gendiskp, field, sector) \ | ||
303 | __all_stat_add(gendiskp, field, -1, sector) | ||
304 | #define all_stat_dec(gendiskp, field, sector) \ | ||
305 | all_stat_add(gendiskp, field, -1, sector) | ||
306 | |||
307 | #define __all_stat_inc(gendiskp, field, sector) \ | ||
308 | __all_stat_add(gendiskp, field, 1, sector) | ||
309 | #define all_stat_inc(gendiskp, field, sector) \ | ||
310 | all_stat_add(gendiskp, field, 1, sector) | ||
311 | |||
312 | #define __all_stat_sub(gendiskp, field, subnd, sector) \ | ||
313 | __all_stat_add(gendiskp, field, -subnd, sector) | ||
314 | #define all_stat_sub(gendiskp, field, subnd, sector) \ | ||
315 | all_stat_add(gendiskp, field, -subnd, sector) | ||
209 | 316 | ||
210 | /* Inlines to alloc and free disk stats in struct gendisk */ | 317 | /* Inlines to alloc and free disk stats in struct gendisk */ |
211 | #ifdef CONFIG_SMP | 318 | #ifdef CONFIG_SMP |
@@ -221,6 +328,20 @@ static inline void free_disk_stats(struct gendisk *disk) | |||
221 | { | 328 | { |
222 | free_percpu(disk->dkstats); | 329 | free_percpu(disk->dkstats); |
223 | } | 330 | } |
331 | |||
332 | static inline int init_part_stats(struct hd_struct *part) | ||
333 | { | ||
334 | part->dkstats = alloc_percpu(struct disk_stats); | ||
335 | if (!part->dkstats) | ||
336 | return 0; | ||
337 | return 1; | ||
338 | } | ||
339 | |||
340 | static inline void free_part_stats(struct hd_struct *part) | ||
341 | { | ||
342 | free_percpu(part->dkstats); | ||
343 | } | ||
344 | |||
224 | #else /* CONFIG_SMP */ | 345 | #else /* CONFIG_SMP */ |
225 | static inline int init_disk_stats(struct gendisk *disk) | 346 | static inline int init_disk_stats(struct gendisk *disk) |
226 | { | 347 | { |
@@ -230,10 +351,20 @@ static inline int init_disk_stats(struct gendisk *disk) | |||
230 | static inline void free_disk_stats(struct gendisk *disk) | 351 | static inline void free_disk_stats(struct gendisk *disk) |
231 | { | 352 | { |
232 | } | 353 | } |
354 | |||
355 | static inline int init_part_stats(struct hd_struct *part) | ||
356 | { | ||
357 | return 1; | ||
358 | } | ||
359 | |||
360 | static inline void free_part_stats(struct hd_struct *part) | ||
361 | { | ||
362 | } | ||
233 | #endif /* CONFIG_SMP */ | 363 | #endif /* CONFIG_SMP */ |
234 | 364 | ||
235 | /* drivers/block/ll_rw_blk.c */ | 365 | /* drivers/block/ll_rw_blk.c */ |
236 | extern void disk_round_stats(struct gendisk *disk); | 366 | extern void disk_round_stats(struct gendisk *disk); |
367 | extern void part_round_stats(struct hd_struct *part); | ||
237 | 368 | ||
238 | /* drivers/block/genhd.c */ | 369 | /* drivers/block/genhd.c */ |
239 | extern int get_blkdev_list(char *, int); | 370 | extern int get_blkdev_list(char *, int); |