diff options
author | Divyesh Shah <dpshah@google.com> | 2010-04-01 18:01:24 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2010-04-02 02:44:36 -0400 |
commit | 303a3acb2362f16c7e7f4c53b40c2f4b396dc8d5 (patch) | |
tree | 7996f28d5659fef44594f7049e30264b16baa62d | |
parent | 9a0785b0da561e1e9c6617df85e93ae107a42f18 (diff) |
blkio: Add io controller stats like
- io_service_time
- io_wait_time
- io_serviced
- io_service_bytes
These stats are accumulated per operation type helping us to distinguish between
read and write, and sync and async IO. This patch does not increment any of
these stats.
Signed-off-by: Divyesh Shah<dpshah@google.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
-rw-r--r-- | block/blk-cgroup.c | 178 | ||||
-rw-r--r-- | block/blk-cgroup.h | 39 | ||||
-rw-r--r-- | block/cfq-iosched.c | 2 |
3 files changed, 194 insertions, 25 deletions
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 5be39813fc9b..ad6843f2e0ab 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c | |||
@@ -55,12 +55,15 @@ struct blkio_cgroup *cgroup_to_blkio_cgroup(struct cgroup *cgroup) | |||
55 | } | 55 | } |
56 | EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup); | 56 | EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup); |
57 | 57 | ||
58 | void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, | 58 | void blkiocg_update_timeslice_used(struct blkio_group *blkg, unsigned long time) |
59 | unsigned long time) | ||
60 | { | 59 | { |
61 | blkg->time += time; | 60 | unsigned long flags; |
61 | |||
62 | spin_lock_irqsave(&blkg->stats_lock, flags); | ||
63 | blkg->stats.time += time; | ||
64 | spin_unlock_irqrestore(&blkg->stats_lock, flags); | ||
62 | } | 65 | } |
63 | EXPORT_SYMBOL_GPL(blkiocg_update_blkio_group_stats); | 66 | EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used); |
64 | 67 | ||
65 | void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, | 68 | void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, |
66 | struct blkio_group *blkg, void *key, dev_t dev) | 69 | struct blkio_group *blkg, void *key, dev_t dev) |
@@ -170,13 +173,121 @@ blkiocg_weight_write(struct cgroup *cgroup, struct cftype *cftype, u64 val) | |||
170 | return 0; | 173 | return 0; |
171 | } | 174 | } |
172 | 175 | ||
173 | #define SHOW_FUNCTION_PER_GROUP(__VAR) \ | 176 | static int |
177 | blkiocg_reset_write(struct cgroup *cgroup, struct cftype *cftype, u64 val) | ||
178 | { | ||
179 | struct blkio_cgroup *blkcg; | ||
180 | struct blkio_group *blkg; | ||
181 | struct hlist_node *n; | ||
182 | struct blkio_group_stats *stats; | ||
183 | |||
184 | blkcg = cgroup_to_blkio_cgroup(cgroup); | ||
185 | spin_lock_irq(&blkcg->lock); | ||
186 | hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) { | ||
187 | spin_lock(&blkg->stats_lock); | ||
188 | stats = &blkg->stats; | ||
189 | memset(stats, 0, sizeof(struct blkio_group_stats)); | ||
190 | spin_unlock(&blkg->stats_lock); | ||
191 | } | ||
192 | spin_unlock_irq(&blkcg->lock); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | void get_key_name(int type, char *disk_id, char *str, int chars_left) | ||
197 | { | ||
198 | strlcpy(str, disk_id, chars_left); | ||
199 | chars_left -= strlen(str); | ||
200 | if (chars_left <= 0) { | ||
201 | printk(KERN_WARNING | ||
202 | "Possibly incorrect cgroup stat display format"); | ||
203 | return; | ||
204 | } | ||
205 | switch (type) { | ||
206 | case IO_READ: | ||
207 | strlcat(str, " Read", chars_left); | ||
208 | break; | ||
209 | case IO_WRITE: | ||
210 | strlcat(str, " Write", chars_left); | ||
211 | break; | ||
212 | case IO_SYNC: | ||
213 | strlcat(str, " Sync", chars_left); | ||
214 | break; | ||
215 | case IO_ASYNC: | ||
216 | strlcat(str, " Async", chars_left); | ||
217 | break; | ||
218 | case IO_TYPE_MAX: | ||
219 | strlcat(str, " Total", chars_left); | ||
220 | break; | ||
221 | default: | ||
222 | strlcat(str, " Invalid", chars_left); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | typedef uint64_t (get_var) (struct blkio_group *, int); | ||
227 | |||
228 | #define MAX_KEY_LEN 100 | ||
229 | uint64_t get_typed_stat(struct blkio_group *blkg, struct cgroup_map_cb *cb, | ||
230 | get_var *getvar, char *disk_id) | ||
231 | { | ||
232 | uint64_t disk_total; | ||
233 | char key_str[MAX_KEY_LEN]; | ||
234 | int type; | ||
235 | |||
236 | for (type = 0; type < IO_TYPE_MAX; type++) { | ||
237 | get_key_name(type, disk_id, key_str, MAX_KEY_LEN); | ||
238 | cb->fill(cb, key_str, getvar(blkg, type)); | ||
239 | } | ||
240 | disk_total = getvar(blkg, IO_READ) + getvar(blkg, IO_WRITE); | ||
241 | get_key_name(IO_TYPE_MAX, disk_id, key_str, MAX_KEY_LEN); | ||
242 | cb->fill(cb, key_str, disk_total); | ||
243 | return disk_total; | ||
244 | } | ||
245 | |||
246 | uint64_t get_stat(struct blkio_group *blkg, struct cgroup_map_cb *cb, | ||
247 | get_var *getvar, char *disk_id) | ||
248 | { | ||
249 | uint64_t var = getvar(blkg, 0); | ||
250 | cb->fill(cb, disk_id, var); | ||
251 | return var; | ||
252 | } | ||
253 | |||
254 | #define GET_STAT_INDEXED(__VAR) \ | ||
255 | uint64_t get_##__VAR##_stat(struct blkio_group *blkg, int type) \ | ||
256 | { \ | ||
257 | return blkg->stats.__VAR[type]; \ | ||
258 | } \ | ||
259 | |||
260 | GET_STAT_INDEXED(io_service_bytes); | ||
261 | GET_STAT_INDEXED(io_serviced); | ||
262 | GET_STAT_INDEXED(io_service_time); | ||
263 | GET_STAT_INDEXED(io_wait_time); | ||
264 | #undef GET_STAT_INDEXED | ||
265 | |||
266 | #define GET_STAT(__VAR, __CONV) \ | ||
267 | uint64_t get_##__VAR##_stat(struct blkio_group *blkg, int dummy) \ | ||
268 | { \ | ||
269 | uint64_t data = blkg->stats.__VAR; \ | ||
270 | if (__CONV) \ | ||
271 | data = (uint64_t)jiffies_to_msecs(data) * NSEC_PER_MSEC;\ | ||
272 | return data; \ | ||
273 | } | ||
274 | |||
275 | GET_STAT(time, 1); | ||
276 | GET_STAT(sectors, 0); | ||
277 | #ifdef CONFIG_DEBUG_BLK_CGROUP | ||
278 | GET_STAT(dequeue, 0); | ||
279 | #endif | ||
280 | #undef GET_STAT | ||
281 | |||
282 | #define SHOW_FUNCTION_PER_GROUP(__VAR, get_stats, getvar, show_total) \ | ||
174 | static int blkiocg_##__VAR##_read(struct cgroup *cgroup, \ | 283 | static int blkiocg_##__VAR##_read(struct cgroup *cgroup, \ |
175 | struct cftype *cftype, struct seq_file *m) \ | 284 | struct cftype *cftype, struct cgroup_map_cb *cb) \ |
176 | { \ | 285 | { \ |
177 | struct blkio_cgroup *blkcg; \ | 286 | struct blkio_cgroup *blkcg; \ |
178 | struct blkio_group *blkg; \ | 287 | struct blkio_group *blkg; \ |
179 | struct hlist_node *n; \ | 288 | struct hlist_node *n; \ |
289 | uint64_t cgroup_total = 0; \ | ||
290 | char disk_id[10]; \ | ||
180 | \ | 291 | \ |
181 | if (!cgroup_lock_live_group(cgroup)) \ | 292 | if (!cgroup_lock_live_group(cgroup)) \ |
182 | return -ENODEV; \ | 293 | return -ENODEV; \ |
@@ -184,19 +295,32 @@ static int blkiocg_##__VAR##_read(struct cgroup *cgroup, \ | |||
184 | blkcg = cgroup_to_blkio_cgroup(cgroup); \ | 295 | blkcg = cgroup_to_blkio_cgroup(cgroup); \ |
185 | rcu_read_lock(); \ | 296 | rcu_read_lock(); \ |
186 | hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {\ | 297 | hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) {\ |
187 | if (blkg->dev) \ | 298 | if (blkg->dev) { \ |
188 | seq_printf(m, "%u:%u %lu\n", MAJOR(blkg->dev), \ | 299 | spin_lock_irq(&blkg->stats_lock); \ |
189 | MINOR(blkg->dev), blkg->__VAR); \ | 300 | snprintf(disk_id, 10, "%u:%u", MAJOR(blkg->dev),\ |
301 | MINOR(blkg->dev)); \ | ||
302 | cgroup_total += get_stats(blkg, cb, getvar, \ | ||
303 | disk_id); \ | ||
304 | spin_unlock_irq(&blkg->stats_lock); \ | ||
305 | } \ | ||
190 | } \ | 306 | } \ |
307 | if (show_total) \ | ||
308 | cb->fill(cb, "Total", cgroup_total); \ | ||
191 | rcu_read_unlock(); \ | 309 | rcu_read_unlock(); \ |
192 | cgroup_unlock(); \ | 310 | cgroup_unlock(); \ |
193 | return 0; \ | 311 | return 0; \ |
194 | } | 312 | } |
195 | 313 | ||
196 | SHOW_FUNCTION_PER_GROUP(time); | 314 | SHOW_FUNCTION_PER_GROUP(time, get_stat, get_time_stat, 0); |
197 | SHOW_FUNCTION_PER_GROUP(sectors); | 315 | SHOW_FUNCTION_PER_GROUP(sectors, get_stat, get_sectors_stat, 0); |
316 | SHOW_FUNCTION_PER_GROUP(io_service_bytes, get_typed_stat, | ||
317 | get_io_service_bytes_stat, 1); | ||
318 | SHOW_FUNCTION_PER_GROUP(io_serviced, get_typed_stat, get_io_serviced_stat, 1); | ||
319 | SHOW_FUNCTION_PER_GROUP(io_service_time, get_typed_stat, | ||
320 | get_io_service_time_stat, 1); | ||
321 | SHOW_FUNCTION_PER_GROUP(io_wait_time, get_typed_stat, get_io_wait_time_stat, 1); | ||
198 | #ifdef CONFIG_DEBUG_BLK_CGROUP | 322 | #ifdef CONFIG_DEBUG_BLK_CGROUP |
199 | SHOW_FUNCTION_PER_GROUP(dequeue); | 323 | SHOW_FUNCTION_PER_GROUP(dequeue, get_stat, get_dequeue_stat, 0); |
200 | #endif | 324 | #endif |
201 | #undef SHOW_FUNCTION_PER_GROUP | 325 | #undef SHOW_FUNCTION_PER_GROUP |
202 | 326 | ||
@@ -204,7 +328,7 @@ SHOW_FUNCTION_PER_GROUP(dequeue); | |||
204 | void blkiocg_update_blkio_group_dequeue_stats(struct blkio_group *blkg, | 328 | void blkiocg_update_blkio_group_dequeue_stats(struct blkio_group *blkg, |
205 | unsigned long dequeue) | 329 | unsigned long dequeue) |
206 | { | 330 | { |
207 | blkg->dequeue += dequeue; | 331 | blkg->stats.dequeue += dequeue; |
208 | } | 332 | } |
209 | EXPORT_SYMBOL_GPL(blkiocg_update_blkio_group_dequeue_stats); | 333 | EXPORT_SYMBOL_GPL(blkiocg_update_blkio_group_dequeue_stats); |
210 | #endif | 334 | #endif |
@@ -217,16 +341,38 @@ struct cftype blkio_files[] = { | |||
217 | }, | 341 | }, |
218 | { | 342 | { |
219 | .name = "time", | 343 | .name = "time", |
220 | .read_seq_string = blkiocg_time_read, | 344 | .read_map = blkiocg_time_read, |
345 | .write_u64 = blkiocg_reset_write, | ||
221 | }, | 346 | }, |
222 | { | 347 | { |
223 | .name = "sectors", | 348 | .name = "sectors", |
224 | .read_seq_string = blkiocg_sectors_read, | 349 | .read_map = blkiocg_sectors_read, |
350 | .write_u64 = blkiocg_reset_write, | ||
351 | }, | ||
352 | { | ||
353 | .name = "io_service_bytes", | ||
354 | .read_map = blkiocg_io_service_bytes_read, | ||
355 | .write_u64 = blkiocg_reset_write, | ||
356 | }, | ||
357 | { | ||
358 | .name = "io_serviced", | ||
359 | .read_map = blkiocg_io_serviced_read, | ||
360 | .write_u64 = blkiocg_reset_write, | ||
361 | }, | ||
362 | { | ||
363 | .name = "io_service_time", | ||
364 | .read_map = blkiocg_io_service_time_read, | ||
365 | .write_u64 = blkiocg_reset_write, | ||
366 | }, | ||
367 | { | ||
368 | .name = "io_wait_time", | ||
369 | .read_map = blkiocg_io_wait_time_read, | ||
370 | .write_u64 = blkiocg_reset_write, | ||
225 | }, | 371 | }, |
226 | #ifdef CONFIG_DEBUG_BLK_CGROUP | 372 | #ifdef CONFIG_DEBUG_BLK_CGROUP |
227 | { | 373 | { |
228 | .name = "dequeue", | 374 | .name = "dequeue", |
229 | .read_seq_string = blkiocg_dequeue_read, | 375 | .read_map = blkiocg_dequeue_read, |
230 | }, | 376 | }, |
231 | #endif | 377 | #endif |
232 | }; | 378 | }; |
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index fe445178f586..5c5e5294b506 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h | |||
@@ -23,6 +23,14 @@ extern struct cgroup_subsys blkio_subsys; | |||
23 | #define blkio_subsys_id blkio_subsys.subsys_id | 23 | #define blkio_subsys_id blkio_subsys.subsys_id |
24 | #endif | 24 | #endif |
25 | 25 | ||
26 | enum io_type { | ||
27 | IO_READ = 0, | ||
28 | IO_WRITE, | ||
29 | IO_SYNC, | ||
30 | IO_ASYNC, | ||
31 | IO_TYPE_MAX | ||
32 | }; | ||
33 | |||
26 | struct blkio_cgroup { | 34 | struct blkio_cgroup { |
27 | struct cgroup_subsys_state css; | 35 | struct cgroup_subsys_state css; |
28 | unsigned int weight; | 36 | unsigned int weight; |
@@ -30,6 +38,23 @@ struct blkio_cgroup { | |||
30 | struct hlist_head blkg_list; | 38 | struct hlist_head blkg_list; |
31 | }; | 39 | }; |
32 | 40 | ||
41 | struct blkio_group_stats { | ||
42 | /* total disk time and nr sectors dispatched by this group */ | ||
43 | uint64_t time; | ||
44 | uint64_t sectors; | ||
45 | /* Total disk time used by IOs in ns */ | ||
46 | uint64_t io_service_time[IO_TYPE_MAX]; | ||
47 | uint64_t io_service_bytes[IO_TYPE_MAX]; /* Total bytes transferred */ | ||
48 | /* Total IOs serviced, post merge */ | ||
49 | uint64_t io_serviced[IO_TYPE_MAX]; | ||
50 | /* Total time spent waiting in scheduler queue in ns */ | ||
51 | uint64_t io_wait_time[IO_TYPE_MAX]; | ||
52 | #ifdef CONFIG_DEBUG_BLK_CGROUP | ||
53 | /* How many times this group has been removed from service tree */ | ||
54 | unsigned long dequeue; | ||
55 | #endif | ||
56 | }; | ||
57 | |||
33 | struct blkio_group { | 58 | struct blkio_group { |
34 | /* An rcu protected unique identifier for the group */ | 59 | /* An rcu protected unique identifier for the group */ |
35 | void *key; | 60 | void *key; |
@@ -38,15 +63,13 @@ struct blkio_group { | |||
38 | #ifdef CONFIG_DEBUG_BLK_CGROUP | 63 | #ifdef CONFIG_DEBUG_BLK_CGROUP |
39 | /* Store cgroup path */ | 64 | /* Store cgroup path */ |
40 | char path[128]; | 65 | char path[128]; |
41 | /* How many times this group has been removed from service tree */ | ||
42 | unsigned long dequeue; | ||
43 | #endif | 66 | #endif |
44 | /* The device MKDEV(major, minor), this group has been created for */ | 67 | /* The device MKDEV(major, minor), this group has been created for */ |
45 | dev_t dev; | 68 | dev_t dev; |
46 | 69 | ||
47 | /* total disk time and nr sectors dispatched by this group */ | 70 | /* Need to serialize the stats in the case of reset/update */ |
48 | unsigned long time; | 71 | spinlock_t stats_lock; |
49 | unsigned long sectors; | 72 | struct blkio_group_stats stats; |
50 | }; | 73 | }; |
51 | 74 | ||
52 | typedef void (blkio_unlink_group_fn) (void *key, struct blkio_group *blkg); | 75 | typedef void (blkio_unlink_group_fn) (void *key, struct blkio_group *blkg); |
@@ -105,8 +128,8 @@ extern void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, | |||
105 | extern int blkiocg_del_blkio_group(struct blkio_group *blkg); | 128 | extern int blkiocg_del_blkio_group(struct blkio_group *blkg); |
106 | extern struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg, | 129 | extern struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg, |
107 | void *key); | 130 | void *key); |
108 | void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, | 131 | void blkiocg_update_timeslice_used(struct blkio_group *blkg, |
109 | unsigned long time); | 132 | unsigned long time); |
110 | #else | 133 | #else |
111 | struct cgroup; | 134 | struct cgroup; |
112 | static inline struct blkio_cgroup * | 135 | static inline struct blkio_cgroup * |
@@ -122,7 +145,7 @@ blkiocg_del_blkio_group(struct blkio_group *blkg) { return 0; } | |||
122 | 145 | ||
123 | static inline struct blkio_group * | 146 | static inline struct blkio_group * |
124 | blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key) { return NULL; } | 147 | blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key) { return NULL; } |
125 | static inline void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, | 148 | static inline void blkiocg_update_timeslice_used(struct blkio_group *blkg, |
126 | unsigned long time) {} | 149 | unsigned long time) {} |
127 | #endif | 150 | #endif |
128 | #endif /* _BLK_CGROUP_H */ | 151 | #endif /* _BLK_CGROUP_H */ |
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7471d36bce89..c5161bbf2fe9 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c | |||
@@ -915,7 +915,7 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, | |||
915 | 915 | ||
916 | cfq_log_cfqg(cfqd, cfqg, "served: vt=%llu min_vt=%llu", cfqg->vdisktime, | 916 | cfq_log_cfqg(cfqd, cfqg, "served: vt=%llu min_vt=%llu", cfqg->vdisktime, |
917 | st->min_vdisktime); | 917 | st->min_vdisktime); |
918 | blkiocg_update_blkio_group_stats(&cfqg->blkg, used_sl); | 918 | blkiocg_update_timeslice_used(&cfqg->blkg, used_sl); |
919 | } | 919 | } |
920 | 920 | ||
921 | #ifdef CONFIG_CFQ_GROUP_IOSCHED | 921 | #ifdef CONFIG_CFQ_GROUP_IOSCHED |