]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - block/blk-mq-debugfs.c
blk-mq: mark blk_mq_rq_ctx_init static
[karo-tx-linux.git] / block / blk-mq-debugfs.c
index bcd2a7d4a3a52fc23f26ee2e119a110579dadde6..9edebbdce0bdfc22ee9ba8319bb3cc58923bbfa7 100644 (file)
 #include <linux/blk-mq.h>
 #include "blk.h"
 #include "blk-mq.h"
+#include "blk-mq-debugfs.h"
 #include "blk-mq-tag.h"
 
-struct blk_mq_debugfs_attr {
-       const char *name;
-       umode_t mode;
-       const struct file_operations *fops;
-};
-
-static int blk_mq_debugfs_seq_open(struct inode *inode, struct file *file,
-                                  const struct seq_operations *ops)
-{
-       struct seq_file *m;
-       int ret;
-
-       ret = seq_open(file, ops);
-       if (!ret) {
-               m = file->private_data;
-               m->private = inode->i_private;
-       }
-       return ret;
-}
-
 static int blk_flags_show(struct seq_file *m, const unsigned long flags,
                          const char *const *flag_name, int flag_name_count)
 {
@@ -53,7 +34,7 @@ static int blk_flags_show(struct seq_file *m, const unsigned long flags,
                if (!(flags & BIT(i)))
                        continue;
                if (sep)
-                       seq_puts(m, " ");
+                       seq_puts(m, "|");
                sep = true;
                if (i < flag_name_count && flag_name[i])
                        seq_puts(m, flag_name[i]);
@@ -63,41 +44,43 @@ static int blk_flags_show(struct seq_file *m, const unsigned long flags,
        return 0;
 }
 
+#define QUEUE_FLAG_NAME(name) [QUEUE_FLAG_##name] = #name
 static const char *const blk_queue_flag_name[] = {
-       [QUEUE_FLAG_QUEUED]      = "QUEUED",
-       [QUEUE_FLAG_STOPPED]     = "STOPPED",
-       [QUEUE_FLAG_SYNCFULL]    = "SYNCFULL",
-       [QUEUE_FLAG_ASYNCFULL]   = "ASYNCFULL",
-       [QUEUE_FLAG_DYING]       = "DYING",
-       [QUEUE_FLAG_BYPASS]      = "BYPASS",
-       [QUEUE_FLAG_BIDI]        = "BIDI",
-       [QUEUE_FLAG_NOMERGES]    = "NOMERGES",
-       [QUEUE_FLAG_SAME_COMP]   = "SAME_COMP",
-       [QUEUE_FLAG_FAIL_IO]     = "FAIL_IO",
-       [QUEUE_FLAG_STACKABLE]   = "STACKABLE",
-       [QUEUE_FLAG_NONROT]      = "NONROT",
-       [QUEUE_FLAG_IO_STAT]     = "IO_STAT",
-       [QUEUE_FLAG_DISCARD]     = "DISCARD",
-       [QUEUE_FLAG_NOXMERGES]   = "NOXMERGES",
-       [QUEUE_FLAG_ADD_RANDOM]  = "ADD_RANDOM",
-       [QUEUE_FLAG_SECERASE]    = "SECERASE",
-       [QUEUE_FLAG_SAME_FORCE]  = "SAME_FORCE",
-       [QUEUE_FLAG_DEAD]        = "DEAD",
-       [QUEUE_FLAG_INIT_DONE]   = "INIT_DONE",
-       [QUEUE_FLAG_NO_SG_MERGE] = "NO_SG_MERGE",
-       [QUEUE_FLAG_POLL]        = "POLL",
-       [QUEUE_FLAG_WC]          = "WC",
-       [QUEUE_FLAG_FUA]         = "FUA",
-       [QUEUE_FLAG_FLUSH_NQ]    = "FLUSH_NQ",
-       [QUEUE_FLAG_DAX]         = "DAX",
-       [QUEUE_FLAG_STATS]       = "STATS",
-       [QUEUE_FLAG_POLL_STATS]  = "POLL_STATS",
-       [QUEUE_FLAG_REGISTERED]  = "REGISTERED",
-};
-
-static int blk_queue_flags_show(struct seq_file *m, void *v)
-{
-       struct request_queue *q = m->private;
+       QUEUE_FLAG_NAME(QUEUED),
+       QUEUE_FLAG_NAME(STOPPED),
+       QUEUE_FLAG_NAME(SYNCFULL),
+       QUEUE_FLAG_NAME(ASYNCFULL),
+       QUEUE_FLAG_NAME(DYING),
+       QUEUE_FLAG_NAME(BYPASS),
+       QUEUE_FLAG_NAME(BIDI),
+       QUEUE_FLAG_NAME(NOMERGES),
+       QUEUE_FLAG_NAME(SAME_COMP),
+       QUEUE_FLAG_NAME(FAIL_IO),
+       QUEUE_FLAG_NAME(STACKABLE),
+       QUEUE_FLAG_NAME(NONROT),
+       QUEUE_FLAG_NAME(IO_STAT),
+       QUEUE_FLAG_NAME(DISCARD),
+       QUEUE_FLAG_NAME(NOXMERGES),
+       QUEUE_FLAG_NAME(ADD_RANDOM),
+       QUEUE_FLAG_NAME(SECERASE),
+       QUEUE_FLAG_NAME(SAME_FORCE),
+       QUEUE_FLAG_NAME(DEAD),
+       QUEUE_FLAG_NAME(INIT_DONE),
+       QUEUE_FLAG_NAME(NO_SG_MERGE),
+       QUEUE_FLAG_NAME(POLL),
+       QUEUE_FLAG_NAME(WC),
+       QUEUE_FLAG_NAME(FUA),
+       QUEUE_FLAG_NAME(FLUSH_NQ),
+       QUEUE_FLAG_NAME(DAX),
+       QUEUE_FLAG_NAME(STATS),
+       QUEUE_FLAG_NAME(POLL_STATS),
+       QUEUE_FLAG_NAME(REGISTERED),
+};
+#undef QUEUE_FLAG_NAME
+
+static int queue_state_show(void *data, struct seq_file *m)
+{
+       struct request_queue *q = data;
 
        blk_flags_show(m, q->queue_flags, blk_queue_flag_name,
                       ARRAY_SIZE(blk_queue_flag_name));
@@ -105,42 +88,43 @@ static int blk_queue_flags_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static ssize_t blk_queue_flags_store(struct file *file, const char __user *ubuf,
-                                    size_t len, loff_t *offp)
+static ssize_t queue_state_write(void *data, const char __user *buf,
+                                size_t count, loff_t *ppos)
 {
-       struct request_queue *q = file_inode(file)->i_private;
-       char op[16] = { }, *s;
+       struct request_queue *q = data;
+       char opbuf[16] = { }, *op;
+
+       /*
+        * The "state" attribute is removed after blk_cleanup_queue() has called
+        * blk_mq_free_queue(). Return if QUEUE_FLAG_DEAD has been set to avoid
+        * triggering a use-after-free.
+        */
+       if (blk_queue_dead(q))
+               return -ENOENT;
+
+       if (count >= sizeof(opbuf)) {
+               pr_err("%s: operation too long\n", __func__);
+               goto inval;
+       }
 
-       len = min(len, sizeof(op) - 1);
-       if (copy_from_user(op, ubuf, len))
+       if (copy_from_user(opbuf, buf, count))
                return -EFAULT;
-       s = op;
-       strsep(&s, " \t\n"); /* strip trailing whitespace */
+       op = strstrip(opbuf);
        if (strcmp(op, "run") == 0) {
                blk_mq_run_hw_queues(q, true);
        } else if (strcmp(op, "start") == 0) {
                blk_mq_start_stopped_hw_queues(q, true);
+       } else if (strcmp(op, "kick") == 0) {
+               blk_mq_kick_requeue_list(q);
        } else {
-               pr_err("%s: unsupported operation %s. Use either 'run' or 'start'\n",
-                      __func__, op);
+               pr_err("%s: unsupported operation '%s'\n", __func__, op);
+inval:
+               pr_err("%s: use 'run', 'start' or 'kick'\n", __func__);
                return -EINVAL;
        }
-       return len;
-}
-
-static int blk_queue_flags_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, blk_queue_flags_show, inode->i_private);
+       return count;
 }
 
-static const struct file_operations blk_queue_flags_fops = {
-       .open           = blk_queue_flags_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-       .write          = blk_queue_flags_store,
-};
-
 static void print_stat(struct seq_file *m, struct blk_rq_stat *stat)
 {
        if (stat->nr_samples) {
@@ -151,9 +135,9 @@ static void print_stat(struct seq_file *m, struct blk_rq_stat *stat)
        }
 }
 
-static int queue_poll_stat_show(struct seq_file *m, void *v)
+static int queue_poll_stat_show(void *data, struct seq_file *m)
 {
-       struct request_queue *q = m->private;
+       struct request_queue *q = data;
        int bucket;
 
        for (bucket = 0; bucket < BLK_MQ_POLL_STATS_BKTS/2; bucket++) {
@@ -168,28 +152,19 @@ static int queue_poll_stat_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static int queue_poll_stat_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, queue_poll_stat_show, inode->i_private);
-}
-
-static const struct file_operations queue_poll_stat_fops = {
-       .open           = queue_poll_stat_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
+#define HCTX_STATE_NAME(name) [BLK_MQ_S_##name] = #name
 static const char *const hctx_state_name[] = {
-       [BLK_MQ_S_STOPPED]       = "STOPPED",
-       [BLK_MQ_S_TAG_ACTIVE]    = "TAG_ACTIVE",
-       [BLK_MQ_S_SCHED_RESTART] = "SCHED_RESTART",
-       [BLK_MQ_S_TAG_WAITING]   = "TAG_WAITING",
-
+       HCTX_STATE_NAME(STOPPED),
+       HCTX_STATE_NAME(TAG_ACTIVE),
+       HCTX_STATE_NAME(SCHED_RESTART),
+       HCTX_STATE_NAME(TAG_WAITING),
+       HCTX_STATE_NAME(START_ON_RUN),
 };
-static int hctx_state_show(struct seq_file *m, void *v)
+#undef HCTX_STATE_NAME
+
+static int hctx_state_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        blk_flags_show(m, hctx->state, hctx_state_name,
                       ARRAY_SIZE(hctx_state_name));
@@ -197,34 +172,26 @@ static int hctx_state_show(struct seq_file *m, void *v)
        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,
-};
-
+#define BLK_TAG_ALLOC_NAME(name) [BLK_TAG_ALLOC_##name] = #name
 static const char *const alloc_policy_name[] = {
-       [BLK_TAG_ALLOC_FIFO]    = "fifo",
-       [BLK_TAG_ALLOC_RR]      = "rr",
+       BLK_TAG_ALLOC_NAME(FIFO),
+       BLK_TAG_ALLOC_NAME(RR),
 };
+#undef BLK_TAG_ALLOC_NAME
 
+#define HCTX_FLAG_NAME(name) [ilog2(BLK_MQ_F_##name)] = #name
 static const char *const hctx_flag_name[] = {
-       [ilog2(BLK_MQ_F_SHOULD_MERGE)]  = "SHOULD_MERGE",
-       [ilog2(BLK_MQ_F_TAG_SHARED)]    = "TAG_SHARED",
-       [ilog2(BLK_MQ_F_SG_MERGE)]      = "SG_MERGE",
-       [ilog2(BLK_MQ_F_BLOCKING)]      = "BLOCKING",
-       [ilog2(BLK_MQ_F_NO_SCHED)]      = "NO_SCHED",
+       HCTX_FLAG_NAME(SHOULD_MERGE),
+       HCTX_FLAG_NAME(TAG_SHARED),
+       HCTX_FLAG_NAME(SG_MERGE),
+       HCTX_FLAG_NAME(BLOCKING),
+       HCTX_FLAG_NAME(NO_SCHED),
 };
+#undef HCTX_FLAG_NAME
 
-static int hctx_flags_show(struct seq_file *m, void *v)
+static int hctx_flags_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        const int alloc_policy = BLK_MQ_FLAG_TO_ALLOC_POLICY(hctx->flags);
 
        seq_puts(m, "alloc_policy=");
@@ -241,76 +208,77 @@ static int hctx_flags_show(struct seq_file *m, void *v)
        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,
-};
-
+#define REQ_OP_NAME(name) [REQ_OP_##name] = #name
 static const char *const op_name[] = {
-       [REQ_OP_READ]           = "READ",
-       [REQ_OP_WRITE]          = "WRITE",
-       [REQ_OP_FLUSH]          = "FLUSH",
-       [REQ_OP_DISCARD]        = "DISCARD",
-       [REQ_OP_ZONE_REPORT]    = "ZONE_REPORT",
-       [REQ_OP_SECURE_ERASE]   = "SECURE_ERASE",
-       [REQ_OP_ZONE_RESET]     = "ZONE_RESET",
-       [REQ_OP_WRITE_SAME]     = "WRITE_SAME",
-       [REQ_OP_WRITE_ZEROES]   = "WRITE_ZEROES",
-       [REQ_OP_SCSI_IN]        = "SCSI_IN",
-       [REQ_OP_SCSI_OUT]       = "SCSI_OUT",
-       [REQ_OP_DRV_IN]         = "DRV_IN",
-       [REQ_OP_DRV_OUT]        = "DRV_OUT",
-};
-
+       REQ_OP_NAME(READ),
+       REQ_OP_NAME(WRITE),
+       REQ_OP_NAME(FLUSH),
+       REQ_OP_NAME(DISCARD),
+       REQ_OP_NAME(ZONE_REPORT),
+       REQ_OP_NAME(SECURE_ERASE),
+       REQ_OP_NAME(ZONE_RESET),
+       REQ_OP_NAME(WRITE_SAME),
+       REQ_OP_NAME(WRITE_ZEROES),
+       REQ_OP_NAME(SCSI_IN),
+       REQ_OP_NAME(SCSI_OUT),
+       REQ_OP_NAME(DRV_IN),
+       REQ_OP_NAME(DRV_OUT),
+};
+#undef REQ_OP_NAME
+
+#define CMD_FLAG_NAME(name) [__REQ_##name] = #name
 static const char *const cmd_flag_name[] = {
-       [__REQ_FAILFAST_DEV]            = "FAILFAST_DEV",
-       [__REQ_FAILFAST_TRANSPORT]      = "FAILFAST_TRANSPORT",
-       [__REQ_FAILFAST_DRIVER]         = "FAILFAST_DRIVER",
-       [__REQ_SYNC]                    = "SYNC",
-       [__REQ_META]                    = "META",
-       [__REQ_PRIO]                    = "PRIO",
-       [__REQ_NOMERGE]                 = "NOMERGE",
-       [__REQ_IDLE]                    = "IDLE",
-       [__REQ_INTEGRITY]               = "INTEGRITY",
-       [__REQ_FUA]                     = "FUA",
-       [__REQ_PREFLUSH]                = "PREFLUSH",
-       [__REQ_RAHEAD]                  = "RAHEAD",
-       [__REQ_BACKGROUND]              = "BACKGROUND",
-       [__REQ_NR_BITS]                 = "NR_BITS",
-};
-
+       CMD_FLAG_NAME(FAILFAST_DEV),
+       CMD_FLAG_NAME(FAILFAST_TRANSPORT),
+       CMD_FLAG_NAME(FAILFAST_DRIVER),
+       CMD_FLAG_NAME(SYNC),
+       CMD_FLAG_NAME(META),
+       CMD_FLAG_NAME(PRIO),
+       CMD_FLAG_NAME(NOMERGE),
+       CMD_FLAG_NAME(IDLE),
+       CMD_FLAG_NAME(INTEGRITY),
+       CMD_FLAG_NAME(FUA),
+       CMD_FLAG_NAME(PREFLUSH),
+       CMD_FLAG_NAME(RAHEAD),
+       CMD_FLAG_NAME(BACKGROUND),
+       CMD_FLAG_NAME(NOUNMAP),
+};
+#undef CMD_FLAG_NAME
+
+#define RQF_NAME(name) [ilog2((__force u32)RQF_##name)] = #name
 static const char *const rqf_name[] = {
-       [ilog2((__force u32)RQF_SORTED)]                = "SORTED",
-       [ilog2((__force u32)RQF_STARTED)]               = "STARTED",
-       [ilog2((__force u32)RQF_QUEUED)]                = "QUEUED",
-       [ilog2((__force u32)RQF_SOFTBARRIER)]           = "SOFTBARRIER",
-       [ilog2((__force u32)RQF_FLUSH_SEQ)]             = "FLUSH_SEQ",
-       [ilog2((__force u32)RQF_MIXED_MERGE)]           = "MIXED_MERGE",
-       [ilog2((__force u32)RQF_MQ_INFLIGHT)]           = "MQ_INFLIGHT",
-       [ilog2((__force u32)RQF_DONTPREP)]              = "DONTPREP",
-       [ilog2((__force u32)RQF_PREEMPT)]               = "PREEMPT",
-       [ilog2((__force u32)RQF_COPY_USER)]             = "COPY_USER",
-       [ilog2((__force u32)RQF_FAILED)]                = "FAILED",
-       [ilog2((__force u32)RQF_QUIET)]                 = "QUIET",
-       [ilog2((__force u32)RQF_ELVPRIV)]               = "ELVPRIV",
-       [ilog2((__force u32)RQF_IO_STAT)]               = "IO_STAT",
-       [ilog2((__force u32)RQF_ALLOCED)]               = "ALLOCED",
-       [ilog2((__force u32)RQF_PM)]                    = "PM",
-       [ilog2((__force u32)RQF_HASHED)]                = "HASHED",
-       [ilog2((__force u32)RQF_STATS)]                 = "STATS",
-       [ilog2((__force u32)RQF_SPECIAL_PAYLOAD)]       = "SPECIAL_PAYLOAD",
-};
-
-static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
+       RQF_NAME(SORTED),
+       RQF_NAME(STARTED),
+       RQF_NAME(QUEUED),
+       RQF_NAME(SOFTBARRIER),
+       RQF_NAME(FLUSH_SEQ),
+       RQF_NAME(MIXED_MERGE),
+       RQF_NAME(MQ_INFLIGHT),
+       RQF_NAME(DONTPREP),
+       RQF_NAME(PREEMPT),
+       RQF_NAME(COPY_USER),
+       RQF_NAME(FAILED),
+       RQF_NAME(QUIET),
+       RQF_NAME(ELVPRIV),
+       RQF_NAME(IO_STAT),
+       RQF_NAME(ALLOCED),
+       RQF_NAME(PM),
+       RQF_NAME(HASHED),
+       RQF_NAME(STATS),
+       RQF_NAME(SPECIAL_PAYLOAD),
+};
+#undef RQF_NAME
+
+#define RQAF_NAME(name) [REQ_ATOM_##name] = #name
+static const char *const rqaf_name[] = {
+       RQAF_NAME(COMPLETE),
+       RQAF_NAME(STARTED),
+       RQAF_NAME(POLL_SLEPT),
+};
+#undef RQAF_NAME
+
+int __blk_mq_debugfs_rq_show(struct seq_file *m, struct request *rq)
 {
-       struct request *rq = list_entry_rq(v);
        const struct blk_mq_ops *const mq_ops = rq->q->mq_ops;
        const unsigned int op = rq->cmd_flags & REQ_OP_MASK;
 
@@ -325,6 +293,8 @@ static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
        seq_puts(m, ", .rq_flags=");
        blk_flags_show(m, (__force unsigned int)rq->rq_flags, rqf_name,
                       ARRAY_SIZE(rqf_name));
+       seq_puts(m, ", .atomic_flags=");
+       blk_flags_show(m, rq->atomic_flags, rqaf_name, ARRAY_SIZE(rqaf_name));
        seq_printf(m, ", .tag=%d, .internal_tag=%d", rq->tag,
                   rq->internal_tag);
        if (mq_ops->show_rq)
@@ -332,6 +302,44 @@ static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
        seq_puts(m, "}\n");
        return 0;
 }
