/* * Copyright (C) 2017 Facebook * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License v2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "blk-mq.h" struct blk_mq_debugfs_attr { const char *name; umode_t mode; const struct file_operations *fops; }; static struct dentry *block_debugfs_root; static int hctx_state_show(struct seq_file *m, void *v) { struct blk_mq_hw_ctx *hctx = m->private; seq_printf(m, "0x%lx\n", hctx->state); return 0; } static int hctx_state_open(struct inode *inode, struct file *file) { return single_open(file, hctx_state_show, inode->i_private); } static const struct file_operations hctx_state_fops = { .open = hctx_state_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int hctx_flags_show(struct seq_file *m, void *v) { struct blk_mq_hw_ctx *hctx = m->private; seq_printf(m, "0x%lx\n", hctx->flags); return 0; } static int hctx_flags_open(struct inode *inode, struct file *file) { return single_open(file, hctx_flags_show, inode->i_private); } static const struct file_operations hctx_flags_fops = { .open = hctx_flags_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = { {"state", 0400, &hctx_state_fops}, {"flags", 0400, &hctx_flags_fops}, }; static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = { }; int blk_mq_debugfs_register(struct request_queue *q, const char *name) { if (!block_debugfs_root) return -ENOENT; q->debugfs_dir = debugfs_create_dir(name, block_debugfs_root); if (!q->debugfs_dir) goto err; if (blk_mq_debugfs_register_hctxs(q)) goto err; return 0; err: blk_mq_debugfs_unregister(q); return -ENOMEM; } void blk_mq_debugfs_unregister(struct request_queue *q) { debugfs_remove_recursive(q->debugfs_dir); q->mq_debugfs_dir = NULL; q->debugfs_dir = NULL; } static int blk_mq_debugfs_register_ctx(struct request_queue *q, struct blk_mq_ctx *ctx, struct dentry *hctx_dir) { struct dentry *ctx_dir; char name[20]; int i; snprintf(name, sizeof(name), "cpu%u", ctx->cpu); ctx_dir = debugfs_create_dir(name, hctx_dir); if (!ctx_dir) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_ctx_attrs); i++) { const struct blk_mq_debugfs_attr *attr; attr = &blk_mq_debugfs_ctx_attrs[i]; if (!debugfs_create_file(attr->name, attr->mode, ctx_dir, ctx, attr->fops)) return -ENOMEM; } return 0; } static int blk_mq_debugfs_register_hctx(struct request_queue *q, struct blk_mq_hw_ctx *hctx) { struct blk_mq_ctx *ctx; struct dentry *hctx_dir; char name[20]; int i; snprintf(name, sizeof(name), "%u", hctx->queue_num); hctx_dir = debugfs_create_dir(name, q->mq_debugfs_dir); if (!hctx_dir) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(blk_mq_debugfs_hctx_attrs); i++) { const struct blk_mq_debugfs_attr *attr; attr = &blk_mq_debugfs_hctx_attrs[i]; if (!debugfs_create_file(attr->name, attr->mode, hctx_dir, hctx, attr->fops)) return -ENOMEM; } hctx_for_each_ctx(hctx, ctx, i) { if (blk_mq_debugfs_register_ctx(q, ctx, hctx_dir)) return -ENOMEM; } return 0; } int blk_mq_debugfs_register_hctxs(struct request_queue *q) { struct blk_mq_hw_ctx *hctx; int i; if (!q->debugfs_dir) return -ENOENT; q->mq_debugfs_dir = debugfs_create_dir("mq", q->debugfs_dir); if (!q->mq_debugfs_dir) goto err; queue_for_each_hw_ctx(q, hctx, i) { if (blk_mq_debugfs_register_hctx(q, hctx)) goto err; } return 0; err: blk_mq_debugfs_unregister_hctxs(q); return -ENOMEM; } void blk_mq_debugfs_unregister_hctxs(struct request_queue *q) { debugfs_remove_recursive(q->mq_debugfs_dir); q->mq_debugfs_dir = NULL; } void blk_mq_debugfs_init(void) { block_debugfs_root = debugfs_create_dir("block", NULL); }