* [PATCH v3 1/3] block: Add a third argument to blk_alloc_queue_node()
2018-02-09 18:44 [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
@ 2018-02-09 18:44 ` Bart Van Assche
2018-02-09 18:44 ` [PATCH v3 2/3] block: Fix a race between the cgroup code and request queue initialization Bart Van Assche
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Bart Van Assche @ 2018-02-09 18:44 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Jan Kara, Joseph Qi,
Bart Van Assche, Philipp Reisner, Ulf Hansson, Kees Cook
This patch does not change any functionality.
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Philipp Reisner <philipp.reisner@linbit.com>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Kees Cook <keescook@chromium.org>
---
block/blk-core.c | 7 ++++---
block/blk-mq.c | 2 +-
drivers/block/null_blk.c | 3 ++-
drivers/ide/ide-probe.c | 2 +-
drivers/lightnvm/core.c | 2 +-
drivers/md/dm.c | 2 +-
drivers/nvdimm/pmem.c | 2 +-
drivers/nvme/host/multipath.c | 2 +-
drivers/scsi/scsi_lib.c | 2 +-
include/linux/blkdev.h | 3 ++-
10 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 2d1a7bbe0634..e873a24bf82d 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -810,7 +810,7 @@ void blk_exit_rl(struct request_queue *q, struct request_list *rl)
struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
{
- return blk_alloc_queue_node(gfp_mask, NUMA_NO_NODE);
+ return blk_alloc_queue_node(gfp_mask, NUMA_NO_NODE, NULL);
}
EXPORT_SYMBOL(blk_alloc_queue);
@@ -888,7 +888,8 @@ static void blk_rq_timed_out_timer(struct timer_list *t)
kblockd_schedule_work(&q->timeout_work);
}
-struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
+struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id,
+ spinlock_t *lock)
{
struct request_queue *q;
@@ -1030,7 +1031,7 @@ blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{
struct request_queue *q;
- q = blk_alloc_queue_node(GFP_KERNEL, node_id);
+ q = blk_alloc_queue_node(GFP_KERNEL, node_id, NULL);
if (!q)
return NULL;
diff --git a/block/blk-mq.c b/block/blk-mq.c
index df93102e2149..ae4e5096f425 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2554,7 +2554,7 @@ struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
{
struct request_queue *uninit_q, *q;
- uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node);
+ uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node, NULL);
if (!uninit_q)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 287a09611c0f..f3d17ea88965 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -1717,7 +1717,8 @@ static int null_add_dev(struct nullb_device *dev)
}
null_init_queues(nullb);
} else if (dev->queue_mode == NULL_Q_BIO) {
- nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node);
+ nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node,
+ NULL);
if (!nullb->q) {
rv = -ENOMEM;
goto out_cleanup_queues;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 17fd55af4d92..2e80a866073c 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -766,7 +766,7 @@ static int ide_init_queue(ide_drive_t *drive)
* limits and LBA48 we could raise it but as yet
* do not.
*/
- q = blk_alloc_queue_node(GFP_KERNEL, hwif_to_node(hwif));
+ q = blk_alloc_queue_node(GFP_KERNEL, hwif_to_node(hwif), NULL);
if (!q)
return 1;
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index dcc9e621e651..5f1988df1593 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -384,7 +384,7 @@ static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create)
goto err_dev;
}
- tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node);
+ tqueue = blk_alloc_queue_node(GFP_KERNEL, dev->q->node, NULL);
if (!tqueue) {
ret = -ENOMEM;
goto err_disk;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d6de00f367ef..3c55564f6367 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1840,7 +1840,7 @@ static struct mapped_device *alloc_dev(int minor)
INIT_LIST_HEAD(&md->table_devices);
spin_lock_init(&md->uevent_lock);
- md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id);
+ md->queue = blk_alloc_queue_node(GFP_KERNEL, numa_node_id, NULL);
if (!md->queue)
goto bad;
md->queue->queuedata = md;
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 10041ac4032c..cfb15ac50925 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -344,7 +344,7 @@ static int pmem_attach_disk(struct device *dev,
return -EBUSY;
}
- q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev));
+ q = blk_alloc_queue_node(GFP_KERNEL, dev_to_node(dev), NULL);
if (!q)
return -ENOMEM;
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 3b211d9e58b8..cf013784fa85 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -162,7 +162,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
if (!(ctrl->subsys->cmic & (1 << 1)) || !multipath)
return 0;
- q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, NULL);
if (!q)
goto out;
q->queuedata = head;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a86df9ca7d1c..71d1135f94d0 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2223,7 +2223,7 @@ struct request_queue *scsi_old_alloc_queue(struct scsi_device *sdev)
struct Scsi_Host *shost = sdev->host;
struct request_queue *q;
- q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, NULL);
if (!q)
return NULL;
q->cmd_size = sizeof(struct scsi_cmnd) + shost->hostt->cmd_size;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 4f3df807cf8f..d78dd92a9b6a 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1321,7 +1321,8 @@ extern long nr_blockdev_pages(void);
bool __must_check blk_get_queue(struct request_queue *);
struct request_queue *blk_alloc_queue(gfp_t);
-struct request_queue *blk_alloc_queue_node(gfp_t, int);
+struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id,
+ spinlock_t *lock);
extern void blk_put_queue(struct request_queue *);
extern void blk_set_queue_dying(struct request_queue *);
--
2.16.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 2/3] block: Fix a race between the cgroup code and request queue initialization
2018-02-09 18:44 [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
2018-02-09 18:44 ` [PATCH v3 1/3] block: Add a third argument to blk_alloc_queue_node() Bart Van Assche
@ 2018-02-09 18:44 ` Bart Van Assche
2018-02-09 18:44 ` [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller Bart Van Assche
2018-02-21 22:51 ` [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
3 siblings, 0 replies; 8+ messages in thread
From: Bart Van Assche @ 2018-02-09 18:44 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Jan Kara, Joseph Qi,
Bart Van Assche, Philipp Reisner, Ulf Hansson, Kees Cook
Initialize the request queue lock earlier such that the following
race can no longer occur:
blk_init_queue_node() blkcg_print_blkgs()
blk_alloc_queue_node (1)
q->queue_lock = &q->__queue_lock (2)
blkcg_init_queue(q) (3)
spin_lock_irq(blkg->q->queue_lock) (4)
q->queue_lock = lock (5)
spin_unlock_irq(blkg->q->queue_lock) (6)
(1) allocate an uninitialized queue;
(2) initialize queue_lock to its default internal lock;
(3) initialize blkcg part of request queue, which will create blkg and
then insert it to blkg_list;
(4) traverse blkg_list and find the created blkg, and then take its
queue lock, here it is the default *internal lock*;
(5) *race window*, now queue_lock is overridden with *driver specified
lock*;
(6) now unlock *driver specified lock*, not the locked *internal lock*,
unlock balance breaks.
The changes in this patch are as follows:
- Move the .queue_lock initialization from blk_init_queue_node() into
blk_alloc_queue_node().
- Only override the .queue_lock pointer for legacy queues because it
is not useful for blk-mq queues to override this pointer.
- For all all block drivers that initialize .queue_lock explicitly,
change the blk_alloc_queue() call in the driver into a
blk_alloc_queue_node() call and remove the explicit .queue_lock
initialization. Additionally, initialize the spin lock that will
be used as queue lock earlier if necessary.
Reported-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Joseph Qi <joseph.qi@linux.alibaba.com>
Cc: Philipp Reisner <philipp.reisner@linbit.com>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: Kees Cook <keescook@chromium.org>
---
block/blk-core.c | 24 ++++++++++++++++--------
drivers/block/drbd/drbd_main.c | 3 +--
drivers/block/umem.c | 7 +++----
3 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index e873a24bf82d..41c74b37be85 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -888,6 +888,19 @@ static void blk_rq_timed_out_timer(struct timer_list *t)
kblockd_schedule_work(&q->timeout_work);
}
+/**
+ * blk_alloc_queue_node - allocate a request queue
+ * @gfp_mask: memory allocation flags
+ * @node_id: NUMA node to allocate memory from
+ * @lock: For legacy queues, pointer to a spinlock that will be used to e.g.
+ * serialize calls to the legacy .request_fn() callback. Ignored for
+ * blk-mq request queues.
+ *
+ * Note: pass the queue lock as the third argument to this function instead of
+ * setting the queue lock pointer explicitly to avoid triggering a sporadic
+ * crash in the blkcg code. This function namely calls blkcg_init_queue() and
+ * the queue lock pointer must be set before blkcg_init_queue() is called.
+ */
struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id,
spinlock_t *lock)
{
@@ -940,11 +953,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id,
mutex_init(&q->sysfs_lock);
spin_lock_init(&q->__queue_lock);
- /*
- * By default initialize queue_lock to internal lock and driver can
- * override it later if need be.
- */
- q->queue_lock = &q->__queue_lock;
+ if (!q->mq_ops)
+ q->queue_lock = lock ? : &q->__queue_lock;
/*
* A queue starts its life with bypass turned on to avoid
@@ -1031,13 +1041,11 @@ blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)
{
struct request_queue *q;
- q = blk_alloc_queue_node(GFP_KERNEL, node_id, NULL);
+ q = blk_alloc_queue_node(GFP_KERNEL, node_id, lock);
if (!q)
return NULL;
q->request_fn = rfn;
- if (lock)
- q->queue_lock = lock;
if (blk_init_allocated_queue(q) < 0) {
blk_cleanup_queue(q);
return NULL;
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 0a0394aa1b9c..185f1ef00a7c 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2816,7 +2816,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_init_set_defaults(device);
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, &resource->req_lock);
if (!q)
goto out_no_q;
device->rq_queue = q;
@@ -2848,7 +2848,6 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
- q->queue_lock = &resource->req_lock;
device->md_io.page = alloc_page(GFP_KERNEL);
if (!device->md_io.page)
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 8077123678ad..5c7fb8cc4149 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -888,13 +888,14 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
card->Active = -1; /* no page is active */
card->bio = NULL;
card->biotail = &card->bio;
+ spin_lock_init(&card->lock);
- card->queue = blk_alloc_queue(GFP_KERNEL);
+ card->queue = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE,
+ &card->lock);
if (!card->queue)
goto failed_alloc;
blk_queue_make_request(card->queue, mm_make_request);
- card->queue->queue_lock = &card->lock;
card->queue->queuedata = card;
tasklet_init(&card->tasklet, process_page, (unsigned long)card);
@@ -968,8 +969,6 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_printk(KERN_INFO, &card->dev->dev,
"Window size %d bytes, IRQ %d\n", data, dev->irq);
- spin_lock_init(&card->lock);
-
pci_set_drvdata(dev, card);
if (pci_write_cmd != 0x0F) /* If not Memory Write & Invalidate */
--
2.16.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller
2018-02-09 18:44 [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
2018-02-09 18:44 ` [PATCH v3 1/3] block: Add a third argument to blk_alloc_queue_node() Bart Van Assche
2018-02-09 18:44 ` [PATCH v3 2/3] block: Fix a race between the cgroup code and request queue initialization Bart Van Assche
@ 2018-02-09 18:44 ` Bart Van Assche
2018-02-22 2:25 ` Joseph Qi
2018-02-21 22:51 ` [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
3 siblings, 1 reply; 8+ messages in thread
From: Bart Van Assche @ 2018-02-09 18:44 UTC (permalink / raw)
To: Jens Axboe
Cc: linux-block, Christoph Hellwig, Jan Kara, Joseph Qi,
Bart Van Assche
Avoid that the following race can occur:
blk_cleanup_queue() blkcg_print_blkgs()
spin_lock_irq(lock) (1) spin_lock_irq(blkg->q->queue_lock) (2,5)
q->queue_lock = &q->__queue_lock (3)
spin_unlock_irq(lock) (4)
spin_unlock_irq(blkg->q->queue_lock) (6)
(1) take driver lock;
(2) busy loop for driver lock;
(3) override driver lock with internal lock;
(4) unlock driver lock;
(5) can take driver lock now;
(6) but unlock internal lock.
This change is safe because only the SCSI core and the NVME core keep
a reference on a request queue after having called blk_cleanup_queue().
Neither driver accesses any of the removed data structures between its
blk_cleanup_queue() and blk_put_queue() calls.
Reported-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
Cc: Jan Kara <jack@suse.com>
---
block/blk-core.c | 31 +++++++++++++++++++++++++++++++
block/blk-sysfs.c | 7 -------
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 41c74b37be85..6febc69a58aa 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -719,6 +719,37 @@ void blk_cleanup_queue(struct request_queue *q)
del_timer_sync(&q->backing_dev_info->laptop_mode_wb_timer);
blk_sync_queue(q);
+ /*
+ * I/O scheduler exit is only safe after the sysfs scheduler attribute
+ * has been removed.
+ */
+ WARN_ON_ONCE(q->kobj.state_in_sysfs);
+
+ /*
+ * Since the I/O scheduler exit code may access cgroup information,
+ * perform I/O scheduler exit before disassociating from the block
+ * cgroup controller.
+ */
+ if (q->elevator) {
+ ioc_clear_queue(q);
+ elevator_exit(q, q->elevator);
+ q->elevator = NULL;
+ }
+
+ /*
+ * Remove all references to @q from the block cgroup controller before
+ * restoring @q->queue_lock to avoid that restoring this pointer causes
+ * e.g. blkcg_print_blkgs() to crash.
+ */
+ blkcg_exit_queue(q);
+
+ /*
+ * Since the cgroup code may dereference the @q->backing_dev_info
+ * pointer, only decrease its reference count after having removed the
+ * association with the block cgroup controller.
+ */
+ bdi_put(q->backing_dev_info);
+
if (q->mq_ops)
blk_mq_free_queue(q);
percpu_ref_exit(&q->q_usage_counter);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index cbea895a5547..fd71a00c9462 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -798,13 +798,6 @@ static void __blk_release_queue(struct work_struct *work)
if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
blk_stat_remove_callback(q, q->poll_cb);
blk_stat_free_callback(q->poll_cb);
- bdi_put(q->backing_dev_info);
- blkcg_exit_queue(q);
-
- if (q->elevator) {
- ioc_clear_queue(q);
- elevator_exit(q, q->elevator);
- }
blk_free_queue_stats(q->stats);
--
2.16.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller
2018-02-09 18:44 ` [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller Bart Van Assche
@ 2018-02-22 2:25 ` Joseph Qi
2018-02-22 3:28 ` Ming Lei
2018-02-22 16:59 ` Bart Van Assche
0 siblings, 2 replies; 8+ messages in thread
From: Joseph Qi @ 2018-02-22 2:25 UTC (permalink / raw)
To: Bart Van Assche, Jens Axboe; +Cc: linux-block, Christoph Hellwig, Jan Kara
Hi Bart,
Sorry for the delayed response since I was on holiday.
On 18/2/10 02:44, Bart Van Assche wrote:
> Avoid that the following race can occur:
>
> blk_cleanup_queue() blkcg_print_blkgs()
> spin_lock_irq(lock) (1) spin_lock_irq(blkg->q->queue_lock) (2,5)
> q->queue_lock = &q->__queue_lock (3)
> spin_unlock_irq(lock) (4)
> spin_unlock_irq(blkg->q->queue_lock) (6)
>
> (1) take driver lock;
> (2) busy loop for driver lock;
> (3) override driver lock with internal lock;
> (4) unlock driver lock;
> (5) can take driver lock now;
> (6) but unlock internal lock.
>
> This change is safe because only the SCSI core and the NVME core keep
> a reference on a request queue after having called blk_cleanup_queue().
> Neither driver accesses any of the removed data structures between its
> blk_cleanup_queue() and blk_put_queue() calls.
>
> Reported-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
> Cc: Jan Kara <jack@suse.com>
> ---
> block/blk-core.c | 31 +++++++++++++++++++++++++++++++
> block/blk-sysfs.c | 7 -------
> 2 files changed, 31 insertions(+), 7 deletions(-)
>
> diff --git a/block/blk-core.c b/block/blk-core.c
> index 41c74b37be85..6febc69a58aa 100644
> --- a/block/blk-core.c
> +++ b/block/blk-core.c
> @@ -719,6 +719,37 @@ void blk_cleanup_queue(struct request_queue *q)
> del_timer_sync(&q->backing_dev_info->laptop_mode_wb_timer);
> blk_sync_queue(q);
>
> + /*
> + * I/O scheduler exit is only safe after the sysfs scheduler attribute
> + * has been removed.
> + */
> + WARN_ON_ONCE(q->kobj.state_in_sysfs);
> +
I notice that several devices such as loop and zram will call
blk_cleanup_queue before del_gendisk, so it will hit this warning. Is
this normal?
Thanks,
Joseph
> + /*
> + * Since the I/O scheduler exit code may access cgroup information,
> + * perform I/O scheduler exit before disassociating from the block
> + * cgroup controller.
> + */
> + if (q->elevator) {
> + ioc_clear_queue(q);
> + elevator_exit(q, q->elevator);
> + q->elevator = NULL;
> + }
> +
> + /*
> + * Remove all references to @q from the block cgroup controller before
> + * restoring @q->queue_lock to avoid that restoring this pointer causes
> + * e.g. blkcg_print_blkgs() to crash.
> + */
> + blkcg_exit_queue(q);
> +
> + /*
> + * Since the cgroup code may dereference the @q->backing_dev_info
> + * pointer, only decrease its reference count after having removed the
> + * association with the block cgroup controller.
> + */
> + bdi_put(q->backing_dev_info);
> +
> if (q->mq_ops)
> blk_mq_free_queue(q);
> percpu_ref_exit(&q->q_usage_counter);
> diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
> index cbea895a5547..fd71a00c9462 100644
> --- a/block/blk-sysfs.c
> +++ b/block/blk-sysfs.c
> @@ -798,13 +798,6 @@ static void __blk_release_queue(struct work_struct *work)
> if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
> blk_stat_remove_callback(q, q->poll_cb);
> blk_stat_free_callback(q->poll_cb);
> - bdi_put(q->backing_dev_info);
> - blkcg_exit_queue(q);
> -
> - if (q->elevator) {
> - ioc_clear_queue(q);
> - elevator_exit(q, q->elevator);
> - }
>
> blk_free_queue_stats(q->stats);
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller
2018-02-22 2:25 ` Joseph Qi
@ 2018-02-22 3:28 ` Ming Lei
2018-02-22 16:59 ` Bart Van Assche
1 sibling, 0 replies; 8+ messages in thread
From: Ming Lei @ 2018-02-22 3:28 UTC (permalink / raw)
To: Joseph Qi
Cc: Bart Van Assche, Jens Axboe, linux-block, Christoph Hellwig,
Jan Kara
On Thu, Feb 22, 2018 at 10:25:28AM +0800, Joseph Qi wrote:
> Hi Bart,
>
> Sorry for the delayed response since I was on holiday.
>
> On 18/2/10 02:44, Bart Van Assche wrote:
> > Avoid that the following race can occur:
> >
> > blk_cleanup_queue() blkcg_print_blkgs()
> > spin_lock_irq(lock) (1) spin_lock_irq(blkg->q->queue_lock) (2,5)
> > q->queue_lock = &q->__queue_lock (3)
> > spin_unlock_irq(lock) (4)
> > spin_unlock_irq(blkg->q->queue_lock) (6)
> >
> > (1) take driver lock;
> > (2) busy loop for driver lock;
> > (3) override driver lock with internal lock;
> > (4) unlock driver lock;
> > (5) can take driver lock now;
> > (6) but unlock internal lock.
> >
> > This change is safe because only the SCSI core and the NVME core keep
> > a reference on a request queue after having called blk_cleanup_queue().
> > Neither driver accesses any of the removed data structures between its
> > blk_cleanup_queue() and blk_put_queue() calls.
> >
> > Reported-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> > Signed-off-by: Bart Van Assche <bart.vanassche@wdc.com>
> > Cc: Jan Kara <jack@suse.com>
> > ---
> > block/blk-core.c | 31 +++++++++++++++++++++++++++++++
> > block/blk-sysfs.c | 7 -------
> > 2 files changed, 31 insertions(+), 7 deletions(-)
> >
> > diff --git a/block/blk-core.c b/block/blk-core.c
> > index 41c74b37be85..6febc69a58aa 100644
> > --- a/block/blk-core.c
> > +++ b/block/blk-core.c
> > @@ -719,6 +719,37 @@ void blk_cleanup_queue(struct request_queue *q)
> > del_timer_sync(&q->backing_dev_info->laptop_mode_wb_timer);
> > blk_sync_queue(q);
> >
> > + /*
> > + * I/O scheduler exit is only safe after the sysfs scheduler attribute
> > + * has been removed.
> > + */
> > + WARN_ON_ONCE(q->kobj.state_in_sysfs);
> > +
> I notice that several devices such as loop and zram will call
> blk_cleanup_queue before del_gendisk, so it will hit this warning. Is
> this normal?
In theory, it should be a bug to call del_gendisk() after blk_cleanup_queue()
since dirty pages can't be flushed to device any more after queue is
cleaned up.
Thanks,
Ming
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller
2018-02-22 2:25 ` Joseph Qi
2018-02-22 3:28 ` Ming Lei
@ 2018-02-22 16:59 ` Bart Van Assche
1 sibling, 0 replies; 8+ messages in thread
From: Bart Van Assche @ 2018-02-22 16:59 UTC (permalink / raw)
To: joseph.qi@linux.alibaba.com, axboe@kernel.dk
Cc: hch@lst.de, linux-block@vger.kernel.org, jack@suse.com
T24gVGh1LCAyMDE4LTAyLTIyIGF0IDEwOjI1ICswODAwLCBKb3NlcGggUWkgd3JvdGU6DQo+IEkg
bm90aWNlIHRoYXQgc2V2ZXJhbCBkZXZpY2VzIHN1Y2ggYXMgbG9vcCBhbmQgenJhbSB3aWxsIGNh
bGwNCj4gYmxrX2NsZWFudXBfcXVldWUgYmVmb3JlIGRlbF9nZW5kaXNrLCBzbyBpdCB3aWxsIGhp
dCB0aGlzIHdhcm5pbmcuIElzDQo+IHRoaXMgbm9ybWFsPw0KDQpIZWxsbyBKb3NlcGgsDQoNClNp
bmNlIHRoZSBkaXNrIG9iamVjdCBoYXMgYSByZWZlcmVuY2UgdG8gdGhlIHF1ZXVlIEkgYWdyZWUg
d2l0aCBNaW5nIHRoYXQgaXQncw0Kd3JvbmcgdG8gY2FsbCBibGtfY2xlYW51cF9xdWV1ZSgpIGJl
Zm9yZSBkZWxfZ2VuZGlzaygpLiBBbnl3YXksIEkgd2lsbCB2ZXJpZnkNCndoZXRoZXIgdGhlcmUg
YXJlIGFueSBibG9jayBkcml2ZXJzIGluIHdoaWNoIHRoZXNlIGNhbGxzIGhhdmUgdG8gYmUgc3dh
cHBlZC4NCg0KQmFydC4=
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup
2018-02-09 18:44 [PATCH v3 0/3] Fix races between blkcg code and request queue initialization and cleanup Bart Van Assche
` (2 preceding siblings ...)
2018-02-09 18:44 ` [PATCH v3 3/3] block: Fix a race between request queue removal and the block cgroup controller Bart Van Assche
@ 2018-02-21 22:51 ` Bart Van Assche
3 siblings, 0 replies; 8+ messages in thread
From: Bart Van Assche @ 2018-02-21 22:51 UTC (permalink / raw)
To: Joseph Qi; +Cc: Jens Axboe, linux-block, Christoph Hellwig, Jan Kara
On 02/09/18 10:44, Bart Van Assche wrote:
> Recently Joseph Qi identified races between the block cgroup code and request
> queue initialization and cleanup. This patch series address these races. Please
> consider these patches for kernel v4.17.
Hello Joseph,
Can you add your Tested-by or Reviewed-by to the patches in this series?
Thanks,
Bart.
^ permalink raw reply [flat|nested] 8+ messages in thread