aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDivyesh Shah <dpshah@google.com>2010-04-01 18:01:24 -0400
committerJens Axboe <jens.axboe@oracle.com>2010-04-02 02:44:36 -0400
commit303a3acb2362f16c7e7f4c53b40c2f4b396dc8d5 (patch)
tree7996f28d5659fef44594f7049e30264b16baa62d
parent9a0785b0da561e1e9c6617df85e93ae107a42f18 (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.c178
-rw-r--r--block/blk-cgroup.h39
-rw-r--r--block/cfq-iosched.c2
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}
56EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup); 56EXPORT_SYMBOL_GPL(cgroup_to_blkio_cgroup);
57 57
58void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, 58void 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}
63EXPORT_SYMBOL_GPL(blkiocg_update_blkio_group_stats); 66EXPORT_SYMBOL_GPL(blkiocg_update_timeslice_used);
64 67
65void blkiocg_add_blkio_group(struct blkio_cgroup *blkcg, 68void 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) \ 176static int
177blkiocg_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
196void 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
226typedef uint64_t (get_var) (struct blkio_group *, int);
227
228#define MAX_KEY_LEN 100
229uint64_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
246uint64_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) \
255uint64_t get_##__VAR##_stat(struct blkio_group *blkg, int type) \
256{ \
257 return blkg->stats.__VAR[type]; \
258} \
259
260GET_STAT_INDEXED(io_service_bytes);
261GET_STAT_INDEXED(io_serviced);
262GET_STAT_INDEXED(io_service_time);
263GET_STAT_INDEXED(io_wait_time);
264#undef GET_STAT_INDEXED
265
266#define GET_STAT(__VAR, __CONV) \
267uint64_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
275GET_STAT(time, 1);
276GET_STAT(sectors, 0);
277#ifdef CONFIG_DEBUG_BLK_CGROUP
278GET_STAT(dequeue, 0);
279#endif
280#undef GET_STAT
281
282#define SHOW_FUNCTION_PER_GROUP(__VAR, get_stats, getvar, show_total) \
174static int blkiocg_##__VAR##_read(struct cgroup *cgroup, \ 283static 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
196SHOW_FUNCTION_PER_GROUP(time); 314SHOW_FUNCTION_PER_GROUP(time, get_stat, get_time_stat, 0);
197SHOW_FUNCTION_PER_GROUP(sectors); 315SHOW_FUNCTION_PER_GROUP(sectors, get_stat, get_sectors_stat, 0);
316SHOW_FUNCTION_PER_GROUP(io_service_bytes, get_typed_stat,
317 get_io_service_bytes_stat, 1);
318SHOW_FUNCTION_PER_GROUP(io_serviced, get_typed_stat, get_io_serviced_stat, 1);
319SHOW_FUNCTION_PER_GROUP(io_service_time, get_typed_stat,
320 get_io_service_time_stat, 1);
321SHOW_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
199SHOW_FUNCTION_PER_GROUP(dequeue); 323SHOW_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);
204void blkiocg_update_blkio_group_dequeue_stats(struct blkio_group *blkg, 328void 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}
209EXPORT_SYMBOL_GPL(blkiocg_update_blkio_group_dequeue_stats); 333EXPORT_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
26enum io_type {
27 IO_READ = 0,
28 IO_WRITE,
29 IO_SYNC,
30 IO_ASYNC,
31 IO_TYPE_MAX
32};
33
26struct blkio_cgroup { 34struct 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
41struct 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
33struct blkio_group { 58struct 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
52typedef void (blkio_unlink_group_fn) (void *key, struct blkio_group *blkg); 75typedef 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,
105extern int blkiocg_del_blkio_group(struct blkio_group *blkg); 128extern int blkiocg_del_blkio_group(struct blkio_group *blkg);
106extern struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg, 129extern struct blkio_group *blkiocg_lookup_group(struct blkio_cgroup *blkcg,
107 void *key); 130 void *key);
108void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, 131void blkiocg_update_timeslice_used(struct blkio_group *blkg,
109 unsigned long time); 132 unsigned long time);
110#else 133#else
111struct cgroup; 134struct cgroup;
112static inline struct blkio_cgroup * 135static inline struct blkio_cgroup *
@@ -122,7 +145,7 @@ blkiocg_del_blkio_group(struct blkio_group *blkg) { return 0; }
122 145
123static inline struct blkio_group * 146static inline struct blkio_group *
124blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key) { return NULL; } 147blkiocg_lookup_group(struct blkio_cgroup *blkcg, void *key) { return NULL; }
125static inline void blkiocg_update_blkio_group_stats(struct blkio_group *blkg, 148static 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