blob: 857bdadc6b1f367e36e4634c0709dae18392d1ba [file] [log] [blame]
Omar Sandoval07e4fea2017-01-25 08:06:40 -08001/*
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"
23
24struct blk_mq_debugfs_attr {
25 const char *name;
26 umode_t mode;
27 const struct file_operations *fops;
28};
29
30static struct dentry *block_debugfs_root;
31
Omar Sandoval950cd7e2017-01-25 08:06:42 -080032static int blk_mq_debugfs_seq_open(struct inode *inode, struct file *file,
33 const struct seq_operations *ops)
34{
35 struct seq_file *m;
36 int ret;
37
38 ret = seq_open(file, ops);
39 if (!ret) {
40 m = file->private_data;
41 m->private = inode->i_private;
42 }
43 return ret;
44}
45
Omar Sandoval9abb2ad2017-01-25 08:06:41 -080046static int hctx_state_show(struct seq_file *m, void *v)
47{
48 struct blk_mq_hw_ctx *hctx = m->private;
49
50 seq_printf(m, "0x%lx\n", hctx->state);
51 return 0;
52}
53
54static int hctx_state_open(struct inode *inode, struct file *file)
55{
56 return single_open(file, hctx_state_show, inode->i_private);
57}
58
59static const struct file_operations hctx_state_fops = {
60 .open = hctx_state_open,
61 .read = seq_read,
62 .llseek = seq_lseek,
63 .release = single_release,
64};
65
66static int hctx_flags_show(struct seq_file *m, void *v)
67{
68 struct blk_mq_hw_ctx *hctx = m->private;
69
70 seq_printf(m, "0x%lx\n", hctx->flags);
71 return 0;
72}
73
74static int hctx_flags_open(struct inode *inode, struct file *file)
75{
76 return single_open(file, hctx_flags_show, inode->i_private);
77}
78
79static const struct file_operations hctx_flags_fops = {
80 .open = hctx_flags_open,
81 .read = seq_read,
82 .llseek = seq_lseek,
83 .release = single_release,
84};
85
Omar Sandoval950cd7e2017-01-25 08:06:42 -080086static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
87{
88 struct request *rq = list_entry_rq(v);
89
Omar Sandoval7b393852017-01-25 08:06:43 -080090 seq_printf(m, "%p {.cmd_type=%u, .cmd_flags=0x%x, .rq_flags=0x%x, .tag=%d, .internal_tag=%d}\n",
91 rq, rq->cmd_type, rq->cmd_flags, (unsigned int)rq->rq_flags,
92 rq->tag, rq->internal_tag);
Omar Sandoval950cd7e2017-01-25 08:06:42 -080093 return 0;
94}
95
96static void *hctx_dispatch_start(struct seq_file *m, loff_t *pos)
97{
98 struct blk_mq_hw_ctx *hctx = m->private;
99
100 spin_lock(&hctx->lock);
101 return seq_list_start(&hctx->dispatch, *pos);
102}
103
104static void *hctx_dispatch_next(struct seq_file *m, void *v, loff_t *pos)
105{
106 struct blk_mq_hw_ctx *hctx = m->private;
107
108 return seq_list_next(v, &hctx->dispatch, pos);
109}
110
111static void hctx_dispatch_stop(struct seq_file *m, void *v)
112{
113 struct blk_mq_hw_ctx *hctx = m->private;
114
115 spin_unlock(&hctx->lock);
116}
117
118static const struct seq_operations hctx_dispatch_seq_ops = {
119 .start = hctx_dispatch_start,
120 .next = hctx_dispatch_next,
121 .stop = hctx_dispatch_stop,
122 .show = blk_mq_debugfs_rq_show,
123};
124
125static int hctx_dispatch_open(struct inode *inode, struct file *file)
126{
127 return blk_mq_debugfs_seq_open(inode, file, &hctx_dispatch_seq_ops);
128}
129
130static const struct file_operations hctx_dispatch_fops = {
131 .open = hctx_dispatch_open,
132 .read = seq_read,
133 .llseek = seq_lseek,
134 .release = seq_release,
135};
136
Omar Sandoval0bfa5282017-01-25 08:06:45 -0800137static int hctx_ctx_map_show(struct seq_file *m, void *v)
138{
139 struct blk_mq_hw_ctx *hctx = m->private;
140
141 sbitmap_bitmap_show(&hctx->ctx_map, m);
142 return 0;
143}
144
145static int hctx_ctx_map_open(struct inode *inode, struct file *file)
146{
147 return single_open(file, hctx_ctx_map_show, inode->i_private);
148}
149
150static const struct file_operations hctx_ctx_map_fops = {
151 .open = hctx_ctx_map_open,
152 .read = seq_read,
153 .llseek = seq_lseek,
154 .release = single_release,
155};
156
Omar Sandoval950cd7e2017-01-25 08:06:42 -0800157static void *ctx_rq_list_start(struct seq_file *m, loff_t *pos)
158{
159 struct blk_mq_ctx *ctx = m->private;
160
161 spin_lock(&ctx->lock);
162 return seq_list_start(&ctx->rq_list, *pos);
163}
164
165static void *ctx_rq_list_next(struct seq_file *m, void *v, loff_t *pos)
166{
167 struct blk_mq_ctx *ctx = m->private;
168
169 return seq_list_next(v, &ctx->rq_list, pos);
170}
171
172static void ctx_rq_list_stop(struct seq_file *m, void *v)
173{
174 struct blk_mq_ctx *ctx = m->private;
175
176 spin_unlock(&ctx->lock);
177}
178
179static const struct seq_operations ctx_rq_list_seq_ops = {
180 .start = ctx_rq_list_start,
181 .next = ctx_rq_list_next,
182 .stop = ctx_rq_list_stop,
183 .show = blk_mq_debugfs_rq_show,
184};
185
186static int ctx_rq_list_open(struct inode *inode, struct file *file)
187{
188 return blk_mq_debugfs_seq_open(inode, file, &ctx_rq_list_seq_ops);
189}
190
191static const struct file_operations ctx_rq_list_fops = {
192 .open = ctx_rq_list_open,
193 .read = seq_read,
194 .llseek = seq_lseek,
195 .release = seq_release,
196};
197
Omar Sandoval07e4fea2017-01-25 08:06:40 -0800198static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = {
Omar Sandoval9abb2ad2017-01-25 08:06:41 -0800199 {"state", 0400, &hctx_state_fops},
200 {"flags", 0400, &hctx_flags_fops},
Omar Sandoval950cd7e2017-01-25 08:06:42 -0800201 {"dispatch", 0400, &hctx_dispatch_fops},
Omar Sandoval0bfa5282017-01-25 08:06:45 -0800202 {"ctx_map", 0400, &hctx_ctx_map_fops},
Omar Sandoval07e4fea2017-01-25 08:06:40 -0800203};
204
205static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = {
Omar Sandoval950cd7e2017-01-25 08:06:42 -0800206 {"rq_list", 0400, &ctx_rq_list_fops},
Omar Sandoval07e4fea2017-01-25 08:06:40 -0800207};
208
209int blk_mq_debugfs_register(struct request_queue *q, const char *name)
210{
211 if (!block_debugfs_root)
212 return -ENOENT;
213
214 q->debugfs_dir = debugfs_create_dir(name, block_debugfs_root);
215 if (!q->debugfs_dir)
216 goto err;
217
218 if (blk_mq_debugfs_register_hctxs(q))
219 goto err;
220
221 return 0;
222
223err:
224 blk_mq_debugfs_unregister(q);
225 return -ENOMEM;
226}
227
228void blk_mq_debugfs_unregister(struct request_queue *q)
229{
230 debugfs_remove_recursive(q->debugfs_dir);
231 q->mq_debugfs_dir = NULL;
232 q->debugfs_dir = NULL;
233}
234
235static int blk_mq_debugfs_register_ctx(struct request_queue *q,
236 struct blk_mq_ctx *ctx,
237 struct dentry *hctx_dir)
238{
239 struct dentry *ctx_dir;
240 char name[20];
241 int i;
242
243 snprintf(name, sizeof(name), "cpu%u", ctx->cpu);
244 ctx_dir = debugfs_create_dir(name, hctx_dir);
245 if (!ctx_dir)
246 return -ENOMEM;
247
248 for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_ctx_attrs); i++) {
249 const struct blk_mq_debugfs_attr *attr;
250
251 attr = &blk_mq_debugfs_ctx_attrs[i];
252 if (!debugfs_create_file(attr->name, attr->mode, ctx_dir, ctx,
253 attr->fops))
254 return -ENOMEM;
255 }
256
257 return 0;
258}
259
260static int blk_mq_debugfs_register_hctx(struct request_queue *q,
261 struct blk_mq_hw_ctx *hctx)
262{
263 struct blk_mq_ctx *ctx;
264 struct dentry *hctx_dir;
265 char name[20];
266 int i;
267
268 snprintf(name, sizeof(name), "%u", hctx->queue_num);
269 hctx_dir = debugfs_create_dir(name, q->mq_debugfs_dir);
270 if (!hctx_dir)
271 return -ENOMEM;
272
273 for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_hctx_attrs); i++) {
274 const struct blk_mq_debugfs_attr *attr;
275
276 attr = &blk_mq_debugfs_hctx_attrs[i];
277 if (!debugfs_create_file(attr->name, attr->mode, hctx_dir, hctx,
278 attr->fops))
279 return -ENOMEM;
280 }
281
282 hctx_for_each_ctx(hctx, ctx, i) {
283 if (blk_mq_debugfs_register_ctx(q, ctx, hctx_dir))
284 return -ENOMEM;
285 }
286
287 return 0;
288}
289
290int blk_mq_debugfs_register_hctxs(struct request_queue *q)
291{
292 struct blk_mq_hw_ctx *hctx;
293 int i;
294
295 if (!q->debugfs_dir)
296 return -ENOENT;
297
298 q->mq_debugfs_dir = debugfs_create_dir("mq", q->debugfs_dir);
299 if (!q->mq_debugfs_dir)
300 goto err;
301
302 queue_for_each_hw_ctx(q, hctx, i) {
303 if (blk_mq_debugfs_register_hctx(q, hctx))
304 goto err;
305 }
306
307 return 0;
308
309err:
310 blk_mq_debugfs_unregister_hctxs(q);
311 return -ENOMEM;
312}
313
314void blk_mq_debugfs_unregister_hctxs(struct request_queue *q)
315{
316 debugfs_remove_recursive(q->mq_debugfs_dir);
317 q->mq_debugfs_dir = NULL;
318}
319
320void blk_mq_debugfs_init(void)
321{
322 block_debugfs_root = debugfs_create_dir("block", NULL);
323}