+EXPORT_SYMBOL_GPL(__blk_mq_debugfs_rq_show);
+
+int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
+{
+       return __blk_mq_debugfs_rq_show(m, list_entry_rq(v));
+}
+EXPORT_SYMBOL_GPL(blk_mq_debugfs_rq_show);
+
+static void *queue_requeue_list_start(struct seq_file *m, loff_t *pos)
+       __acquires(&q->requeue_lock)
+{
+       struct request_queue *q = m->private;
+
+       spin_lock_irq(&q->requeue_lock);
+       return seq_list_start(&q->requeue_list, *pos);
+}
+
+static void *queue_requeue_list_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       struct request_queue *q = m->private;
+
+       return seq_list_next(v, &q->requeue_list, pos);
+}
+
+static void queue_requeue_list_stop(struct seq_file *m, void *v)
+       __releases(&q->requeue_lock)
+{
+       struct request_queue *q = m->private;
+
+       spin_unlock_irq(&q->requeue_lock);
+}
+
+static const struct seq_operations queue_requeue_list_seq_ops = {
+       .start  = queue_requeue_list_start,
+       .next   = queue_requeue_list_next,
+       .stop   = queue_requeue_list_stop,
+       .show   = blk_mq_debugfs_rq_show,
+};
 
 static void *hctx_dispatch_start(struct seq_file *m, loff_t *pos)
        __acquires(&hctx->lock)
