Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753313AbbGSLrG (ORCPT ); Sun, 19 Jul 2015 07:47:06 -0400 Received: from mail-ie0-f176.google.com ([209.85.223.176]:36250 "EHLO mail-ie0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752145AbbGSLrD (ORCPT ); Sun, 19 Jul 2015 07:47:03 -0400 MIME-Version: 1.0 In-Reply-To: <1437236903-31617-3-git-send-email-akinobu.mita@gmail.com> References: <1437236903-31617-1-git-send-email-akinobu.mita@gmail.com> <1437236903-31617-3-git-send-email-akinobu.mita@gmail.com> Date: Sun, 19 Jul 2015 19:47:02 +0800 Message-ID: Subject: Re: [PATCH v3 2/7] blk-mq: fix sysfs registration/unregistration race From: Ming Lei To: Akinobu Mita Cc: Linux Kernel Mailing List , Jens Axboe Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7048 Lines: 216 On Sun, Jul 19, 2015 at 12:28 AM, Akinobu Mita wrote: > There is a race between cpu hotplug handling and adding/deleting > gendisk for blk-mq, where both are trying to register and unregister > the same sysfs entries. > > null_add_dev > --> blk_mq_init_queue > --> blk_mq_init_allocated_queue > --> add to 'all_q_list' (*) > --> add_disk > --> blk_register_queue > --> blk_mq_register_disk (++) > > null_del_dev > --> del_gendisk > --> blk_unregister_queue > --> blk_mq_unregister_disk (--) > --> blk_cleanup_queue > --> blk_mq_free_queue > --> del from 'all_q_list' (*) > > blk_mq_queue_reinit > --> blk_mq_sysfs_unregister (-) > --> blk_mq_sysfs_register (+) > > While the request queue is added to 'all_q_list' (*), > blk_mq_queue_reinit() can be called for the queue anytime by CPU > hotplug callback. But blk_mq_sysfs_unregister (-) and > blk_mq_sysfs_register (+) in blk_mq_queue_reinit must not be called > before blk_mq_register_disk (++) and after blk_mq_unregister_disk (--) > is finished. Because '/sys/block/*/mq/' is not exists. > > There has already been BLK_MQ_F_SYSFS_UP flag in hctx->flags which can > be used to track these sysfs stuff, but it is only fixing this issue > partially. > > In order to fix it completely, we just need per-queue flag instead of > per-hctx flag with appropriate locking. So this introduces > q->mq_sysfs_init_done which is properly protected with all_q_mutex. > > Also, we need to ensure that blk_mq_map_swqueue() is called with > all_q_mutex is held. Since hctx->nr_ctx is reset temporarily and > updated in blk_mq_map_swqueue(), so we should avoid > blk_mq_register_hctx() seeing the temporary hctx->nr_ctx value > in CPU hotplug handling or adding/deleting gendisk . > > Signed-off-by: Akinobu Mita > Cc: Jens Axboe > Cc: Ming Lei Reviewed-by: Ming Lei > --- > block/blk-mq-sysfs.c | 30 ++++++++++++++++++++++-------- > block/blk-mq.c | 6 +++--- > include/linux/blk-mq.h | 1 - > include/linux/blkdev.h | 2 ++ > 4 files changed, 27 insertions(+), 12 deletions(-) > > diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c > index b79685e..79096a6 100644 > --- a/block/blk-mq-sysfs.c > +++ b/block/blk-mq-sysfs.c > @@ -332,7 +332,7 @@ static void blk_mq_unregister_hctx(struct blk_mq_hw_ctx *hctx) > struct blk_mq_ctx *ctx; > int i; > > - if (!hctx->nr_ctx || !(hctx->flags & BLK_MQ_F_SYSFS_UP)) > + if (!hctx->nr_ctx) > return; > > hctx_for_each_ctx(hctx, ctx, i) > @@ -347,7 +347,7 @@ static int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx) > struct blk_mq_ctx *ctx; > int i, ret; > > - if (!hctx->nr_ctx || !(hctx->flags & BLK_MQ_F_SYSFS_UP)) > + if (!hctx->nr_ctx) > return 0; > > ret = kobject_add(&hctx->kobj, &q->mq_kobj, "%u", hctx->queue_num); > @@ -370,6 +370,8 @@ void blk_mq_unregister_disk(struct gendisk *disk) > struct blk_mq_ctx *ctx; > int i, j; > > + blk_mq_disable_hotplug(); > + > queue_for_each_hw_ctx(q, hctx, i) { > blk_mq_unregister_hctx(hctx); > > @@ -384,6 +386,9 @@ void blk_mq_unregister_disk(struct gendisk *disk) > kobject_put(&q->mq_kobj); > > kobject_put(&disk_to_dev(disk)->kobj); > + > + q->mq_sysfs_init_done = false; > + blk_mq_enable_hotplug(); > } > > static void blk_mq_sysfs_init(struct request_queue *q) > @@ -414,27 +419,30 @@ int blk_mq_register_disk(struct gendisk *disk) > struct blk_mq_hw_ctx *hctx; > int ret, i; > > + blk_mq_disable_hotplug(); > + > blk_mq_sysfs_init(q); > > ret = kobject_add(&q->mq_kobj, kobject_get(&dev->kobj), "%s", "mq"); > if (ret < 0) > - return ret; > + goto out; > > kobject_uevent(&q->mq_kobj, KOBJ_ADD); > > queue_for_each_hw_ctx(q, hctx, i) { > - hctx->flags |= BLK_MQ_F_SYSFS_UP; > ret = blk_mq_register_hctx(hctx); > if (ret) > break; > } > > - if (ret) { > + if (ret) > blk_mq_unregister_disk(disk); > - return ret; > - } > + else > + q->mq_sysfs_init_done = true; > +out: > + blk_mq_enable_hotplug(); > > - return 0; > + return ret; > } > EXPORT_SYMBOL_GPL(blk_mq_register_disk); > > @@ -443,6 +451,9 @@ void blk_mq_sysfs_unregister(struct request_queue *q) > struct blk_mq_hw_ctx *hctx; > int i; > > + if (!q->mq_sysfs_init_done) > + return; > + > queue_for_each_hw_ctx(q, hctx, i) > blk_mq_unregister_hctx(hctx); > } > @@ -452,6 +463,9 @@ int blk_mq_sysfs_register(struct request_queue *q) > struct blk_mq_hw_ctx *hctx; > int i, ret = 0; > > + if (!q->mq_sysfs_init_done) > + return ret; > + > queue_for_each_hw_ctx(q, hctx, i) { > ret = blk_mq_register_hctx(hctx); > if (ret) > diff --git a/block/blk-mq.c b/block/blk-mq.c > index f29f766..68921b7 100644 > --- a/block/blk-mq.c > +++ b/block/blk-mq.c > @@ -2045,13 +2045,13 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, > goto err_hctxs; > > mutex_lock(&all_q_mutex); > - list_add_tail(&q->all_q_node, &all_q_list); > - mutex_unlock(&all_q_mutex); > > + list_add_tail(&q->all_q_node, &all_q_list); > blk_mq_add_queue_tag_set(set, q); > - > blk_mq_map_swqueue(q); > > + mutex_unlock(&all_q_mutex); > + > return q; > > err_hctxs: > diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h > index 37d1602..b80ba45 100644 > --- a/include/linux/blk-mq.h > +++ b/include/linux/blk-mq.h > @@ -145,7 +145,6 @@ enum { > BLK_MQ_F_SHOULD_MERGE = 1 << 0, > BLK_MQ_F_TAG_SHARED = 1 << 1, > BLK_MQ_F_SG_MERGE = 1 << 2, > - BLK_MQ_F_SYSFS_UP = 1 << 3, > BLK_MQ_F_DEFER_ISSUE = 1 << 4, > BLK_MQ_F_ALLOC_POLICY_START_BIT = 8, > BLK_MQ_F_ALLOC_POLICY_BITS = 1, > diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h > index d4068c1..b02c90b 100644 > --- a/include/linux/blkdev.h > +++ b/include/linux/blkdev.h > @@ -462,6 +462,8 @@ struct request_queue { > > struct blk_mq_tag_set *tag_set; > struct list_head tag_set_list; > + > + bool mq_sysfs_init_done; > }; > > #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ > -- > 1.9.1 > -- Ming Lei -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/