Skip to content

Commit 88bac2c

Browse files
Achkinazi, Igorkeithbusch
authored andcommitted
nvme-multipath: set BIO_REMAPPED on bios remapped to per-path namespace disks
When nvme_ns_head_submit_bio() remaps a bio from the multipath head to a per-path namespace, bio_set_dev() clears BIO_REMAPPED. The remapped bio is then resubmitted through submit_bio_noacct() which calls bio_check_eod() because BIO_REMAPPED is not set. This races with nvme_ns_remove() which zeroes the per-path capacity before synchronize_srcu(): CPU 0 (IO submission) --------------------- srcu_read_lock() nvme_find_path() -> ns [NVME_NS_READY is set] CPU 1 (namespace removal) ------------------------- clear_bit(NVME_NS_READY) set_capacity(ns->disk, 0) synchronize_srcu() <- blocks CPU 0 (IO submission) --------------------- bio_set_dev(bio, ns->disk->part0) [clears BIO_REMAPPED] submit_bio_noacct(bio) -> bio_check_eod() sees capacity=0 -> bio fails with IO error The SRCU read lock prevents synchronize_srcu() from completing, but does not prevent set_capacity(0) from executing. The bio fails the EOD check before it reaches the NVMe driver, so nvme_failover_req() never gets a chance to redirect it to another path of multipath. IO errors are reported to the application despite another path being available. On older kernels (before commit 0b64682 "block: skip unnecessary checks for split bio"), the same race was also reachable through split remainders resubmitted via submit_bio_noacct(). Fix this by setting BIO_REMAPPED after bio_set_dev() in nvme_ns_head_submit_bio(). This skips bio_check_eod() on the per-path device; the EOD check already passed on the multipath head. NVMe per-path namespace devices are always whole disks (bd_partno=0), so the blk_partition_remap() skip also gated by BIO_REMAPPED is a no-op. The flag does not persist across failover and cannot go stale if the namespace geometry changes between attempts: nvme_failover_req() calls bio_set_dev() to redirect the bio back to the multipath head, which clears BIO_REMAPPED. When nvme_requeue_work() resubmits through submit_bio_noacct(), bio_check_eod() runs normally against the current capacity. Same approach as commit 3a905c3 ("block: skip bio_check_eod for partition-remapped bios"). Fixes: a7c7f7b ("nvme: use bio_set_dev to assign ->bi_bdev") Cc: stable@vger.kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Igor Achkinazi <igor.achkinazi@dell.com> Signed-off-by: Keith Busch <kbusch@kernel.org>
1 parent 4cf0697 commit 88bac2c

1 file changed

Lines changed: 6 additions & 0 deletions

File tree

drivers/nvme/host/multipath.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,12 @@ static void nvme_ns_head_submit_bio(struct bio *bio)
521521
ns = nvme_find_path(head);
522522
if (likely(ns)) {
523523
bio_set_dev(bio, ns->disk->part0);
524+
/*
525+
* Use BIO_REMAPPED to skip bio_check_eod() when this bio
526+
* enters submit_bio_noacct() for the per-path device. The EOD
527+
* check already passed on the multipath head.
528+
*/
529+
bio_set_flag(bio, BIO_REMAPPED);
524530
bio->bi_opf |= REQ_NVME_MPATH;
525531
trace_block_bio_remap(bio, disk_devt(ns->head->disk),
526532
bio->bi_iter.bi_sector);

0 commit comments

Comments
 (0)