diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 6beb1f5b7..7f3fbac0f 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -70,6 +70,7 @@ static DEFINE_MUTEX(lock); static int null_major; static DEFINE_IDA(nullb_indexes); static struct blk_mq_tag_set tag_set; +static struct blk_mq_tag_set blocking_tag_set; enum { NULL_IRQ_NONE = 0, @@ -1847,34 +1848,44 @@ static int null_init_tag_set(struct blk_mq_tag_set *set, int poll_queues) return blk_mq_alloc_tag_set(set); } -static int null_init_global_tag_set(void) +static int null_init_global_tag_set(struct blk_mq_tag_set *set, bool blocking) { int error; - if (tag_set.ops) + if (set->ops) return 0; - tag_set.nr_hw_queues = g_submit_queues; - tag_set.queue_depth = g_hw_queue_depth; - tag_set.numa_node = g_home_node; + set->nr_hw_queues = g_submit_queues; + set->queue_depth = g_hw_queue_depth; + set->numa_node = g_home_node; if (g_no_sched) - tag_set.flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT; + set->flags |= BLK_MQ_F_NO_SCHED_BY_DEFAULT; if (g_shared_tag_bitmap) - tag_set.flags |= BLK_MQ_F_TAG_HCTX_SHARED; - if (g_blocking) - tag_set.flags |= BLK_MQ_F_BLOCKING; + set->flags |= BLK_MQ_F_TAG_HCTX_SHARED; + if (blocking) + set->flags |= BLK_MQ_F_BLOCKING; - error = null_init_tag_set(&tag_set, g_poll_queues); + error = null_init_tag_set(set, g_poll_queues); if (error) - tag_set.ops = NULL; + set->ops = NULL; return error; } static int null_setup_tagset(struct nullb *nullb) { if (nullb->dev->shared_tags) { - nullb->tag_set = &tag_set; - return null_init_global_tag_set(); + bool blocking = g_blocking || nullb->dev->blocking; + + /* + * Some memory-backed devices can sleep in their request + * submission path, which is invalid if the tag set is + * non-blocking since the block layer then calls the driver + * under rcu_read_lock(). The blocking flag is fixed when a tag + * set is allocated, so use a dedicated blocking global tag set + * for such devices. + */ + nullb->tag_set = blocking ? &blocking_tag_set : &tag_set; + return null_init_global_tag_set(nullb->tag_set, blocking); } nullb->tag_set = &nullb->__tag_set; @@ -2195,6 +2206,8 @@ static int __init null_init(void) unregister_blkdev(null_major, "nullb"); if (tag_set.ops) blk_mq_free_tag_set(&tag_set); + if (blocking_tag_set.ops) + blk_mq_free_tag_set(&blocking_tag_set); return ret; } @@ -2215,6 +2228,8 @@ static void __exit null_exit(void) if (tag_set.ops) blk_mq_free_tag_set(&tag_set); + if (blocking_tag_set.ops) + blk_mq_free_tag_set(&blocking_tag_set); } module_init(null_init);