@@ -364,37 +372,43 @@ static const struct seq_operations hctx_dispatch_seq_ops = {
        .show   = blk_mq_debugfs_rq_show,
 };
 
-static int hctx_dispatch_open(struct inode *inode, struct file *file)
+struct show_busy_params {
+       struct seq_file         *m;
+       struct blk_mq_hw_ctx    *hctx;
+};
+
+/*
+ * Note: the state of a request may change while this function is in progress,
+ * e.g. due to a concurrent blk_mq_finish_request() call.
+ */
+static void hctx_show_busy_rq(struct request *rq, void *data, bool reserved)
 {
-       return blk_mq_debugfs_seq_open(inode, file, &hctx_dispatch_seq_ops);
-}
+       const struct show_busy_params *params = data;
 
-static const struct file_operations hctx_dispatch_fops = {
-       .open           = hctx_dispatch_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
+       if (blk_mq_map_queue(rq->q, rq->mq_ctx->cpu) == params->hctx &&
+           test_bit(REQ_ATOM_STARTED, &rq->atomic_flags))
+               __blk_mq_debugfs_rq_show(params->m,
+                                        list_entry_rq(&rq->queuelist));
+}
 
-static int hctx_ctx_map_show(struct seq_file *m, void *v)
+static int hctx_busy_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
+       struct show_busy_params params = { .m = m, .hctx = hctx };
+
+       blk_mq_tagset_busy_iter(hctx->queue->tag_set, hctx_show_busy_rq,
+                               &params);
 
