Commit | Line | Data |
---|---|---|
07e4fead OS |
1 | /* |
2 | * Copyright (C) 2017 Facebook | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public | |
6 | * License v2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/blkdev.h> | |
19 | #include <linux/debugfs.h> | |
20 | ||
21 | #include <linux/blk-mq.h> | |
22 | #include "blk-mq.h" | |
d96b37c0 | 23 | #include "blk-mq-tag.h" |
07e4fead OS |
24 | |
25 | struct blk_mq_debugfs_attr { | |
26 | const char *name; | |
27 | umode_t mode; | |
28 | const struct file_operations *fops; | |
29 | }; | |
30 | ||
31 | static struct dentry *block_debugfs_root; | |
32 | ||
950cd7e9 OS |
33 | static int blk_mq_debugfs_seq_open(struct inode *inode, struct file *file, |
34 | const struct seq_operations *ops) | |
35 | { | |
36 | struct seq_file *m; | |
37 | int ret; | |
38 | ||
39 | ret = seq_open(file, ops); | |
40 | if (!ret) { | |
41 | m = file->private_data; | |
42 | m->private = inode->i_private; | |
43 | } | |
44 | return ret; | |
45 | } | |
46 | ||
9abb2ad2 OS |
47 | static int hctx_state_show(struct seq_file *m, void *v) |
48 | { | |
49 | struct blk_mq_hw_ctx *hctx = m->private; | |
50 | ||
51 | seq_printf(m, "0x%lx\n", hctx->state); | |
52 | return 0; | |
53 | } | |
54 | ||
55 | static int hctx_state_open(struct inode *inode, struct file *file) | |
56 | { | |
57 | return single_open(file, hctx_state_show, inode->i_private); | |
58 | } | |
59 | ||
60 | static const struct file_operations hctx_state_fops = { | |
61 | .open = hctx_state_open, | |
62 | .read = seq_read, | |
63 | .llseek = seq_lseek, | |
64 | .release = single_release, | |
65 | }; | |
66 | ||
67 | static int hctx_flags_show(struct seq_file *m, void *v) | |
68 | { | |
69 | struct blk_mq_hw_ctx *hctx = m->private; | |
70 | ||
71 | seq_printf(m, "0x%lx\n", hctx->flags); | |
72 | return 0; | |
73 | } | |
74 | ||
75 | static int hctx_flags_open(struct inode *inode, struct file *file) | |
76 | { | |
77 | return single_open(file, hctx_flags_show, inode->i_private); | |
78 | } | |
79 | ||
80 | static const struct file_operations hctx_flags_fops = { | |
81 | .open = hctx_flags_open, | |
82 | .read = seq_read, | |
83 | .llseek = seq_lseek, | |
84 | .release = single_release, | |
85 | }; | |
86 | ||
950cd7e9 OS |
87 | static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v) |
88 | { | |
89 | struct request *rq = list_entry_rq(v); | |
90 | ||
aebf526b CH |
91 | seq_printf(m, "%p {.cmd_flags=0x%x, .rq_flags=0x%x, .tag=%d, .internal_tag=%d}\n", |
92 | rq, rq->cmd_flags, (unsigned int)rq->rq_flags, | |
7b393852 | 93 | rq->tag, rq->internal_tag); |
950cd7e9 OS |
94 | return 0; |
95 | } | |
96 | ||
97 | static void *hctx_dispatch_start(struct seq_file *m, loff_t *pos) | |
98 | { | |
99 | struct blk_mq_hw_ctx *hctx = m->private; | |
100 | ||
101 | spin_lock(&hctx->lock); | |
102 | return seq_list_start(&hctx->dispatch, *pos); | |
103 | } | |
104 | ||
105 | static void *hctx_dispatch_next(struct seq_file *m, void *v, loff_t *pos) | |
106 | { | |
107 | struct blk_mq_hw_ctx *hctx = m->private; | |
108 | ||
109 | return seq_list_next(v, &hctx->dispatch, pos); | |
110 | } | |
111 | ||
112 | static void hctx_dispatch_stop(struct seq_file *m, void *v) | |
113 | { | |
114 | struct blk_mq_hw_ctx *hctx = m->private; | |
115 | ||
116 | spin_unlock(&hctx->lock); | |
117 | } | |
118 | ||
119 | static const struct seq_operations hctx_dispatch_seq_ops = { | |
120 | .start = hctx_dispatch_start, | |
121 | .next = hctx_dispatch_next, | |
122 | .stop = hctx_dispatch_stop, | |
123 | .show = blk_mq_debugfs_rq_show, | |
124 | }; | |
125 | ||
126 | static int hctx_dispatch_open(struct inode *inode, struct file *file) | |
127 | { | |
128 | return blk_mq_debugfs_seq_open(inode, file, &hctx_dispatch_seq_ops); | |
129 | } | |
130 | ||
131 | static const struct file_operations hctx_dispatch_fops = { | |
132 | .open = hctx_dispatch_open, | |
133 | .read = seq_read, | |
134 | .llseek = seq_lseek, | |
135 | .release = seq_release, | |
136 | }; | |
137 | ||
0bfa5288 OS |
138 | static int hctx_ctx_map_show(struct seq_file *m, void *v) |
139 | { | |
140 | struct blk_mq_hw_ctx *hctx = m->private; | |
141 | ||
142 | sbitmap_bitmap_show(&hctx->ctx_map, m); | |
143 | return 0; | |
144 | } | |
145 | ||
146 | static int hctx_ctx_map_open(struct inode *inode, struct file *file) | |
147 | { | |
148 | return single_open(file, hctx_ctx_map_show, inode->i_private); | |
149 | } | |
150 | ||
151 | static const struct file_operations hctx_ctx_map_fops = { | |
152 | .open = hctx_ctx_map_open, | |
153 | .read = seq_read, | |
154 | .llseek = seq_lseek, | |
155 | .release = single_release, | |
156 | }; | |
157 | ||
d96b37c0 OS |
158 | static void blk_mq_debugfs_tags_show(struct seq_file *m, |
159 | struct blk_mq_tags *tags) | |
160 | { | |
161 | seq_printf(m, "nr_tags=%u\n", tags->nr_tags); | |
162 | seq_printf(m, "nr_reserved_tags=%u\n", tags->nr_reserved_tags); | |
163 | seq_printf(m, "active_queues=%d\n", | |
164 | atomic_read(&tags->active_queues)); | |
165 | ||
166 | seq_puts(m, "\nbitmap_tags:\n"); | |
167 | sbitmap_queue_show(&tags->bitmap_tags, m); | |
168 | ||
169 | if (tags->nr_reserved_tags) { | |
170 | seq_puts(m, "\nbreserved_tags:\n"); | |
171 | sbitmap_queue_show(&tags->breserved_tags, m); | |
172 | } | |
173 | } | |
174 | ||
175 | static int hctx_tags_show(struct seq_file *m, void *v) | |
176 | { | |
177 | struct blk_mq_hw_ctx *hctx = m->private; | |
178 | struct request_queue *q = hctx->queue; | |
179 | ||
180 | mutex_lock(&q->sysfs_lock); | |
181 | if (hctx->tags) | |
182 | blk_mq_debugfs_tags_show(m, hctx->tags); | |
183 | mutex_unlock(&q->sysfs_lock); | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | static int hctx_tags_open(struct inode *inode, struct file *file) | |
189 | { | |
190 | return single_open(file, hctx_tags_show, inode->i_private); | |
191 | } | |
192 | ||
193 | static const struct file_operations hctx_tags_fops = { | |
194 | .open = hctx_tags_open, | |
195 | .read = seq_read, | |
196 | .llseek = seq_lseek, | |
197 | .release = single_release, | |
198 | }; | |
199 | ||
d7e3621a OS |
200 | static int hctx_tags_bitmap_show(struct seq_file *m, void *v) |
201 | { | |
202 | struct blk_mq_hw_ctx *hctx = m->private; | |
203 | struct request_queue *q = hctx->queue; | |
204 | ||
205 | mutex_lock(&q->sysfs_lock); | |
206 | if (hctx->tags) | |
207 | sbitmap_bitmap_show(&hctx->tags->bitmap_tags.sb, m); | |
208 | mutex_unlock(&q->sysfs_lock); | |
209 | return 0; | |
210 | } | |
211 | ||
212 | static int hctx_tags_bitmap_open(struct inode *inode, struct file *file) | |
213 | { | |
214 | return single_open(file, hctx_tags_bitmap_show, inode->i_private); | |
215 | } | |
216 | ||
217 | static const struct file_operations hctx_tags_bitmap_fops = { | |
218 | .open = hctx_tags_bitmap_open, | |
219 | .read = seq_read, | |
220 | .llseek = seq_lseek, | |
221 | .release = single_release, | |
222 | }; | |
223 | ||
d96b37c0 OS |
224 | static int hctx_sched_tags_show(struct seq_file *m, void *v) |
225 | { | |
226 | struct blk_mq_hw_ctx *hctx = m->private; | |
227 | struct request_queue *q = hctx->queue; | |
228 | ||
229 | mutex_lock(&q->sysfs_lock); | |
230 | if (hctx->sched_tags) | |
231 | blk_mq_debugfs_tags_show(m, hctx->sched_tags); | |
232 | mutex_unlock(&q->sysfs_lock); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | static int hctx_sched_tags_open(struct inode *inode, struct file *file) | |
238 | { | |
239 | return single_open(file, hctx_sched_tags_show, inode->i_private); | |
240 | } | |
241 | ||
242 | static const struct file_operations hctx_sched_tags_fops = { | |
243 | .open = hctx_sched_tags_open, | |
244 | .read = seq_read, | |
245 | .llseek = seq_lseek, | |
246 | .release = single_release, | |
247 | }; | |
248 | ||
d7e3621a OS |
249 | static int hctx_sched_tags_bitmap_show(struct seq_file *m, void *v) |
250 | { | |
251 | struct blk_mq_hw_ctx *hctx = m->private; | |
252 | struct request_queue *q = hctx->queue; | |
253 | ||
254 | mutex_lock(&q->sysfs_lock); | |
255 | if (hctx->sched_tags) | |
256 | sbitmap_bitmap_show(&hctx->sched_tags->bitmap_tags.sb, m); | |
257 | mutex_unlock(&q->sysfs_lock); | |
258 | return 0; | |
259 | } | |
260 | ||
261 | static int hctx_sched_tags_bitmap_open(struct inode *inode, struct file *file) | |
262 | { | |
263 | return single_open(file, hctx_sched_tags_bitmap_show, inode->i_private); | |
264 | } | |
265 | ||
266 | static const struct file_operations hctx_sched_tags_bitmap_fops = { | |
267 | .open = hctx_sched_tags_bitmap_open, | |
268 | .read = seq_read, | |
269 | .llseek = seq_lseek, | |
270 | .release = single_release, | |
271 | }; | |
272 | ||
be215473 OS |
273 | static int hctx_io_poll_show(struct seq_file *m, void *v) |
274 | { | |
275 | struct blk_mq_hw_ctx *hctx = m->private; | |
276 | ||
277 | seq_printf(m, "considered=%lu\n", hctx->poll_considered); | |
278 | seq_printf(m, "invoked=%lu\n", hctx->poll_invoked); | |
279 | seq_printf(m, "success=%lu\n", hctx->poll_success); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static int hctx_io_poll_open(struct inode *inode, struct file *file) | |
284 | { | |
285 | return single_open(file, hctx_io_poll_show, inode->i_private); | |
286 | } | |
287 | ||
288 | static ssize_t hctx_io_poll_write(struct file *file, const char __user *buf, | |
289 | size_t count, loff_t *ppos) | |
290 | { | |
291 | struct seq_file *m = file->private_data; | |
292 | struct blk_mq_hw_ctx *hctx = m->private; | |
293 | ||
294 | hctx->poll_considered = hctx->poll_invoked = hctx->poll_success = 0; | |
295 | return count; | |
296 | } | |
297 | ||
298 | static const struct file_operations hctx_io_poll_fops = { | |
299 | .open = hctx_io_poll_open, | |
300 | .read = seq_read, | |
301 | .write = hctx_io_poll_write, | |
302 | .llseek = seq_lseek, | |
303 | .release = single_release, | |
304 | }; | |
305 | ||
306 | static void print_stat(struct seq_file *m, struct blk_rq_stat *stat) | |
307 | { | |
308 | seq_printf(m, "samples=%d, mean=%lld, min=%llu, max=%llu", | |
309 | stat->nr_samples, stat->mean, stat->min, stat->max); | |
310 | } | |
311 | ||
312 | static int hctx_stats_show(struct seq_file *m, void *v) | |
313 | { | |
314 | struct blk_mq_hw_ctx *hctx = m->private; | |
315 | struct blk_rq_stat stat[2]; | |
316 | ||
317 | blk_stat_init(&stat[BLK_STAT_READ]); | |
318 | blk_stat_init(&stat[BLK_STAT_WRITE]); | |
319 | ||
320 | blk_hctx_stat_get(hctx, stat); | |
321 | ||
322 | seq_puts(m, "read: "); | |
323 | print_stat(m, &stat[BLK_STAT_READ]); | |
324 | seq_puts(m, "\n"); | |
325 | ||
326 | seq_puts(m, "write: "); | |
327 | print_stat(m, &stat[BLK_STAT_WRITE]); | |
328 | seq_puts(m, "\n"); | |
329 | return 0; | |
330 | } | |
331 | ||
332 | static int hctx_stats_open(struct inode *inode, struct file *file) | |
333 | { | |
334 | return single_open(file, hctx_stats_show, inode->i_private); | |
335 | } | |
336 | ||
337 | static ssize_t hctx_stats_write(struct file *file, const char __user *buf, | |
338 | size_t count, loff_t *ppos) | |
339 | { | |
340 | struct seq_file *m = file->private_data; | |
341 | struct blk_mq_hw_ctx *hctx = m->private; | |
342 | struct blk_mq_ctx *ctx; | |
343 | int i; | |
344 | ||
345 | hctx_for_each_ctx(hctx, ctx, i) { | |
346 | blk_stat_init(&ctx->stat[BLK_STAT_READ]); | |
347 | blk_stat_init(&ctx->stat[BLK_STAT_WRITE]); | |
348 | } | |
349 | return count; | |
350 | } | |
351 | ||
352 | static const struct file_operations hctx_stats_fops = { | |
353 | .open = hctx_stats_open, | |
354 | .read = seq_read, | |
355 | .write = hctx_stats_write, | |
356 | .llseek = seq_lseek, | |
357 | .release = single_release, | |
358 | }; | |
359 | ||
360 | static int hctx_dispatched_show(struct seq_file *m, void *v) | |
361 | { | |
362 | struct blk_mq_hw_ctx *hctx = m->private; | |
363 | int i; | |
364 | ||
365 | seq_printf(m, "%8u\t%lu\n", 0U, hctx->dispatched[0]); | |
366 | ||
367 | for (i = 1; i < BLK_MQ_MAX_DISPATCH_ORDER - 1; i++) { | |
368 | unsigned int d = 1U << (i - 1); | |
369 | ||
370 | seq_printf(m, "%8u\t%lu\n", d, hctx->dispatched[i]); | |
371 | } | |
372 | ||
373 | seq_printf(m, "%8u+\t%lu\n", 1U << (i - 1), hctx->dispatched[i]); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | static int hctx_dispatched_open(struct inode *inode, struct file *file) | |
378 | { | |
379 | return single_open(file, hctx_dispatched_show, inode->i_private); | |
380 | } | |
381 | ||
382 | static ssize_t hctx_dispatched_write(struct file *file, const char __user *buf, | |
383 | size_t count, loff_t *ppos) | |
384 | { | |
385 | struct seq_file *m = file->private_data; | |
386 | struct blk_mq_hw_ctx *hctx = m->private; | |
387 | int i; | |
388 | ||
389 | for (i = 0; i < BLK_MQ_MAX_DISPATCH_ORDER; i++) | |
390 | hctx->dispatched[i] = 0; | |
391 | return count; | |
392 | } | |
393 | ||
394 | static const struct file_operations hctx_dispatched_fops = { | |
395 | .open = hctx_dispatched_open, | |
396 | .read = seq_read, | |
397 | .write = hctx_dispatched_write, | |
398 | .llseek = seq_lseek, | |
399 | .release = single_release, | |
400 | }; | |
401 | ||
4a46f05e OS |
402 | static int hctx_queued_show(struct seq_file *m, void *v) |
403 | { | |
404 | struct blk_mq_hw_ctx *hctx = m->private; | |
405 | ||
406 | seq_printf(m, "%lu\n", hctx->queued); | |
407 | return 0; | |
408 | } | |
409 | ||
410 | static int hctx_queued_open(struct inode *inode, struct file *file) | |
411 | { | |
412 | return single_open(file, hctx_queued_show, inode->i_private); | |
413 | } | |
414 | ||
415 | static ssize_t hctx_queued_write(struct file *file, const char __user *buf, | |
416 | size_t count, loff_t *ppos) | |
417 | { | |
418 | struct seq_file *m = file->private_data; | |
419 | struct blk_mq_hw_ctx *hctx = m->private; | |
420 | ||
421 | hctx->queued = 0; | |
422 | return count; | |
423 | } | |
424 | ||
425 | static const struct file_operations hctx_queued_fops = { | |
426 | .open = hctx_queued_open, | |
427 | .read = seq_read, | |
428 | .write = hctx_queued_write, | |
429 | .llseek = seq_lseek, | |
430 | .release = single_release, | |
431 | }; | |
432 | ||
433 | static int hctx_run_show(struct seq_file *m, void *v) | |
434 | { | |
435 | struct blk_mq_hw_ctx *hctx = m->private; | |
436 | ||
437 | seq_printf(m, "%lu\n", hctx->run); | |
438 | return 0; | |
439 | } | |
440 | ||
441 | static int hctx_run_open(struct inode *inode, struct file *file) | |
442 | { | |
443 | return single_open(file, hctx_run_show, inode->i_private); | |
444 | } | |
445 | ||
446 | static ssize_t hctx_run_write(struct file *file, const char __user *buf, | |
447 | size_t count, loff_t *ppos) | |
448 | { | |
449 | struct seq_file *m = file->private_data; | |
450 | struct blk_mq_hw_ctx *hctx = m->private; | |
451 | ||
452 | hctx->run = 0; | |
453 | return count; | |
454 | } | |
455 | ||
456 | static const struct file_operations hctx_run_fops = { | |
457 | .open = hctx_run_open, | |
458 | .read = seq_read, | |
459 | .write = hctx_run_write, | |
460 | .llseek = seq_lseek, | |
461 | .release = single_release, | |
462 | }; | |
463 | ||
464 | static int hctx_active_show(struct seq_file *m, void *v) | |
465 | { | |
466 | struct blk_mq_hw_ctx *hctx = m->private; | |
467 | ||
468 | seq_printf(m, "%d\n", atomic_read(&hctx->nr_active)); | |
469 | return 0; | |
470 | } | |
471 | ||
472 | static int hctx_active_open(struct inode *inode, struct file *file) | |
473 | { | |
474 | return single_open(file, hctx_active_show, inode->i_private); | |
475 | } | |
476 | ||
477 | static const struct file_operations hctx_active_fops = { | |
478 | .open = hctx_active_open, | |
479 | .read = seq_read, | |
480 | .llseek = seq_lseek, | |
481 | .release = single_release, | |
482 | }; | |
483 | ||
950cd7e9 OS |
484 | static void *ctx_rq_list_start(struct seq_file *m, loff_t *pos) |
485 | { | |
486 | struct blk_mq_ctx *ctx = m->private; | |
487 | ||
488 | spin_lock(&ctx->lock); | |
489 | return seq_list_start(&ctx->rq_list, *pos); | |
490 | } | |
491 | ||
492 | static void *ctx_rq_list_next(struct seq_file *m, void *v, loff_t *pos) | |
493 | { | |
494 | struct blk_mq_ctx *ctx = m->private; | |
495 | ||
496 | return seq_list_next(v, &ctx->rq_list, pos); | |
497 | } | |
498 | ||
499 | static void ctx_rq_list_stop(struct seq_file *m, void *v) | |
500 | { | |
501 | struct blk_mq_ctx *ctx = m->private; | |
502 | ||
503 | spin_unlock(&ctx->lock); | |
504 | } | |
505 | ||
506 | static const struct seq_operations ctx_rq_list_seq_ops = { | |
507 | .start = ctx_rq_list_start, | |
508 | .next = ctx_rq_list_next, | |
509 | .stop = ctx_rq_list_stop, | |
510 | .show = blk_mq_debugfs_rq_show, | |
511 | }; | |
512 | ||
513 | static int ctx_rq_list_open(struct inode *inode, struct file *file) | |
514 | { | |
515 | return blk_mq_debugfs_seq_open(inode, file, &ctx_rq_list_seq_ops); | |
516 | } | |
517 | ||
518 | static const struct file_operations ctx_rq_list_fops = { | |
519 | .open = ctx_rq_list_open, | |
520 | .read = seq_read, | |
521 | .llseek = seq_lseek, | |
522 | .release = seq_release, | |
523 | }; | |
524 | ||
4a46f05e OS |
525 | static int ctx_dispatched_show(struct seq_file *m, void *v) |
526 | { | |
527 | struct blk_mq_ctx *ctx = m->private; | |
528 | ||
529 | seq_printf(m, "%lu %lu\n", ctx->rq_dispatched[1], ctx->rq_dispatched[0]); | |
530 | return 0; | |
531 | } | |
532 | ||
533 | static int ctx_dispatched_open(struct inode *inode, struct file *file) | |
534 | { | |
535 | return single_open(file, ctx_dispatched_show, inode->i_private); | |
536 | } | |
537 | ||
538 | static ssize_t ctx_dispatched_write(struct file *file, const char __user *buf, | |
539 | size_t count, loff_t *ppos) | |
540 | { | |
541 | struct seq_file *m = file->private_data; | |
542 | struct blk_mq_ctx *ctx = m->private; | |
543 | ||
544 | ctx->rq_dispatched[0] = ctx->rq_dispatched[1] = 0; | |
545 | return count; | |
546 | } | |
547 | ||
548 | static const struct file_operations ctx_dispatched_fops = { | |
549 | .open = ctx_dispatched_open, | |
550 | .read = seq_read, | |
551 | .write = ctx_dispatched_write, | |
552 | .llseek = seq_lseek, | |
553 | .release = single_release, | |
554 | }; | |
555 | ||
556 | static int ctx_merged_show(struct seq_file *m, void *v) | |
557 | { | |
558 | struct blk_mq_ctx *ctx = m->private; | |
559 | ||
560 | seq_printf(m, "%lu\n", ctx->rq_merged); | |
561 | return 0; | |
562 | } | |
563 | ||
564 | static int ctx_merged_open(struct inode *inode, struct file *file) | |
565 | { | |
566 | return single_open(file, ctx_merged_show, inode->i_private); | |
567 | } | |
568 | ||
569 | static ssize_t ctx_merged_write(struct file *file, const char __user *buf, | |
570 | size_t count, loff_t *ppos) | |
571 | { | |
572 | struct seq_file *m = file->private_data; | |
573 | struct blk_mq_ctx *ctx = m->private; | |
574 | ||
575 | ctx->rq_merged = 0; | |
576 | return count; | |
577 | } | |
578 | ||
579 | static const struct file_operations ctx_merged_fops = { | |
580 | .open = ctx_merged_open, | |
581 | .read = seq_read, | |
582 | .write = ctx_merged_write, | |
583 | .llseek = seq_lseek, | |
584 | .release = single_release, | |
585 | }; | |
586 | ||
587 | static int ctx_completed_show(struct seq_file *m, void *v) | |
588 | { | |
589 | struct blk_mq_ctx *ctx = m->private; | |
590 | ||
591 | seq_printf(m, "%lu %lu\n", ctx->rq_completed[1], ctx->rq_completed[0]); | |
592 | return 0; | |
593 | } | |
594 | ||
595 | static int ctx_completed_open(struct inode *inode, struct file *file) | |
596 | { | |
597 | return single_open(file, ctx_completed_show, inode->i_private); | |
598 | } | |
599 | ||
600 | static ssize_t ctx_completed_write(struct file *file, const char __user *buf, | |
601 | size_t count, loff_t *ppos) | |
602 | { | |
603 | struct seq_file *m = file->private_data; | |
604 | struct blk_mq_ctx *ctx = m->private; | |
605 | ||
606 | ctx->rq_completed[0] = ctx->rq_completed[1] = 0; | |
607 | return count; | |
608 | } | |
609 | ||
610 | static const struct file_operations ctx_completed_fops = { | |
611 | .open = ctx_completed_open, | |
612 | .read = seq_read, | |
613 | .write = ctx_completed_write, | |
614 | .llseek = seq_lseek, | |
615 | .release = single_release, | |
616 | }; | |
617 | ||
07e4fead | 618 | static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = { |
9abb2ad2 OS |
619 | {"state", 0400, &hctx_state_fops}, |
620 | {"flags", 0400, &hctx_flags_fops}, | |
950cd7e9 | 621 | {"dispatch", 0400, &hctx_dispatch_fops}, |
0bfa5288 | 622 | {"ctx_map", 0400, &hctx_ctx_map_fops}, |
d96b37c0 | 623 | {"tags", 0400, &hctx_tags_fops}, |
d7e3621a | 624 | {"tags_bitmap", 0400, &hctx_tags_bitmap_fops}, |
d96b37c0 | 625 | {"sched_tags", 0400, &hctx_sched_tags_fops}, |
d7e3621a | 626 | {"sched_tags_bitmap", 0400, &hctx_sched_tags_bitmap_fops}, |
be215473 OS |
627 | {"io_poll", 0600, &hctx_io_poll_fops}, |
628 | {"stats", 0600, &hctx_stats_fops}, | |
629 | {"dispatched", 0600, &hctx_dispatched_fops}, | |
4a46f05e OS |
630 | {"queued", 0600, &hctx_queued_fops}, |
631 | {"run", 0600, &hctx_run_fops}, | |
632 | {"active", 0400, &hctx_active_fops}, | |
07e4fead OS |
633 | }; |
634 | ||
635 | static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = { | |
950cd7e9 | 636 | {"rq_list", 0400, &ctx_rq_list_fops}, |
4a46f05e OS |
637 | {"dispatched", 0600, &ctx_dispatched_fops}, |
638 | {"merged", 0600, &ctx_merged_fops}, | |
639 | {"completed", 0600, &ctx_completed_fops}, | |
07e4fead OS |
640 | }; |
641 | ||
642 | int blk_mq_debugfs_register(struct request_queue *q, const char *name) | |
643 | { | |
644 | if (!block_debugfs_root) | |
645 | return -ENOENT; | |
646 | ||
647 | q->debugfs_dir = debugfs_create_dir(name, block_debugfs_root); | |
648 | if (!q->debugfs_dir) | |
649 | goto err; | |
650 | ||
651 | if (blk_mq_debugfs_register_hctxs(q)) | |
652 | goto err; | |
653 | ||
654 | return 0; | |
655 | ||
656 | err: | |
657 | blk_mq_debugfs_unregister(q); | |
658 | return -ENOMEM; | |
659 | } | |
660 | ||
661 | void blk_mq_debugfs_unregister(struct request_queue *q) | |
662 | { | |
663 | debugfs_remove_recursive(q->debugfs_dir); | |
664 | q->mq_debugfs_dir = NULL; | |
665 | q->debugfs_dir = NULL; | |
666 | } | |
667 | ||
668 | static int blk_mq_debugfs_register_ctx(struct request_queue *q, | |
669 | struct blk_mq_ctx *ctx, | |
670 | struct dentry *hctx_dir) | |
671 | { | |
672 | struct dentry *ctx_dir; | |
673 | char name[20]; | |
674 | int i; | |
675 | ||
676 | snprintf(name, sizeof(name), "cpu%u", ctx->cpu); | |
677 | ctx_dir = debugfs_create_dir(name, hctx_dir); | |
678 | if (!ctx_dir) | |
679 | return -ENOMEM; | |
680 | ||
681 | for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_ctx_attrs); i++) { | |
682 | const struct blk_mq_debugfs_attr *attr; | |
683 | ||
684 | attr = &blk_mq_debugfs_ctx_attrs[i]; | |
685 | if (!debugfs_create_file(attr->name, attr->mode, ctx_dir, ctx, | |
686 | attr->fops)) | |
687 | return -ENOMEM; | |
688 | } | |
689 | ||
690 | return 0; | |
691 | } | |
692 | ||
693 | static int blk_mq_debugfs_register_hctx(struct request_queue *q, | |
694 | struct blk_mq_hw_ctx *hctx) | |
695 | { | |
696 | struct blk_mq_ctx *ctx; | |
697 | struct dentry *hctx_dir; | |
698 | char name[20]; | |
699 | int i; | |
700 | ||
701 | snprintf(name, sizeof(name), "%u", hctx->queue_num); | |
702 | hctx_dir = debugfs_create_dir(name, q->mq_debugfs_dir); | |
703 | if (!hctx_dir) | |
704 | return -ENOMEM; | |
705 | ||
706 | for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_hctx_attrs); i++) { | |
707 | const struct blk_mq_debugfs_attr *attr; | |
708 | ||
709 | attr = &blk_mq_debugfs_hctx_attrs[i]; | |
710 | if (!debugfs_create_file(attr->name, attr->mode, hctx_dir, hctx, | |
711 | attr->fops)) | |
712 | return -ENOMEM; | |
713 | } | |
714 | ||
715 | hctx_for_each_ctx(hctx, ctx, i) { | |
716 | if (blk_mq_debugfs_register_ctx(q, ctx, hctx_dir)) | |
717 | return -ENOMEM; | |
718 | } | |
719 | ||
720 | return 0; | |
721 | } | |
722 | ||
723 | int blk_mq_debugfs_register_hctxs(struct request_queue *q) | |
724 | { | |
725 | struct blk_mq_hw_ctx *hctx; | |
726 | int i; | |
727 | ||
728 | if (!q->debugfs_dir) | |
729 | return -ENOENT; | |
730 | ||
731 | q->mq_debugfs_dir = debugfs_create_dir("mq", q->debugfs_dir); | |
732 | if (!q->mq_debugfs_dir) | |
733 | goto err; | |
734 | ||
735 | queue_for_each_hw_ctx(q, hctx, i) { | |
736 | if (blk_mq_debugfs_register_hctx(q, hctx)) | |
737 | goto err; | |
738 | } | |
739 | ||
740 | return 0; | |
741 | ||
742 | err: | |
743 | blk_mq_debugfs_unregister_hctxs(q); | |
744 | return -ENOMEM; | |
745 | } | |
746 | ||
747 | void blk_mq_debugfs_unregister_hctxs(struct request_queue *q) | |
748 | { | |
749 | debugfs_remove_recursive(q->mq_debugfs_dir); | |
750 | q->mq_debugfs_dir = NULL; | |
751 | } | |
752 | ||
753 | void blk_mq_debugfs_init(void) | |
754 | { | |
755 | block_debugfs_root = debugfs_create_dir("block", NULL); | |
756 | } |