blk-mq: Export queue state through /sys/kernel/debug/block/*/state
authorBart Van Assche <bart.vanassche@sandisk.com>
Mon, 10 Apr 2017 22:13:15 +0000 (16:13 -0600)
committerJens Axboe <axboe@fb.com>
Mon, 10 Apr 2017 22:13:15 +0000 (16:13 -0600)
Make it possible to check whether or not a block layer queue has
been stopped. Make it possible to start and to run a blk-mq queue
from user space.

Signed-off-by: Bart Van Assche <bart.vanassche@sandisk.com>
Cc: Omar Sandoval <osandov@fb.com>
Cc: Hannes Reinecke <hare@suse.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
block/blk-mq-debugfs.c

index 4b3f962a9c7a42c2e3df62628f3ae44a136d4260..b25c76a0cbbce51581d495b3d074f24ef990a726 100644 (file)
@@ -43,6 +43,109 @@ static int blk_mq_debugfs_seq_open(struct inode *inode, struct file *file,
        return ret;
 }
 
+static int blk_flags_show(struct seq_file *m, const unsigned long flags,
+                         const char *const *flag_name, int flag_name_count)
+{
+       bool sep = false;
+       int i;
+
+       for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) {
+               if (!(flags & BIT(i)))
+                       continue;
+               if (sep)
+                       seq_puts(m, " ");
+               sep = true;
+               if (i < flag_name_count && flag_name[i])
+                       seq_puts(m, flag_name[i]);
+               else
+                       seq_printf(m, "%d", i);
+       }
+       seq_puts(m, "\n");
+       return 0;
+}
+
+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;
+
+       blk_flags_show(m, q->queue_flags, blk_queue_flag_name,
+                      ARRAY_SIZE(blk_queue_flag_name));
+       return 0;
+}
+
+static ssize_t blk_queue_flags_store(struct file *file, const char __user *ubuf,
+                                    size_t len, loff_t *offp)
+{
+       struct request_queue *q = file_inode(file)->i_private;
+       char op[16] = { }, *s;
+
+       len = min(len, sizeof(op) - 1);
+       if (copy_from_user(op, ubuf, len))
+               return -EFAULT;
+       s = op;
+       strsep(&s, " \t\n"); /* strip trailing whitespace */
+       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 {
+               pr_err("%s: unsupported operation %s. Use either 'run' or 'start'\n",
+                      __func__, op);
+               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);
+}
+
+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 const struct blk_mq_debugfs_attr blk_queue_attrs[] = {
+       {"state", 0600, &blk_queue_flags_fops},
+       {},
+};
+
 static void print_stat(struct seq_file *m, struct blk_rq_stat *stat)
 {
        if (stat->nr_samples) {
@@ -735,6 +838,9 @@ int blk_mq_debugfs_register_hctxs(struct request_queue *q)
        if (!q->debugfs_dir)
                return -ENOENT;
 
+       if (!debugfs_create_files(q->debugfs_dir, q, blk_queue_attrs))
+               goto err;
+
        q->mq_debugfs_dir = debugfs_create_dir("mq", q->debugfs_dir);
        if (!q->mq_debugfs_dir)
                goto err;