diff options
author | Divyesh Shah <dpshah@google.com> | 2010-04-09 00:14:23 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2010-04-09 02:36:07 -0400 |
commit | 812d402648f4fc1ab1091b2172a46fc1b367c724 (patch) | |
tree | 9dc52d5bfdbc170559169a0157ed3295d551e9cf | |
parent | 84c124da9ff50bd71fab9c939ee5b7cd8bef2bd9 (diff) |
blkio: Add io_merged stat
This includes both the number of bios merged into requests belonging to this
cgroup as well as the number of requests merged together.
In the past, we've observed different merging behavior across upstream kernels,
some by design some actual bugs. This stat helps a lot in debugging such
problems when applications report decreased throughput with a new kernel
version.
This needed adding an extra elevator function to capture bios being merged as I
did not want to pollute elevator code with blkiocg knowledge and hence needed
the accounting invocation to come from CFQ.
Signed-off-by: Divyesh Shah<dpshah@google.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | Documentation/cgroups/blkio-controller.txt | 5 | ||||
-rw-r--r-- | block/blk-cgroup.c | 17 | ||||
-rw-r--r-- | block/blk-cgroup.h | 8 | ||||
-rw-r--r-- | block/blk-core.c | 2 | ||||
-rw-r--r-- | block/cfq-iosched.c | 11 | ||||
-rw-r--r-- | block/elevator.c | 9 | ||||
-rw-r--r-- | include/linux/elevator.h | 6 |
7 files changed, 57 insertions, 1 deletions
diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroups/blkio-controller.txt index ed04fe9cce1a..810e30171a54 100644 --- a/Documentation/cgroups/blkio-controller.txt +++ b/Documentation/cgroups/blkio-controller.txt | |||
@@ -134,6 +134,11 @@ Details of cgroup files | |||
134 | minor number of the device, third field specifies the operation type | 134 | minor number of the device, third field specifies the operation type |
135 | and the fourth field specifies the io_wait_time in ns. | 135 | and the fourth field specifies the io_wait_time in ns. |
136 | 136 | ||
137 | - blkio.io_merged | ||
138 | - Total number of bios/requests merged into requests belonging to this | ||
139 | cgroup. This is further divided by the type of operation - read or | ||
140 | write, sync or async. | ||
141 | |||
137 | - blkio.dequeue | 142 | - blkio.dequeue |
138 | - Debugging aid only enabled if CONFIG_DEBUG_CFQ_IOSCHED=y. This | 143 | - Debugging aid only enabled if CONFIG_DEBUG_CFQ_IOSCHED=y. This |
139 | gives the statistics about how many a times a group was dequeued | 144 | gives the statistics about how many a times a group was dequeued |
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 6797df508821..d23b538858ce 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c | |||
@@ -127,6 +127,18 @@ void blkiocg_update_completion_stats(struct blkio_group *blkg, | |||
127 | } | 127 | } |
128 | EXPORT_SYMBOL_GPL(blkiocg_update_completion_stats); | 128 | EXPORT_SYMBOL_GPL(blkiocg_update_completion_stats); |
129 | 129 | ||
130 | void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction, | ||
131 | bool sync) | ||
132 | { | ||
133 | unsigned long flags; | ||
134 | |||
135 | spin_lock_irqsave(&blkg->stats_lock, flags); | ||
136 | blkio_add_stat(blkg->stats.stat_arr[BLKIO_STAT_MERGED], 1, direction, | ||
137 | sync); | ||
138 | spin_unlock_irqrestore(&blkg->stats_lock, flags); | ||
139 | } | ||
140 | EXPORT_SYMBOL_GPL(blkiocg_update_io_merged_stats); | ||
141 | |||
130 | void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, | 142 | void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, |
131 | struct blkio_group *blkg, void *key, dev_t dev) | 143 | struct blkio_group *blkg, void *key, dev_t dev) |
132 | { | 144 | { |
@@ -363,6 +375,7 @@ SHOW_FUNCTION_PER_GROUP(io_service_bytes, BLKIO_STAT_SERVICE_BYTES, 1); | |||
363 | SHOW_FUNCTION_PER_GROUP(io_serviced, BLKIO_STAT_SERVICED, 1); | 375 | SHOW_FUNCTION_PER_GROUP(io_serviced, BLKIO_STAT_SERVICED, 1); |
364 | SHOW_FUNCTION_PER_GROUP(io_service_time, BLKIO_STAT_SERVICE_TIME, 1); | 376 | SHOW_FUNCTION_PER_GROUP(io_service_time, BLKIO_STAT_SERVICE_TIME, 1); |
365 | SHOW_FUNCTION_PER_GROUP(io_wait_time, BLKIO_STAT_WAIT_TIME, 1); | 377 | SHOW_FUNCTION_PER_GROUP(io_wait_time, BLKIO_STAT_WAIT_TIME, 1); |
378 | SHOW_FUNCTION_PER_GROUP(io_merged, BLKIO_STAT_MERGED, 1); | ||
366 | #ifdef CONFIG_DEBUG_BLK_CGROUP | 379 | #ifdef CONFIG_DEBUG_BLK_CGROUP |
367 | SHOW_FUNCTION_PER_GROUP(dequeue, BLKIO_STAT_DEQUEUE, 0); | 380 | SHOW_FUNCTION_PER_GROUP(dequeue, BLKIO_STAT_DEQUEUE, 0); |
368 | #endif | 381 | #endif |
@@ -408,6 +421,10 @@ struct cftype blkio_files[] = { | |||
408 | .read_map = blkiocg_io_wait_time_read, | 421 | .read_map = blkiocg_io_wait_time_read, |
409 | }, | 422 | }, |
410 | { | 423 | { |
424 | .name = "io_merged", | ||
425 | .read_map = blkiocg_io_merged_read, | ||
426 | }, | ||
427 | { | ||
411 | .name = "reset_stats", | 428 | .name = "reset_stats", |
412 | .write_u64 = blkiocg_reset_stats, | 429 | .write_u64 = blkiocg_reset_stats, |
413 | }, | 430 | }, |
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index b22e55390a4f..470a29db6bec 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h | |||
@@ -34,6 +34,8 @@ enum stat_type { | |||
34 | BLKIO_STAT_SERVICED, | 34 | BLKIO_STAT_SERVICED, |
35 | /* Total time spent waiting in scheduler queue in ns */ | 35 | /* Total time spent waiting in scheduler queue in ns */ |
36 | BLKIO_STAT_WAIT_TIME, | 36 | BLKIO_STAT_WAIT_TIME, |
37 | /* Number of IOs merged */ | ||
38 | BLKIO_STAT_MERGED, | ||
37 | /* All the single valued stats go below this */ | 39 | /* All the single valued stats go below this */ |
38 | BLKIO_STAT_TIME, | 40 | BLKIO_STAT_TIME, |
39 | BLKIO_STAT_SECTORS, | 41 | BLKIO_STAT_SECTORS, |
@@ -61,7 +63,7 @@ struct blkio_group_stats { | |||
61 | /* total disk time and nr sectors dispatched by this group */ | 63 | /* total disk time and nr sectors dispatched by this group */ |
62 | uint64_t time; | 64 | uint64_t time; |
63 | uint64_t sectors; | 65 | uint64_t sectors; |
64 | uint64_t stat_arr[BLKIO_STAT_WAIT_TIME + 1][BLKIO_STAT_TOTAL]; | 66 | uint64_t stat_arr[BLKIO_STAT_MERGED + 1][BLKIO_STAT_TOTAL]; |
65 | #ifdef CONFIG_DEBUG_BLK_CGROUP | 67 | #ifdef CONFIG_DEBUG_BLK_CGROUP |
66 | /* How many times this group has been removed from service tree */ | 68 | /* How many times this group has been removed from service tree */ |
67 | unsigned long dequeue; | 69 | unsigned long dequeue; |
@@ -148,6 +150,8 @@ void blkiocg_update_dispatch_stats(struct blkio_group *blkg, uint64_t bytes, | |||
148 | bool direction, bool sync); | 150 | bool direction, bool sync); |
149 | void blkiocg_update_completion_stats(struct blkio_group *blkg, | 151 | void blkiocg_update_completion_stats(struct blkio_group *blkg, |
150 | uint64_t start_time, uint64_t io_start_time, bool direction, bool sync); | 152 | uint64_t start_time, uint64_t io_start_time, bool direction, bool sync); |
153 | void blkiocg_update_io_merged_stats(struct blkio_group *blkg, bool direction, | ||
154 | bool sync); | ||
151 | #else | 155 | #else |
152 | struct cgroup; | 156 | struct cgroup; |
153 | static inline struct blkio_cgroup * | 157 | static inline struct blkio_cgroup * |
@@ -169,5 +173,7 @@ static inline void blkiocg_update_dispatch_stats(struct blkio_group *blkg, | |||
169 | static inline void blkiocg_update_completion_stats(struct blkio_group *blkg, | 173 | static inline void blkiocg_update_completion_stats(struct blkio_group *blkg, |
170 | uint64_t start_time, uint64_t io_start_time, bool direction, | 174 | uint64_t start_time, uint64_t io_start_time, bool direction, |
171 | bool sync) {} | 175 | bool sync) {} |
176 | static inline void blkiocg_update_io_merged_stats(struct blkio_group *blkg, | ||
177 | bool direction, bool sync) {} | ||
172 | #endif | 178 | #endif |
173 | #endif /* _BLK_CGROUP_H */ | 179 | #endif /* _BLK_CGROUP_H */ |
diff --git a/block/blk-core.c b/block/blk-core.c index 4b1b29ef2cb0..e9a5ae25db8c 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -1202,6 +1202,7 @@ static int __make_request(struct request_queue *q, struct bio *bio) | |||
1202 | if (!blk_rq_cpu_valid(req)) | 1202 | if (!blk_rq_cpu_valid(req)) |
1203 | req->cpu = bio->bi_comp_cpu; | 1203 | req->cpu = bio->bi_comp_cpu; |
1204 | drive_stat_acct(req, 0); | 1204 | drive_stat_acct(req, 0); |
1205 | elv_bio_merged(q, req, bio); | ||
1205 | if (!attempt_back_merge(q, req)) | 1206 | if (!attempt_back_merge(q, req)) |
1206 | elv_merged_request(q, req, el_ret); | 1207 | elv_merged_request(q, req, el_ret); |
1207 | goto out; | 1208 | goto out; |
@@ -1235,6 +1236,7 @@ static int __make_request(struct request_queue *q, struct bio *bio) | |||
1235 | if (!blk_rq_cpu_valid(req)) | 1236 | if (!blk_rq_cpu_valid(req)) |
1236 | req->cpu = bio->bi_comp_cpu; | 1237 | req->cpu = bio->bi_comp_cpu; |
1237 | drive_stat_acct(req, 0); | 1238 | drive_stat_acct(req, 0); |
1239 | elv_bio_merged(q, req, bio); | ||
1238 | if (!attempt_front_merge(q, req)) | 1240 | if (!attempt_front_merge(q, req)) |
1239 | elv_merged_request(q, req, el_ret); | 1241 | elv_merged_request(q, req, el_ret); |
1240 | goto out; | 1242 | goto out; |
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 5617ae030b15..4eb1906cf6c6 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
@@ -1467,6 +1467,14 @@ static void cfq_merged_request(struct request_queue *q, struct request *req, | |||
1467 | } | 1467 | } |
1468 | } | 1468 | } |
1469 | 1469 | ||
1470 | static void cfq_bio_merged(struct request_queue *q, struct request *req, | ||
1471 | struct bio *bio) | ||
1472 | { | ||
1473 | struct cfq_queue *cfqq = RQ_CFQQ(req); | ||
1474 | blkiocg_update_io_merged_stats(&cfqq->cfqg->blkg, bio_data_dir(bio), | ||
1475 | cfq_bio_sync(bio)); | ||
1476 | } | ||
1477 | |||
1470 | static void | 1478 | static void |
1471 | cfq_merged_requests(struct request_queue *q, struct request *rq, | 1479 | cfq_merged_requests(struct request_queue *q, struct request *rq, |
1472 | struct request *next) | 1480 | struct request *next) |
@@ -1484,6 +1492,8 @@ cfq_merged_requests(struct request_queue *q, struct request *rq, | |||
1484 | if (cfqq->next_rq == next) | 1492 | if (cfqq->next_rq == next) |
1485 | cfqq->next_rq = rq; | 1493 | cfqq->next_rq = rq; |
1486 | cfq_remove_request(next); | 1494 | cfq_remove_request(next); |
1495 | blkiocg_update_io_merged_stats(&cfqq->cfqg->blkg, rq_data_dir(next), | ||
1496 | rq_is_sync(next)); | ||
1487 | } | 1497 | } |
1488 | 1498 | ||
1489 | static int cfq_allow_merge(struct request_queue *q, struct request *rq, | 1499 | static int cfq_allow_merge(struct request_queue *q, struct request *rq, |
@@ -3861,6 +3871,7 @@ static struct elevator_type iosched_cfq = { | |||
3861 | .elevator_merged_fn = cfq_merged_request, | 3871 | .elevator_merged_fn = cfq_merged_request, |
3862 | .elevator_merge_req_fn = cfq_merged_requests, | 3872 | .elevator_merge_req_fn = cfq_merged_requests, |
3863 | .elevator_allow_merge_fn = cfq_allow_merge, | 3873 | .elevator_allow_merge_fn = cfq_allow_merge, |
3874 | .elevator_bio_merged_fn = cfq_bio_merged, | ||
3864 | .elevator_dispatch_fn = cfq_dispatch_requests, | 3875 | .elevator_dispatch_fn = cfq_dispatch_requests, |
3865 | .elevator_add_req_fn = cfq_insert_request, | 3876 | .elevator_add_req_fn = cfq_insert_request, |
3866 | .elevator_activate_req_fn = cfq_activate_request, | 3877 | .elevator_activate_req_fn = cfq_activate_request, |
diff --git a/block/elevator.c b/block/elevator.c index 76e3702d5381..5e734592bb40 100644 --- a/block/elevator.c +++ b/block/elevator.c | |||
@@ -539,6 +539,15 @@ void elv_merge_requests(struct request_queue *q, struct request *rq, | |||
539 | q->last_merge = rq; | 539 | q->last_merge = rq; |
540 | } | 540 | } |
541 | 541 | ||
542 | void elv_bio_merged(struct request_queue *q, struct request *rq, | ||
543 | struct bio *bio) | ||
544 | { | ||
545 | struct elevator_queue *e = q->elevator; | ||
546 | |||
547 | if (e->ops->elevator_bio_merged_fn) | ||
548 | e->ops->elevator_bio_merged_fn(q, rq, bio); | ||
549 | } | ||
550 | |||
542 | void elv_requeue_request(struct request_queue *q, struct request *rq) | 551 | void elv_requeue_request(struct request_queue *q, struct request *rq) |
543 | { | 552 | { |
544 | /* | 553 | /* |
diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 1cb3372e65d8..2c958f4fce1e 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h | |||
@@ -14,6 +14,9 @@ typedef void (elevator_merged_fn) (struct request_queue *, struct request *, int | |||
14 | 14 | ||
15 | typedef int (elevator_allow_merge_fn) (struct request_queue *, struct request *, struct bio *); | 15 | typedef int (elevator_allow_merge_fn) (struct request_queue *, struct request *, struct bio *); |
16 | 16 | ||
17 | typedef void (elevator_bio_merged_fn) (struct request_queue *, | ||
18 | struct request *, struct bio *); | ||
19 | |||
17 | typedef int (elevator_dispatch_fn) (struct request_queue *, int); | 20 | typedef int (elevator_dispatch_fn) (struct request_queue *, int); |
18 | 21 | ||
19 | typedef void (elevator_add_req_fn) (struct request_queue *, struct request *); | 22 | typedef void (elevator_add_req_fn) (struct request_queue *, struct request *); |
@@ -36,6 +39,7 @@ struct elevator_ops | |||
36 | elevator_merged_fn *elevator_merged_fn; | 39 | elevator_merged_fn *elevator_merged_fn; |
37 | elevator_merge_req_fn *elevator_merge_req_fn; | 40 | elevator_merge_req_fn *elevator_merge_req_fn; |
38 | elevator_allow_merge_fn *elevator_allow_merge_fn; | 41 | elevator_allow_merge_fn *elevator_allow_merge_fn; |
42 | elevator_bio_merged_fn *elevator_bio_merged_fn; | ||
39 | 43 | ||
40 | elevator_dispatch_fn *elevator_dispatch_fn; | 44 | elevator_dispatch_fn *elevator_dispatch_fn; |
41 | elevator_add_req_fn *elevator_add_req_fn; | 45 | elevator_add_req_fn *elevator_add_req_fn; |
@@ -103,6 +107,8 @@ extern int elv_merge(struct request_queue *, struct request **, struct bio *); | |||
103 | extern void elv_merge_requests(struct request_queue *, struct request *, | 107 | extern void elv_merge_requests(struct request_queue *, struct request *, |
104 | struct request *); | 108 | struct request *); |
105 | extern void elv_merged_request(struct request_queue *, struct request *, int); | 109 | extern void elv_merged_request(struct request_queue *, struct request *, int); |
110 | extern void elv_bio_merged(struct request_queue *q, struct request *, | ||
111 | struct bio *); | ||
106 | extern void elv_requeue_request(struct request_queue *, struct request *); | 112 | extern void elv_requeue_request(struct request_queue *, struct request *); |
107 | extern int elv_queue_empty(struct request_queue *); | 113 | extern int elv_queue_empty(struct request_queue *); |
108 | extern struct request *elv_former_request(struct request_queue *, struct request *); | 114 | extern struct request *elv_former_request(struct request_queue *, struct request *); |