Skip to content

Commit 1f02134

Browse files
committed
nvme-multipath: defer partition scanning
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 <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 0ce96a6 commit 1f02134

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

drivers/nvme/host/multipath.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,20 @@ static int nvme_add_ns_head_cdev(struct nvme_ns_head *head)
579579
return ret;
580580
}
581581

582+
static void nvme_partition_scan_work(struct work_struct *work)
583+
{
584+
struct nvme_ns_head *head =
585+
container_of(work, struct nvme_ns_head, partition_scan_work);
586+
587+
if (WARN_ON_ONCE(!test_and_clear_bit(GD_SUPPRESS_PART_SCAN,
588+
&head->disk->state)))
589+
return;
590+
591+
mutex_lock(&head->disk->open_mutex);
592+
bdev_disk_changed(head->disk, false);
593+
mutex_unlock(&head->disk->open_mutex);
594+
}
595+
582596
static void nvme_requeue_work(struct work_struct *work)
583597
{
584598
struct nvme_ns_head *head =
@@ -605,6 +619,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
605619
bio_list_init(&head->requeue_list);
606620
spin_lock_init(&head->requeue_lock);
607621
INIT_WORK(&head->requeue_work, nvme_requeue_work);
622+
INIT_WORK(&head->partition_scan_work, nvme_partition_scan_work);
608623

609624
/*
610625
* Add a multipath node if the subsystems supports multiple controllers.
@@ -628,6 +643,16 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
628643
return PTR_ERR(head->disk);
629644
head->disk->fops = &nvme_ns_head_ops;
630645
head->disk->private_data = head;
646+
647+
/*
648+
* We need to suppress the partition scan from occuring within the
649+
* controller's scan_work context. If a path error occurs here, the IO
650+
* will wait until a path becomes available or all paths are torn down,
651+
* but that action also occurs within scan_work, so it would deadlock.
652+
* Defer the partion scan to a different context that does not block
653+
* scan_work.
654+
*/
655+
set_bit(GD_SUPPRESS_PART_SCAN, &head->disk->state);
631656
sprintf(head->disk->disk_name, "nvme%dn%d",
632657
ctrl->subsys->instance, head->instance);
633658
return 0;
@@ -654,6 +679,7 @@ static void nvme_mpath_set_live(struct nvme_ns *ns)
654679
return;
655680
}
656681
nvme_add_ns_head_cdev(head);
682+
kblockd_schedule_work(&head->partition_scan_work);
657683
}
658684

659685
mutex_lock(&head->lock);
@@ -973,14 +999,14 @@ void nvme_mpath_shutdown_disk(struct nvme_ns_head *head)
973999
return;
9741000
if (test_and_clear_bit(NVME_NSHEAD_DISK_LIVE, &head->flags)) {
9751001
nvme_cdev_del(&head->cdev, &head->cdev_device);
1002+
/*
1003+
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
1004+
* to allow multipath to fail all I/O.
1005+
*/
1006+
synchronize_srcu(&head->srcu);
1007+
kblockd_schedule_work(&head->requeue_work);
9761008
del_gendisk(head->disk);
9771009
}
978-
/*
979-
* requeue I/O after NVME_NSHEAD_DISK_LIVE has been cleared
980-
* to allow multipath to fail all I/O.
981-
*/
982-
synchronize_srcu(&head->srcu);
983-
kblockd_schedule_work(&head->requeue_work);
9841010
}
9851011

9861012
void nvme_mpath_remove_disk(struct nvme_ns_head *head)
@@ -990,6 +1016,7 @@ void nvme_mpath_remove_disk(struct nvme_ns_head *head)
9901016
/* make sure all pending bios are cleaned up */
9911017
kblockd_schedule_work(&head->requeue_work);
9921018
flush_work(&head->requeue_work);
1019+
flush_work(&head->partition_scan_work);
9931020
put_disk(head->disk);
9941021
}
9951022

drivers/nvme/host/nvme.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ struct nvme_ns_head {
494494
struct bio_list requeue_list;
495495
spinlock_t requeue_lock;
496496
struct work_struct requeue_work;
497+
struct work_struct partition_scan_work;
497498
struct mutex lock;
498499
unsigned long flags;
499500
#define NVME_NSHEAD_DISK_LIVE 0

0 commit comments

Comments
 (0)