-       sbitmap_bitmap_show(&hctx->ctx_map, m);
        return 0;
 }
 
-static int hctx_ctx_map_open(struct inode *inode, struct file *file)
+static int hctx_ctx_map_show(void *data, struct seq_file *m)
 {
-       return single_open(file, hctx_ctx_map_show, inode->i_private);
-}
+       struct blk_mq_hw_ctx *hctx = data;
 
-static const struct file_operations hctx_ctx_map_fops = {
-       .open           = hctx_ctx_map_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+       sbitmap_bitmap_show(&hctx->ctx_map, m);
+       return 0;
+}
 
 static void blk_mq_debugfs_tags_show(struct seq_file *m,
                                     struct blk_mq_tags *tags)
@@ -413,9 +427,9 @@ static void blk_mq_debugfs_tags_show(struct seq_file *m,
        }
 }
 
-static int hctx_tags_show(struct seq_file *m, void *v)
+static int hctx_tags_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        struct request_queue *q = hctx->queue;
        int res;
 
@@ -430,21 +444,9 @@ out:
        return res;
 }
 
-static int hctx_tags_open(struct inode *inode, struct file *file)
+static int hctx_tags_bitmap_show(void *data, struct seq_file *m)
 {
-       return single_open(file, hctx_tags_show, inode->i_private);
-}
-
-static const struct file_operations hctx_tags_fops = {
-       .open           = hctx_tags_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_tags_bitmap_show(struct seq_file *m, void *v)
-{
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        struct request_queue *q = hctx->queue;
        int res;
 
@@ -459,21 +461,9 @@ out:
        return res;
 }
 
-static int hctx_tags_bitmap_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_tags_bitmap_show, inode->i_private);
-}
-
-static const struct file_operations hctx_tags_bitmap_fops = {
-       .open           = hctx_tags_bitmap_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_sched_tags_show(struct seq_file *m, void *v)
+static int hctx_sched_tags_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        struct request_queue *q = hctx->queue;
        int res;
 
@@ -488,21 +478,9 @@ out:
        return res;
 }
 
