2 * Copyright (C) 2017 Facebook
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.
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.
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/>.
17 #include <linux/kernel.h>
18 #include <linux/blkdev.h>
19 #include <linux/debugfs.h>
21 #include <linux/blk-mq.h>
24 struct blk_mq_debugfs_attr
{
27 const struct file_operations
*fops
;
30 static struct dentry
*block_debugfs_root
;
32 static int blk_mq_debugfs_seq_open(struct inode
*inode
, struct file
*file
,
33 const struct seq_operations
*ops
)
38 ret
= seq_open(file
, ops
);
40 m
= file
->private_data
;
41 m
->private = inode
->i_private
;
46 static int hctx_state_show(struct seq_file
*m
, void *v
)
48 struct blk_mq_hw_ctx
*hctx
= m
->private;
50 seq_printf(m
, "0x%lx\n", hctx
->state
);
54 static int hctx_state_open(struct inode
*inode
, struct file
*file
)
56 return single_open(file
, hctx_state_show
, inode
->i_private
);
59 static const struct file_operations hctx_state_fops
= {
60 .open
= hctx_state_open
,
63 .release
= single_release
,
66 static int hctx_flags_show(struct seq_file
*m
, void *v
)
68 struct blk_mq_hw_ctx
*hctx
= m
->private;
70 seq_printf(m
, "0x%lx\n", hctx
->flags
);
74 static int hctx_flags_open(struct inode
*inode
, struct file
*file
)
76 return single_open(file
, hctx_flags_show
, inode
->i_private
);
79 static const struct file_operations hctx_flags_fops
= {
80 .open
= hctx_flags_open
,
83 .release
= single_release
,
86 static int blk_mq_debugfs_rq_show(struct seq_file
*m
, void *v
)
88 struct request
*rq
= list_entry_rq(v
);
90 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
);
96 static void *hctx_dispatch_start(struct seq_file
*m
, loff_t
*pos
)
98 struct blk_mq_hw_ctx
*hctx
= m
->private;
100 spin_lock(&hctx
->lock
);
101 return seq_list_start(&hctx
->dispatch
, *pos
);
104 static void *hctx_dispatch_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
106 struct blk_mq_hw_ctx
*hctx
= m
->private;
108 return seq_list_next(v
, &hctx
->dispatch
, pos
);
111 static void hctx_dispatch_stop(struct seq_file
*m
, void *v
)
113 struct blk_mq_hw_ctx
*hctx
= m
->private;
115 spin_unlock(&hctx
->lock
);
118 static 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
,
125 static int hctx_dispatch_open(struct inode
*inode
, struct file
*file
)
127 return blk_mq_debugfs_seq_open(inode
, file
, &hctx_dispatch_seq_ops
);
130 static const struct file_operations hctx_dispatch_fops
= {
131 .open
= hctx_dispatch_open
,
134 .release
= seq_release
,
137 static void *ctx_rq_list_start(struct seq_file
*m
, loff_t
*pos
)
139 struct blk_mq_ctx
*ctx
= m
->private;
141 spin_lock(&ctx
->lock
);
142 return seq_list_start(&ctx
->rq_list
, *pos
);
145 static void *ctx_rq_list_next(struct seq_file
*m
, void *v
, loff_t
*pos
)
147 struct blk_mq_ctx
*ctx
= m
->private;
149 return seq_list_next(v
, &ctx
->rq_list
, pos
);
152 static void ctx_rq_list_stop(struct seq_file
*m
, void *v
)
154 struct blk_mq_ctx
*ctx
= m
->private;
156 spin_unlock(&ctx
->lock
);
159 static const struct seq_operations ctx_rq_list_seq_ops
= {
160 .start
= ctx_rq_list_start
,
161 .next
= ctx_rq_list_next
,
162 .stop
= ctx_rq_list_stop
,
163 .show
= blk_mq_debugfs_rq_show
,
166 static int ctx_rq_list_open(struct inode
*inode
, struct file
*file
)
168 return blk_mq_debugfs_seq_open(inode
, file
, &ctx_rq_list_seq_ops
);
171 static const struct file_operations ctx_rq_list_fops
= {
172 .open
= ctx_rq_list_open
,
175 .release
= seq_release
,
178 static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs
[] = {
179 {"state", 0400, &hctx_state_fops
},
180 {"flags", 0400, &hctx_flags_fops
},
181 {"dispatch", 0400, &hctx_dispatch_fops
},
184 static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs
[] = {
185 {"rq_list", 0400, &ctx_rq_list_fops
},
188 int blk_mq_debugfs_register(struct request_queue
*q
, const char *name
)
190 if (!block_debugfs_root
)
193 q
->debugfs_dir
= debugfs_create_dir(name
, block_debugfs_root
);
197 if (blk_mq_debugfs_register_hctxs(q
))
203 blk_mq_debugfs_unregister(q
);
207 void blk_mq_debugfs_unregister(struct request_queue
*q
)
209 debugfs_remove_recursive(q
->debugfs_dir
);
210 q
->mq_debugfs_dir
= NULL
;
211 q
->debugfs_dir
= NULL
;
214 static int blk_mq_debugfs_register_ctx(struct request_queue
*q
,
215 struct blk_mq_ctx
*ctx
,
216 struct dentry
*hctx_dir
)
218 struct dentry
*ctx_dir
;
222 snprintf(name
, sizeof(name
), "cpu%u", ctx
->cpu
);
223 ctx_dir
= debugfs_create_dir(name
, hctx_dir
);
227 for (i
= 0; i
< ARRAY_SIZE(blk_mq_debugfs_ctx_attrs
); i
++) {
228 const struct blk_mq_debugfs_attr
*attr
;
230 attr
= &blk_mq_debugfs_ctx_attrs
[i
];
231 if (!debugfs_create_file(attr
->name
, attr
->mode
, ctx_dir
, ctx
,
239 static int blk_mq_debugfs_register_hctx(struct request_queue
*q
,
240 struct blk_mq_hw_ctx
*hctx
)
242 struct blk_mq_ctx
*ctx
;
243 struct dentry
*hctx_dir
;
247 snprintf(name
, sizeof(name
), "%u", hctx
->queue_num
);
248 hctx_dir
= debugfs_create_dir(name
, q
->mq_debugfs_dir
);
252 for (i
= 0; i
< ARRAY_SIZE(blk_mq_debugfs_hctx_attrs
); i
++) {
253 const struct blk_mq_debugfs_attr
*attr
;
255 attr
= &blk_mq_debugfs_hctx_attrs
[i
];
256 if (!debugfs_create_file(attr
->name
, attr
->mode
, hctx_dir
, hctx
,
261 hctx_for_each_ctx(hctx
, ctx
, i
) {
262 if (blk_mq_debugfs_register_ctx(q
, ctx
, hctx_dir
))
269 int blk_mq_debugfs_register_hctxs(struct request_queue
*q
)
271 struct blk_mq_hw_ctx
*hctx
;
277 q
->mq_debugfs_dir
= debugfs_create_dir("mq", q
->debugfs_dir
);
278 if (!q
->mq_debugfs_dir
)
281 queue_for_each_hw_ctx(q
, hctx
, i
) {
282 if (blk_mq_debugfs_register_hctx(q
, hctx
))
289 blk_mq_debugfs_unregister_hctxs(q
);
293 void blk_mq_debugfs_unregister_hctxs(struct request_queue
*q
)
295 debugfs_remove_recursive(q
->mq_debugfs_dir
);
296 q
->mq_debugfs_dir
= NULL
;
299 void blk_mq_debugfs_init(void)
301 block_debugfs_root
= debugfs_create_dir("block", NULL
);