nvme-multipath: defer partition scanning
[ Upstream commit 1f021341eef41e77a633186e9be5223de2ce5d48 ] We need to suppress the partition scan from occuring within the controller's scan_work context. If a path error occurs here, the IO will wait until a path becomes available or all paths are torn down, but that action also occurs within scan_work, so it would deadlock. Defer the partion scan to a different context that does not block scan_work. Reported-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
f17c880a47
commit
60de2e03f9
@ -463,6 +463,20 @@ static int nvme_add_ns_head_cdev(struct nvme_ns_head *head)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void nvme_partition_scan_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct nvme_ns_head *head =
|
||||||
|
container_of(work, struct nvme_ns_head, partition_scan_work);
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!test_and_clear_bit(GD_SUPPRESS_PART_SCAN,
|
||||||
|
&head->disk->state)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mutex_lock(&head->disk->open_mutex);
|
||||||
|
bdev_disk_changed(head->disk, false);
|
||||||
|
mutex_unlock(&head->disk->open_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static void nvme_requeue_work(struct work_struct *work)
|
static void nvme_requeue_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct nvme_ns_head *head =
|
struct nvme_ns_head *head =
|
||||||
@ -489,6 +503,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
|||||||
bio_list_init(&head->requeue_list);
|
bio_list_init(&head->requeue_list);
|
||||||
spin_lock_init(&head->requeue_lock);
|
spin_lock_init(&head->requeue_lock);
|
||||||
INIT_WORK(&head->requeue_work, nvme_requeue_work);
|
INIT_WORK(&head->requeue_work, nvme_requeue_work);
|
||||||
|
INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a multipath node if the subsystems supports multiple controllers.
|
* Add a multipath node if the subsystems supports multiple controllers.
|
||||||
@ -504,6 +519,16 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
head->disk->fops = &nvme_ns_head_ops;
|
head->disk->fops = &nvme_ns_head_ops;
|
||||||
head->disk->private_data = head;
|
head->disk->private_data = head;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to suppress the partition scan from occuring within the
|
||||||
|
* controller's scan_work context. If a path error occurs here, the IO
|
||||||
|
* will wait until a path becomes available or all paths are torn down,
|
||||||
|
* but that action also occurs within scan_work, so it would deadlock.
|
||||||
|
* Defer the partion scan to a different context that does not block
|
||||||
|
* scan_work.
|
||||||
|
*/
|
||||||
|
set_bit(GD_SUPPRESS_PART_SCAN, &head->disk->state);
|
||||||
sprintf(head->disk->disk_name, "nvme%dn%d",
|
sprintf(head->disk->disk_name, "nvme%dn%d",
|
||||||
ctrl->subsys->instance, head->instance);
|
ctrl->subsys->instance, head->instance);
|
||||||
|
|
||||||
@ -552,6 +577,7 @@ static void nvme_mpath_set_live(struct nvme_ns *ns)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nvme_add_ns_head_cdev(head);
|
nvme_add_ns_head_cdev(head);
|
||||||
|
kblockd_schedule_work(&head->partition_scan_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&head->lock);
|
mutex_lock(&head->lock);
|
||||||
@ -851,6 +877,12 @@ void nvme_mpath_shutdown_disk(struct nvme_ns_head *head)
|
|||||||
kblockd_schedule_work(&head->requeue_work);
|
kblockd_schedule_work(&head->requeue_work);
|
||||||
if (test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
|
if (test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
|
||||||
nvme_cdev_del(&head->cdev, &head->cdev_device);
|
nvme_cdev_del(&head->cdev, &head->cdev_device);
|
||||||
|
/*
|
||||||
|
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
|
||||||
|
* to allow multipath to fail all I/O.
|
||||||
|
*/
|
||||||
|
synchronize_srcu(&head->srcu);
|
||||||
|
kblockd_schedule_work(&head->requeue_work);
|
||||||
del_gendisk(head->disk);
|
del_gendisk(head->disk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -862,6 +894,7 @@ void nvme_mpath_remove_disk(struct nvme_ns_head *head)
|
|||||||
/* make sure all pending bios are cleaned up */
|
/* make sure all pending bios are cleaned up */
|
||||||
kblockd_schedule_work(&head->requeue_work);
|
kblockd_schedule_work(&head->requeue_work);
|
||||||
flush_work(&head->requeue_work);
|
flush_work(&head->requeue_work);
|
||||||
|
flush_work(&head->partition_scan_work);
|
||||||
put_disk(head->disk);
|
put_disk(head->disk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,6 +460,7 @@ struct nvme_ns_head {
|
|||||||
struct bio_list requeue_list;
|
struct bio_list requeue_list;
|
||||||
spinlock_t requeue_lock;
|
spinlock_t requeue_lock;
|
||||||
struct work_struct requeue_work;
|
struct work_struct requeue_work;
|
||||||
|
struct work_struct partition_scan_work;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
#define NVME_NSHEAD_DISK_LIVE 0
|
#define NVME_NSHEAD_DISK_LIVE 0
|
||||||
|
Loading…
Reference in New Issue
Block a user