-static int hctx_sched_tags_open(struct inode *inode, struct file *file)
+static int hctx_sched_tags_bitmap_show(void *data, struct seq_file *m)
 {
-       return single_open(file, hctx_sched_tags_show, inode->i_private);
-}
-
-static const struct file_operations hctx_sched_tags_fops = {
-       .open           = hctx_sched_tags_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_sched_tags_bitmap_show(struct seq_file *m, void *v)
-{
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        struct request_queue *q = hctx->queue;
        int res;
 
@@ -517,21 +495,9 @@ out:
        return res;
 }
 
-static int hctx_sched_tags_bitmap_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_sched_tags_bitmap_show, inode->i_private);
-}
-
-static const struct file_operations hctx_sched_tags_bitmap_fops = {
-       .open           = hctx_sched_tags_bitmap_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_io_poll_show(struct seq_file *m, void *v)
+static int hctx_io_poll_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        seq_printf(m, "considered=%lu\n", hctx->poll_considered);
        seq_printf(m, "invoked=%lu\n", hctx->poll_invoked);
@@ -539,32 +505,18 @@ static int hctx_io_poll_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static int hctx_io_poll_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_io_poll_show, inode->i_private);
-}
-
-static ssize_t hctx_io_poll_write(struct file *file, const char __user *buf,
+static ssize_t hctx_io_poll_write(void *data, const char __user *buf,
                                  size_t count, loff_t *ppos)
 {
-       struct seq_file *m = file->private_data;
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        hctx->poll_considered = hctx->poll_invoked = hctx->poll_success = 0;
        return count;
 }
 
-static const struct file_operations hctx_io_poll_fops = {
-       .open           = hctx_io_poll_open,
-       .read           = seq_read,
-       .write          = hctx_io_poll_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_dispatched_show(struct seq_file *m, void *v)
+static int hctx_dispatched_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        int i;
 
        seq_printf(m, "%8u\t%lu\n", 0U, hctx->dispatched[0]);
@@ -579,16 +531,10 @@ static int hctx_dispatched_show(struct seq_file *m, void *v)
        return 0;
 }
 
-static int hctx_dispatched_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_dispatched_show, inode->i_private);
-}
-
-static ssize_t hctx_dispatched_write(struct file *file, const char __user *buf,
+static ssize_t hctx_dispatched_write(void *data, const char __user *buf,
                                     size_t count, loff_t *ppos)
 {
-       struct seq_file *m = file->private_data;
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
        int i;
 
        for (i = 0; i < BLK_MQ_MAX_DISPATCH_ORDER; i++)
@@ -596,96 +542,48 @@ static ssize_t hctx_dispatched_write(struct file *file, const char __user *buf,
        return count;
 }
 
-static const struct file_operations hctx_dispatched_fops = {
-       .open           = hctx_dispatched_open,
-       .read           = seq_read,
-       .write          = hctx_dispatched_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_queued_show(struct seq_file *m, void *v)
+static int hctx_queued_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        seq_printf(m, "%lu\n", hctx->queued);
        return 0;
 }
 
-static int hctx_queued_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_queued_show, inode->i_private);
-}
-
-static ssize_t hctx_queued_write(struct file *file, const char __user *buf,
+static ssize_t hctx_queued_write(void *data, const char __user *buf,
                                 size_t count, loff_t *ppos)
 {
-       struct seq_file *m = file->private_data;
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        hctx->queued = 0;
        return count;
 }
 
-static const struct file_operations hctx_queued_fops = {
-       .open           = hctx_queued_open,
-       .read           = seq_read,
-       .write          = hctx_queued_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_run_show(struct seq_file *m, void *v)
+static int hctx_run_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        seq_printf(m, "%lu\n", hctx->run);
        return 0;
 }
 
-static int hctx_run_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_run_show, inode->i_private);
-}
-
-static ssize_t hctx_run_write(struct file *file, const char __user *buf,
-                                size_t count, loff_t *ppos)
+static ssize_t hctx_run_write(void *data, const char __user *buf, size_t count,
+                             loff_t *ppos)
 {
-       struct seq_file *m = file->private_data;
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        hctx->run = 0;
        return count;
 }
 
