diff options
-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 |