-static const struct file_operations hctx_run_fops = {
-       .open           = hctx_run_open,
-       .read           = seq_read,
-       .write          = hctx_run_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int hctx_active_show(struct seq_file *m, void *v)
+static int hctx_active_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_hw_ctx *hctx = m->private;
+       struct blk_mq_hw_ctx *hctx = data;
 
        seq_printf(m, "%d\n", atomic_read(&hctx->nr_active));
        return 0;
 }
 
-static int hctx_active_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, hctx_active_show, inode->i_private);
-}
-
-static const struct file_operations hctx_active_fops = {
-       .open           = hctx_active_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
 static void *ctx_rq_list_start(struct seq_file *m, loff_t *pos)
        __acquires(&ctx->lock)
 {
@@ -716,156 +614,194 @@ static const struct seq_operations ctx_rq_list_seq_ops = {
        .stop   = ctx_rq_list_stop,
        .show   = blk_mq_debugfs_rq_show,
 };
-
-static int ctx_rq_list_open(struct inode *inode, struct file *file)
-{
-       return blk_mq_debugfs_seq_open(inode, file, &ctx_rq_list_seq_ops);
-}
-
-static const struct file_operations ctx_rq_list_fops = {
-       .open           = ctx_rq_list_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = seq_release,
-};
-
-static int ctx_dispatched_show(struct seq_file *m, void *v)
+static int ctx_dispatched_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_ctx *ctx = m->private;
+       struct blk_mq_ctx *ctx = data;
 
        seq_printf(m, "%lu %lu\n", ctx->rq_dispatched[1], ctx->rq_dispatched[0]);
        return 0;
 }
 
-static int ctx_dispatched_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ctx_dispatched_show, inode->i_private);
-}
-
-static ssize_t ctx_dispatched_write(struct file *file, const char __user *buf,
+static ssize_t ctx_dispatched_write(void *data, const char __user *buf,
                                    size_t count, loff_t *ppos)
 {
-       struct seq_file *m = file->private_data;
-       struct blk_mq_ctx *ctx = m->private;
+       struct blk_mq_ctx *ctx = data;
 
        ctx->rq_dispatched[0] = ctx->rq_dispatched[1] = 0;
        return count;
 }
 
-static const struct file_operations ctx_dispatched_fops = {
-       .open           = ctx_dispatched_open,
-       .read           = seq_read,
-       .write          = ctx_dispatched_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int ctx_merged_show(struct seq_file *m, void *v)
+static int ctx_merged_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_ctx *ctx = m->private;
+       struct blk_mq_ctx *ctx = data;
 
        seq_printf(m, "%lu\n", ctx->rq_merged);
        return 0;
 }
 
-static int ctx_merged_open(struct inode *inode, struct file *file)
+static ssize_t ctx_merged_write(void *data, const char __user *buf,
+                               size_t count, loff_t *ppos)
 {
-       return single_open(file, ctx_merged_show, inode->i_private);
-}
-
-static ssize_t ctx_merged_write(struct file *file, const char __user *buf,
-                                   size_t count, loff_t *ppos)
-{
-       struct seq_file *m = file->private_data;
-       struct blk_mq_ctx *ctx = m->private;
+       struct blk_mq_ctx *ctx = data;
 
        ctx->rq_merged = 0;
        return count;
 }
 
-static const struct file_operations ctx_merged_fops = {
-       .open           = ctx_merged_open,
-       .read           = seq_read,
-       .write          = ctx_merged_write,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int ctx_completed_show(struct seq_file *m, void *v)
+static int ctx_completed_show(void *data, struct seq_file *m)
 {
-       struct blk_mq_ctx *ctx = m->private;
+       struct blk_mq_ctx *ctx = data;
 
        seq_printf(m, "%lu %lu\n", ctx->rq_completed[1], ctx->rq_completed[0]);
        return 0;
 }
 
-static int ctx_completed_open(struct inode *inode, struct file *file)
+static ssize_t ctx_completed_write(void *data, const char __user *buf,
+                                  size_t count, loff_t *ppos)
 {
-       return single_open(file, ctx_completed_show, inode->i_private);
+       struct blk_mq_ctx *ctx = data;
+
+       ctx->rq_completed[0] = ctx->rq_completed[1] = 0;
+       return count;
 }
 
-static ssize_t ctx_completed_write(struct file *file, const char __user *buf,
-                                  size_t count, loff_t *ppos)
+static int blk_mq_debugfs_show(struct seq_file *m, void *v)
+{
+       const struct blk_mq_debugfs_attr *attr = m->private;
+       void *data = d_inode(m->file->f_path.dentry->d_parent)->i_private;
+
+       return attr->show(data, m);
+}
+
+static ssize_t blk_mq_debugfs_write(struct file *file, const char __user *buf,
+                                   size_t count, loff_t *ppos)
 {
        struct seq_file *m = file->private_data;
-       struct blk_mq_ctx *ctx = m->private;
+       const struct blk_mq_debugfs_attr *attr = m->private;
+       void *data = d_inode(file->f_path.dentry->d_parent)->i_private;
 
-       ctx->rq_completed[0] = ctx->rq_completed[1] = 0;
-       return count;
+       if (!attr->write)
+               return -EPERM;
+
+       return attr->write(data, buf, count, ppos);
+}
+
+static int blk_mq_debugfs_open(struct inode *inode, struct file *file)
+{
+       const struct blk_mq_debugfs_attr *attr = inode->i_private;
+       void *data = d_inode(file->f_path.dentry->d_parent)->i_private;
+       struct seq_file *m;
+       int ret;
+
+       if (attr->seq_ops) {
+               ret = seq_open(file, attr->seq_ops);
+               if (!ret) {
+                       m = file->private_data;
+                       m->private = data;
+               }
+               return ret;
+       }
+
+       if (WARN_ON_ONCE(!attr->show))
+               return -EPERM;
+
+       return single_open(file, blk_mq_debugfs_show, inode->i_private);
+}
+
+static int blk_mq_debugfs_release(struct inode *inode, struct file *file)
+{
+       const struct blk_mq_debugfs_attr *attr = inode->i_private;
+
+       if (attr->show)
+               return single_release(inode, file);
+       else
+               return seq_release(inode, file);
 }
 
-static const struct file_operations ctx_completed_fops = {
-       .open           = ctx_completed_open,
+const struct file_operations blk_mq_debugfs_fops = {
+       .open           = blk_mq_debugfs_open,
        .read           = seq_read,
-       .write          = ctx_completed_write,
+       .write          = blk_mq_debugfs_write,
        .llseek         = seq_lseek,
-       .release        = single_release,
+       .release        = blk_mq_debugfs_release,
 };
 
 static const struct blk_mq_debugfs_attr blk_mq_debugfs_queue_attrs[] = {
-       {"poll_stat", 0400, &queue_poll_stat_fops},
-       {"state", 0600, &blk_queue_flags_fops},
+       {"poll_stat", 0400, queue_poll_stat_show},
+       {"requeue_list", 0400, .seq_ops = &queue_requeue_list_seq_ops},
+       {"state", 0600, queue_state_show, queue_state_write},
        {},
 };
 
 static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = {
-       {"state", 0400, &hctx_state_fops},
-       {"flags", 0400, &hctx_flags_fops},
-       {"dispatch", 0400, &hctx_dispatch_fops},
-       {"ctx_map", 0400, &hctx_ctx_map_fops},
-       {"tags", 0400, &hctx_tags_fops},
-       {"tags_bitmap", 0400, &hctx_tags_bitmap_fops},
-       {"sched_tags", 0400, &hctx_sched_tags_fops},
-       {"sched_tags_bitmap", 0400, &hctx_sched_tags_bitmap_fops},
-       {"io_poll", 0600, &hctx_io_poll_fops},
-       {"dispatched", 0600, &hctx_dispatched_fops},
-       {"queued", 0600, &hctx_queued_fops},
-       {"run", 0600, &hctx_run_fops},
-       {"active", 0400, &hctx_active_fops},
+       {"state", 0400, hctx_state_show},
+       {"flags", 0400, hctx_flags_show},
+       {"dispatch", 0400, .seq_ops = &hctx_dispatch_seq_ops},
+       {"busy", 0400, hctx_busy_show},
+       {"ctx_map", 0400, hctx_ctx_map_show},
+       {"tags", 0400, hctx_tags_show},
+       {"tags_bitmap", 0400, hctx_tags_bitmap_show},
+       {"sched_tags", 0400, hctx_sched_tags_show},
+       {"sched_tags_bitmap", 0400, hctx_sched_tags_bitmap_show},
+       {"io_poll", 0600, hctx_io_poll_show, hctx_io_poll_write},
+       {"dispatched", 0600, hctx_dispatched_show, hctx_dispatched_write},
+       {"queued", 0600, hctx_queued_show, hctx_queued_write},
+       {"run", 0600, hctx_run_show, hctx_run_write},
+       {"active", 0400, hctx_active_show},
        {},
 };
 
 static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = {
-       {"rq_list", 0400, &ctx_rq_list_fops},
-       {"dispatched", 0600, &ctx_dispatched_fops},
-       {"merged", 0600, &ctx_merged_fops},
-       {"completed", 0600, &ctx_completed_fops},
+       {"rq_list", 0400, .seq_ops = &ctx_rq_list_seq_ops},
+       {"dispatched", 0600, ctx_dispatched_show, ctx_dispatched_write},
+       {"merged", 0600, ctx_merged_show, ctx_merged_write},
+       {"completed", 0600, ctx_completed_show, ctx_completed_write},
        {},
 };
 
+static bool debugfs_create_files(struct dentry *parent, void *data,
+                                const struct blk_mq_debugfs_attr *attr)
+{
+       d_inode(parent)->i_private = data;
+
+       for (; attr->name; attr++) {
+               if (!debugfs_create_file(attr->name, attr->mode, parent,
+                                        (void *)attr, &blk_mq_debugfs_fops))
+                       return false;
+       }
+       return true;
+}
+
 int blk_mq_debugfs_register(struct request_queue *q)
 {
+       struct blk_mq_hw_ctx *hctx;
+       int i;
+
        if (!blk_debugfs_root)
                return -ENOENT;
 
        q->debugfs_dir = debugfs_create_dir(kobject_name(q->kobj.parent),
                                            blk_debugfs_root);
        if (!q->debugfs_dir)
-               goto err;
+               return -ENOMEM;
 
-       if (blk_mq_debugfs_register_mq(q))
+       if (!debugfs_create_files(q->debugfs_dir, q,
+                                 blk_mq_debugfs_queue_attrs))
                goto err;
 
+       /*
+        * blk_mq_init_hctx() attempted to do this already, but q->debugfs_dir
+        * didn't exist yet (because we don't know what to name the directory
+        * until the queue is registered to a gendisk).
+        */
+       queue_for_each_hw_ctx(q, hctx, i) {
+               if (!hctx->debugfs_dir && blk_mq_debugfs_register_hctx(q, hctx))
+                       goto err;
+               if (q->elevator && !hctx->sched_debugfs_dir &&
+                   blk_mq_debugfs_register_sched_hctx(q, hctx))
+                       goto err;
+       }
+
        return 0;
 
 err:
@@ -876,30 +812,18 @@ err:
 void blk_mq_debugfs_unregister(struct request_queue *q)
 {
        debugfs_remove_recursive(q->debugfs_dir);
-       q->mq_debugfs_dir = NULL;
+       q->sched_debugfs_dir = NULL;
        q->debugfs_dir = NULL;
 }
 
-static bool debugfs_create_files(struct dentry *parent, void *data,
-                               const struct blk_mq_debugfs_attr *attr)
-{
-       for (; attr->name; attr++) {
-               if (!debugfs_create_file(attr->name, attr->mode, parent,
-                                        data, attr->fops))
-                       return false;
-       }
-       return true;
-}
-
-static int blk_mq_debugfs_register_ctx(struct request_queue *q,
-                                      struct blk_mq_ctx *ctx,
-                                      struct dentry *hctx_dir)
+static int blk_mq_debugfs_register_ctx(struct blk_mq_hw_ctx *hctx,
+                                      struct blk_mq_ctx *ctx)
 {
        struct dentry *ctx_dir;
        char name[20];
 
        snprintf(name, sizeof(name), "cpu%u", ctx->cpu);
-       ctx_dir = debugfs_create_dir(name, hctx_dir);
+       ctx_dir = debugfs_create_dir(name, hctx->debugfs_dir);
        if (!ctx_dir)
                return -ENOMEM;
 
@@ -909,59 +833,122 @@ static int blk_mq_debugfs_register_ctx(struct request_queue *q,
        return 0;
 }
 
-static int blk_mq_debugfs_register_hctx(struct request_queue *q,
-                                       struct blk_mq_hw_ctx *hctx)
+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;
+       if (!q->debugfs_dir)
+               return -ENOENT;
 
-       if (!debugfs_create_files(hctx_dir, hctx, blk_mq_debugfs_hctx_attrs))
+       snprintf(name, sizeof(name), "hctx%u", hctx->queue_num);
+       hctx->debugfs_dir = debugfs_create_dir(name, q->debugfs_dir);
+       if (!hctx->debugfs_dir)
                return -ENOMEM;
 
+       if (!debugfs_create_files(hctx->debugfs_dir, hctx,
+                                 blk_mq_debugfs_hctx_attrs))
+               goto err;
+
        hctx_for_each_ctx(hctx, ctx, i) {
-               if (blk_mq_debugfs_register_ctx(q, ctx, hctx_dir))
+               if (blk_mq_debugfs_register_ctx(hctx, ctx))
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       blk_mq_debugfs_unregister_hctx(hctx);
+       return -ENOMEM;
+}
+
+void blk_mq_debugfs_unregister_hctx(struct blk_mq_hw_ctx *hctx)
+{
+       debugfs_remove_recursive(hctx->debugfs_dir);
+       hctx->sched_debugfs_dir = NULL;
+       hctx->debugfs_dir = NULL;
+}
+
+int blk_mq_debugfs_register_hctxs(struct request_queue *q)
+{
+       struct blk_mq_hw_ctx *hctx;
+       int i;
+
+       queue_for_each_hw_ctx(q, hctx, i) {
+               if (blk_mq_debugfs_register_hctx(q, hctx))
                        return -ENOMEM;
        }
 
        return 0;
 }
 
-int blk_mq_debugfs_register_mq(struct request_queue *q)
+void blk_mq_debugfs_unregister_hctxs(struct request_queue *q)
 {
        struct blk_mq_hw_ctx *hctx;
        int i;
 
+       queue_for_each_hw_ctx(q, hctx, i)
+               blk_mq_debugfs_unregister_hctx(hctx);
+}
+
+int blk_mq_debugfs_register_sched(struct request_queue *q)
+{
+       struct elevator_type *e = q->elevator->type;
+
        if (!q->debugfs_dir)
                return -ENOENT;
 
-       q->mq_debugfs_dir = debugfs_create_dir("mq", q->debugfs_dir);
-       if (!q->mq_debugfs_dir)
-               goto err;
+       if (!e->queue_debugfs_attrs)
+               return 0;
 
-       if (!debugfs_create_files(q->mq_debugfs_dir, q, blk_mq_debugfs_queue_attrs))
-               goto err;
+       q->sched_debugfs_dir = debugfs_create_dir("sched", q->debugfs_dir);
+       if (!q->sched_debugfs_dir)
+               return -ENOMEM;
 
-       queue_for_each_hw_ctx(q, hctx, i) {
-               if (blk_mq_debugfs_register_hctx(q, hctx))
-                       goto err;
-       }
+       if (!debugfs_create_files(q->sched_debugfs_dir, q,
+                                 e->queue_debugfs_attrs))
+               goto err;
 
        return 0;
 
 err:
-       blk_mq_debugfs_unregister_mq(q);
+       blk_mq_debugfs_unregister_sched(q);
        return -ENOMEM;
 }
 
-void blk_mq_debugfs_unregister_mq(struct request_queue *q)
+void blk_mq_debugfs_unregister_sched(struct request_queue *q)
+{
+       debugfs_remove_recursive(q->sched_debugfs_dir);
+       q->sched_debugfs_dir = NULL;
+}
+
+int blk_mq_debugfs_register_sched_hctx(struct request_queue *q,
+                                      struct blk_mq_hw_ctx *hctx)
+{
+       struct elevator_type *e = q->elevator->type;
+
+       if (!hctx->debugfs_dir)
+               return -ENOENT;
+
+       if (!e->hctx_debugfs_attrs)
+               return 0;
+
+       hctx->sched_debugfs_dir = debugfs_create_dir("sched",
+                                                    hctx->debugfs_dir);
+       if (!hctx->sched_debugfs_dir)
+               return -ENOMEM;
+
+       if (!debugfs_create_files(hctx->sched_debugfs_dir, hctx,
+                                 e->hctx_debugfs_attrs))
+               return -ENOMEM;
+
+       return 0;
+}
+
+void blk_mq_debugfs_unregister_sched_hctx(struct blk_mq_hw_ctx *hctx)
 {
-       debugfs_remove_recursive(q->mq_debugfs_dir);
-       q->mq_debugfs_dir = NULL;
+       debugfs_remove_recursive(hctx->sched_debugfs_dir);
+       hctx->sched_debugfs_dir = NULL;
 }