summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-09-05 08:58:55 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-09-05 08:58:55 -0700
commitd0fc310b4dfd334023b90d2423818044190c0f68 (patch)
tree77be8fce6a8b0b707e5c1df0a36a9a23fdfbe24b
parent4d7d9486c04d917265f64c55bd23b2cc4fe7749c (diff)
parent00ef2248c5dae08548f7ceb6951b022f89905330 (diff)
downloadlinux-next-d0fc310b4dfd334023b90d2423818044190c0f68.tar.gz
linux-next-d0fc310b4dfd334023b90d2423818044190c0f68.zip
Merge tag 'block-7.3-20260905' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull block fixes from Jens Axboe: - NVMe fixes via Keith: - nvme-tcp fixes for an out-of-bounds write on an over-long PDU - nvmet-tcp, nvmet-rdma and nvme-rdma leak and cleanup-ordering fixes - FDP placement id array racy access fix - nvme-fc double free of fabrics options on nvme_add_ctrl() failure, and a secret leak failure - Fault injection opcode filtering - stale namespace removal during scan - Various other smaller fixes and cleanups - Flag zoned disks with GENHD_FL_NO_PART - Save the page offset gaps in a cloned bio - Fix dma_alignment for large or unreported limits in loop and zloop - Clear VM_MAYWRITE on a read-only ublk char device mmap * tag 'block-7.3-20260905' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: (25 commits) nvme-tcp.h: drop kernel-doc comments, fix a few descriptions nvme-fc: fix double free of fabrics options when nvme_add_ctrl() fails nvmet: reject namespace enable without device path nvmet-auth: Synchronize timeout work during SQ teardown MAINTAINERS: update nvme entry nvmet-tcp: reject unsolicited H2CData PDUs nvme-tcp: defer TLS inline send to io_work nvmet-tcp: fix out-of-bounds write when receiving an over-long PDU nvme-tcp: return -EPROTO for a C2HData on a write nvmet: print namespace IDs as unsigned 32bit value nvme: print namespace IDs as unsigned 32bit value nvme: remove stale namespaces by NSID range during scan nvme: add missing SRCU grace period in error path nvme-fabrics: fix DHCHAP secret leak on parse failure ublk: clear VM_MAYWRITE on read-only ublk char device mmap loop, zloop: fix dma_alignment for large or unreported limits block: save page offset gaps in cloned bio block: flag zoned disks with GENHD_FL_NO_PART nvmet-rdma: fix queue leak when connect backlog is exceeded nvme: add opcode filtering for fault injection ...
-rw-r--r--Documentation/fault-injection/nvme-fault-injection.rst65
-rw-r--r--MAINTAINERS2
-rw-r--r--block/bio.c9
-rw-r--r--block/genhd.c7
-rw-r--r--drivers/block/loop.c8
-rw-r--r--drivers/block/ublk_drv.c6
-rw-r--r--drivers/block/zloop.c8
-rw-r--r--drivers/nvme/host/core.c99
-rw-r--r--drivers/nvme/host/fabrics.c2
-rw-r--r--drivers/nvme/host/fault_inject.c14
-rw-r--r--drivers/nvme/host/fc.c26
-rw-r--r--drivers/nvme/host/nvme.h2
-rw-r--r--drivers/nvme/host/rdma.c18
-rw-r--r--drivers/nvme/host/sysfs.c2
-rw-r--r--drivers/nvme/host/tcp.c12
-rw-r--r--drivers/nvme/host/zns.c4
-rw-r--r--drivers/nvme/target/auth.c6
-rw-r--r--drivers/nvme/target/configfs.c4
-rw-r--r--drivers/nvme/target/core.c9
-rw-r--r--drivers/nvme/target/nvmet.h2
-rw-r--r--drivers/nvme/target/pr.c2
-rw-r--r--drivers/nvme/target/rdma.c15
-rw-r--r--drivers/nvme/target/tcp.c10
-rw-r--r--include/linux/nvme-tcp.h18
24 files changed, 260 insertions, 90 deletions
diff --git a/Documentation/fault-injection/nvme-fault-injection.rst b/Documentation/fault-injection/nvme-fault-injection.rst
index 1d4427890d75..09730acf0163 100644
--- a/Documentation/fault-injection/nvme-fault-injection.rst
+++ b/Documentation/fault-injection/nvme-fault-injection.rst
@@ -176,3 +176,68 @@ Message from dmesg::
secondary_startup_64+0xa4/0xb0
nvme nvme0: Could not set queue count (16385)
nvme nvme0: IO queues not created
+
+Example 4: Inject an error into the first write command
+-------------------------------------------------------
+
+::
+
+ echo 0x01 > /sys/kernel/debug/nvme0n1/fault_inject/opcode
+ echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times
+ echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability
+ dd if=/dev/zero of=/dev/nvme0n1 oflag=direct bs=512 count=1
+
+Expected Result::
+
+ The first write command sent to nvme0n1 fails
+
+Message from dmesg::
+
+ FAULT_INJECTION: forcing a failure.
+ name fault_inject, interval 1, probability 100, space 0, times 1
+ CPU: 4 UID: 0 PID: 0 Comm: swapper/4 Not tainted 7.1.0+ #5 PREEMPT(full)
+ Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-20240910_120124-localhost 04/01/2014
+ Call Trace:
+ <IRQ>
+ dump_stack_lvl+0x6e/0xa0
+ dump_stack+0x10/0x16
+ should_fail_ex+0x461/0x510
+ should_fail+0xb/0x20
+ nvme_should_fail+0x11b/0x240 [nvme_core]
+ nvme_poll_cq+0x6ad/0xb30 [nvme]
+ nvme_irq+0x84/0xe0 [nvme]
+ ? __pfx_nvme_irq+0x10/0x10 [nvme]
+ ? rcu_core+0xa40/0xa90
+ ? __pfx_sched_balance_softirq+0x10/0x10
+ ? debug_smp_processor_id+0x17/0x20
+ ? rcu_is_watching+0x13/0xa0
+ __handle_irq_event_percpu+0x396/0x610
+ handle_irq_event_percpu+0xf/0x90
+ handle_irq_event+0xab/0x110
+ handle_edge_irq+0x1a3/0x210
+ __common_interrupt+0xff/0x170
+ common_interrupt+0x90/0xc0
+ </IRQ>
+ <TASK>
+ asm_common_interrupt+0x27/0x40
+ RIP: 0010:pv_native_safe_halt+0x13/0x20
+ Code: 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 8b 05 0a 2a 58 01 85 c0 7e 07 0f 00 2d ff cc 0d 00 fb f4 <c3> cc 0
+ RSP: 0018:ffff888100a67e40 EFLAGS: 00000242
+ RAX: 0000000000000001 RBX: ffff888100a49c40 RCX: ffffed102b6c645b
+ RDX: ffffed102b6c645b RSI: ffffffff82a0d3c0 RDI: ffffffff81428b9b
+ RBP: ffff888100a67e48 R08: ffffed102b6c645b R09: 0000000000000004
+ R10: ffffed102b6c645a R11: 0000000000000001 R12: 0000000000000000
+ R13: 0000000000000000 R14: ffffed1020149388 R15: dffffc0000000000
+ ? do_idle+0x19b/0x2c0
+ ? default_idle+0x9/0x20
+ arch_cpu_idle+0x9/0x10
+ default_idle_call+0x6b/0xa0
+ do_idle+0x19b/0x2c0
+ ? __pfx_do_idle+0x10/0x10
+ ? complete_with_flags+0x63/0x70
+ cpu_startup_entry+0x55/0x60
+ start_secondary+0x1df/0x1e0
+ common_startup_64+0x13e/0x158
+ </TASK>
+ nvme0n1: Write(0x1) @ LBA 0, 1 blocks, Invalid Command Opcode (sct 0x0 / sc 0x1) DNR
+ operation not supported error, dev nvme0n1, sector 0 op 0x1:(WRITE) flags 0x8800 phys_seg 1 prio class 2
diff --git a/MAINTAINERS b/MAINTAINERS
index 9fb50c3ca080..c9f866debdf0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19563,7 +19563,7 @@ F: include/linux/platform_data/x86/nvidia-wmi-ec-backlight.h
NVM EXPRESS DRIVER
M: Keith Busch <kbusch@kernel.org>
-M: Jens Axboe <axboe@fb.com>
+M: Jens Axboe <axboe@kernel.dk>
M: Christoph Hellwig <hch@lst.de>
M: Sagi Grimberg <sagi@grimberg.me>
L: linux-nvme@lists.infradead.org
diff --git a/block/bio.c b/block/bio.c
index 898b2f5ef8c8..f95b63c0604a 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -859,6 +859,7 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp)
bio->bi_ioprio = bio_src->bi_ioprio;
bio->bi_write_hint = bio_src->bi_write_hint;
bio->bi_write_stream = bio_src->bi_write_stream;
+ bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit;
bio->bi_iter = bio_src->bi_iter;
bio->bi_io_vec = bio_src->bi_io_vec;
@@ -1972,6 +1973,14 @@ struct bio *bio_split(struct bio *bio, int sectors,
bio_advance(bio, split->bi_iter.bi_size);
+ /*
+ * The gap bit is set when splitting to limits and only applies to the
+ * front bio that was split off. The remaining bio will calcualte its
+ * gap value when it is subsequently split to limits, so it is safe to
+ * re-initialize the value back to 0.
+ */
+ bio->bi_bvec_gap_bit = 0;
+
if (bio_flagged(bio, BIO_TRACE_COMPLETION))
bio_set_flag(split, BIO_TRACE_COMPLETION);
diff --git a/block/genhd.c b/block/genhd.c
index f1990c7cdfb9..10ca8b4d6eea 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -448,6 +448,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk,
}
/*
+ * We do not support partitions with zoned block devices, so do not try
+ * to scan the partitions table.
+ */
+ if (blk_queue_is_zoned(disk->queue))
+ disk->flags |= GENHD_FL_NO_PART;
+
+ /*
* If the driver provides an explicit major number it also must provide
* the number of minors numbers supported, and those will be used to
* setup the gendisk.
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6f12976035b0..758c20678bf6 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -458,12 +458,14 @@ static void loop_update_dio_alignment(struct loop_device *lo)
* Use the dio alignment of the file system if provided. The incomoing
* request's bio_vec is forwarded to the backing file unchanged, so its
* required memory alignment becomes the device's dma_alignment when
- * used for direct-io.
+ * used for direct-io. The file system reports zeroed alignments if the
+ * file can't be used for direct-io at all, so fall back to the block
+ * device limits in that case.
*/
if (!vfs_getattr(&file->f_path, &st, STATX_DIOALIGN, 0) &&
- (st.result_mask & STATX_DIOALIGN)) {
+ (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) {
lo->lo_min_dio_size = st.dio_offset_align;
- lo->lo_dio_mem_align = st.dio_mem_align - 1;
+ lo->lo_dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1);
return;
}
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 6c5bec7da97c..e5ba07d8d281 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2653,6 +2653,12 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
if (vma->vm_flags & VM_WRITE)
return -EPERM;
+ /*
+ * The per-queue command buffer is kernel-written ABI; prevent
+ * the daemon from upgrading to writable via mprotect().
+ */
+ vm_flags_clear(vma, VM_MAYWRITE);
+
end = UBLKSRV_CMD_BUF_OFFSET + ub->dev_info.nr_hw_queues * max_sz;
if (phys_off < UBLKSRV_CMD_BUF_OFFSET || phys_off >= end)
return -EINVAL;
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index 4323ac108cae..f0ca221524db 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -1042,12 +1042,14 @@ static int zloop_get_block_size(struct zloop_device *zlo,
* Use the dio alignment of the file system if provided. The incoming
* request's bio_vec is forwarded to the backing file unchanged, so its
* required memory alignment becomes the device's dma_alignment when
- * used for direct-io.
+ * used for direct-io. The file system reports zeroed alignments if the
+ * file can't be used for direct-io at all, so fall back to the block
+ * device limits in that case.
*/
if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
- (st.result_mask & STATX_DIOALIGN)) {
+ (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) {
zlo->block_size = st.dio_offset_align;
- zlo->dio_mem_align = st.dio_mem_align - 1;
+ zlo->dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1);
} else if (sb_bdev) {
zlo->block_size = bdev_physical_block_size(sb_bdev);
zlo->dio_mem_align = bdev_dma_alignment(sb_bdev);
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 1322c678f4eb..758245c799a1 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -155,8 +155,6 @@ static const struct class nvme_ns_chr_class = {
};
static void nvme_put_subsystem(struct nvme_subsystem *subsys);
-static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
- unsigned nsid);
static void nvme_update_keep_alive(struct nvme_ctrl *ctrl,
struct nvme_command *cmd);
static int nvme_get_log_lsi(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page,
@@ -1612,7 +1610,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl,
}
if (nvme_multi_css(ctrl) && !csi_seen) {
- dev_warn(ctrl->device, "Command set not reported for nsid:%d\n",
+ dev_warn(ctrl->device, "Command set not reported for nsid:%u\n",
info->nsid);
status = -EINVAL;
}
@@ -2341,14 +2339,6 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
size_t size;
int i, ret;
- /*
- * The FDP configuration is static for the lifetime of the namespace,
- * so return immediately if we've already registered this namespace's
- * streams.
- */
- if (head->nr_plids)
- return 0;
-
ret = nvme_get_features(ctrl, NVME_FEAT_FDP, info->endgid, NULL, 0,
&fdp);
if (ret) {
@@ -2395,6 +2385,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
for (i = 0; i < head->nr_plids; i++)
head->plids[i] = le16_to_cpu(ruhs->ruhsd[i].pid);
+ head->write_stream_granularity = min(info->runs, U32_MAX);
free:
kfree(ruhs);
return ret;
@@ -2442,12 +2433,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
goto out;
}
- if (ns->ctrl->ctratt & NVME_CTRL_ATTR_FDPS) {
- ret = nvme_query_fdp_info(ns, info);
- if (ret < 0)
- goto out;
- }
-
if (nvme_invalid_lba_sz(le64_to_cpu(id->nsze),
id->lbaf[lbaf].ds - SECTOR_SHIFT, &capacity)) {
dev_warn_once(ns->ctrl->device,
@@ -2468,9 +2453,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
if (!nvme_update_disk_info(ns, id, nvm, &lim))
capacity = 0;
+ /*
+ * A failed zone info query leaves zi zero-initialized, so skip the
+ * zoned limits update instead of configuring the queue from it.
+ * During a revalidation that keeps the zone geometry the queue was
+ * last validated with; on a first scan the namespace is registered
+ * without zoned limits, so that it is still available as a handle
+ * for admin commands.
+ */
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
- ns->head->ids.csi == NVME_CSI_ZNS)
- nvme_update_zone_info(ns, &lim, &zi);
+ ns->head->ids.csi == NVME_CSI_ZNS) {
+ if (zi.zone_size)
+ nvme_update_zone_info(ns, &lim, &zi);
+ else
+ dev_warn(ns->ctrl->device,
+ "zone info query failed for nsid %u, %s\n",
+ ns->head->ns_id,
+ blk_queue_is_zoned(ns->disk->queue) ?
+ "keeping the previous zone limits" :
+ "not enabling zoned mode");
+ }
if ((ns->ctrl->vwc & NVME_CTRL_VWC_PRESENT) && !info->no_vwc)
lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA;
@@ -2490,10 +2492,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
capacity = 0;
lim.max_write_streams = ns->head->nr_plids;
- if (lim.max_write_streams)
- lim.write_stream_granularity = min(info->runs, U32_MAX);
- else
- lim.write_stream_granularity = 0;
+ lim.write_stream_granularity = ns->head->write_stream_granularity;
/*
* Only set the DEAC bit if the device guarantees that reads from
@@ -4001,10 +4000,11 @@ static void nvme_add_ns_cdev(struct nvme_ns *ns)
set_bit(NVME_NS_CDEV_LIVE, &ns->flags);
}
-static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
+static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ns *ns,
struct nvme_ns_info *info)
- __must_hold(&ctrl->subsys->lock)
+ __must_hold(&ns->ctrl->subsys->lock)
{
+ struct nvme_ctrl *ctrl = ns->ctrl;
struct nvme_ns_head *head;
size_t size = sizeof(*head);
int ret = -ENOMEM;
@@ -4032,6 +4032,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
ratelimit_state_init(&head->rs_nuse, 5 * HZ, 1);
ratelimit_set_flags(&head->rs_nuse, RATELIMIT_MSG_ON_RELEASE);
kref_init(&head->ref);
+ ns->head = head;
if (head->ids.csi) {
ret = nvme_get_effects_log(ctrl, head->ids.csi, &head->effects);
@@ -4040,21 +4041,30 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
} else
head->effects = ctrl->effects;
+ if (ctrl->ctratt & NVME_CTRL_ATTR_FDPS) {
+ ret = nvme_query_fdp_info(ns, info);
+ if (ret < 0)
+ goto out_cleanup_srcu;
+ }
+
ret = nvme_mpath_alloc_disk(ctrl, head);
if (ret)
- goto out_cleanup_srcu;
+ goto out_cleanup_fdp;
list_add_tail(&head->entry, &ctrl->subsys->nsheads);
kref_get(&ctrl->subsys->ref);
return head;
+out_cleanup_fdp:
+ kfree(head->plids);
out_cleanup_srcu:
cleanup_srcu_struct(&head->srcu);
out_ida_remove:
ida_free(&ctrl->subsys->ns_ida, head->instance);
out_free_head:
kfree(head);
+ ns->head = NULL;
out:
if (ret > 0)
ret = blk_status_to_errno(nvme_error_status(ret));
@@ -4116,13 +4126,13 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
((ns->ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) &&
info->is_shared)) {
dev_err(ctrl->device,
- "ignoring nsid %d because of duplicate IDs\n",
+ "ignoring nsid %u because of duplicate IDs\n",
info->nsid);
return ret;
}
dev_err(ctrl->device,
- "clearing duplicate IDs for nsid %d\n", info->nsid);
+ "clearing duplicate IDs for nsid %u\n", info->nsid);
dev_err(ctrl->device,
"use of /dev/disk/by-id/ may cause data corruption\n");
memset(&info->ids.nguid, 0, sizeof(info->ids.nguid));
@@ -4137,11 +4147,11 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
ret = nvme_subsys_check_duplicate_ids(ctrl->subsys, &info->ids);
if (ret) {
dev_err(ctrl->device,
- "duplicate IDs in subsystem for nsid %d\n",
+ "duplicate IDs in subsystem for nsid %u\n",
info->nsid);
goto out_unlock;
}
- head = nvme_alloc_ns_head(ctrl, info);
+ head = nvme_alloc_ns_head(ns, info);
if (IS_ERR(head)) {
ret = PTR_ERR(head);
goto out_unlock;
@@ -4151,20 +4161,20 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
if ((!info->is_shared || !head->shared) &&
!list_empty(&head->list)) {
dev_err(ctrl->device,
- "Duplicate unshared namespace %d\n",
+ "Duplicate unshared namespace %u\n",
info->nsid);
goto out_put_ns_head;
}
if (!nvme_ns_ids_equal(&head->ids, &info->ids)) {
dev_err(ctrl->device,
- "IDs don't match for shared namespace %d\n",
+ "IDs don't match for shared namespace %u\n",
info->nsid);
goto out_put_ns_head;
}
if (!multipath) {
dev_warn(ctrl->device,
- "Found shared namespace %d, but multipathing not supported.\n",
+ "Found shared namespace %u, but multipathing not supported.\n",
info->nsid);
dev_warn_once(ctrl->device,
"Shared namespace support requires core_nvme.multipath=Y.\n");
@@ -4333,6 +4343,9 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info)
last_path = true;
}
mutex_unlock(&ctrl->subsys->lock);
+
+ /* guarantee not available in head->list */
+ synchronize_srcu(&ns->head->srcu);
if (last_path)
nvme_put_ns_head(ns->head);
nvme_put_ns_head(ns->head);
@@ -4410,7 +4423,7 @@ static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info)
if (!nvme_ns_ids_equal(&ns->head->ids, &info->ids)) {
dev_err(ns->ctrl->device,
- "identifiers changed for nsid %d\n", ns->head->ns_id);
+ "identifiers changed for nsid %u\n", ns->head->ns_id);
goto out;
}
@@ -4437,7 +4450,7 @@ static void nvme_scan_ns(struct nvme_ctrl *ctrl, unsigned nsid)
if (info.ids.csi != NVME_CSI_NVM && !nvme_multi_css(ctrl)) {
dev_warn(ctrl->device,
- "command set not reported for nsid: %d\n", nsid);
+ "command set not reported for nsid: %u\n", nsid);
return;
}
@@ -4501,15 +4514,16 @@ static void nvme_scan_ns_async(void *data, async_cookie_t cookie)
nvme_scan_ns(scan_info->ctrl, nsid);
}
-static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
- unsigned nsid)
+static void nvme_remove_nsid_range(struct nvme_ctrl *ctrl, u32 start, u32 end)
{
struct nvme_ns *ns, *next;
LIST_HEAD(rm_list);
mutex_lock(&ctrl->namespaces_lock);
list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) {
- if (ns->head->ns_id > nsid) {
+ if (ns->head->ns_id >= end)
+ break;
+ if (ns->head->ns_id > start) {
list_del_rcu(&ns->list);
synchronize_srcu(&ctrl->srcu);
list_add_tail_rcu(&ns->list, &rm_list);
@@ -4559,13 +4573,14 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl)
goto out;
async_schedule_domain(nvme_scan_ns_async, &scan_info,
&domain);
- while (++prev < nsid)
- nvme_ns_remove_by_nsid(ctrl, prev);
+ if (prev + 1 < nsid)
+ nvme_remove_nsid_range(ctrl, prev, nsid);
+ prev = max(prev + 1, nsid);
}
async_synchronize_full_domain(&domain);
}
out:
- nvme_remove_invalid_namespaces(ctrl, prev);
+ nvme_remove_nsid_range(ctrl, prev, UINT_MAX);
free:
async_synchronize_full_domain(&domain);
kfree(ns_list);
@@ -4585,7 +4600,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl)
for (i = 1; i <= nn; i++)
nvme_scan_ns(ctrl, i);
- nvme_remove_invalid_namespaces(ctrl, nn);
+ nvme_remove_nsid_range(ctrl, nn, UINT_MAX);
}
static void nvme_clear_changed_ns_log(struct nvme_ctrl *ctrl)
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index fd5abd04e080..59f823dfbbcc 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -1028,6 +1028,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
pr_err("Invalid DH-CHAP secret %s\n", p);
+ kfree_sensitive(p);
ret = -EINVAL;
goto out;
}
@@ -1042,6 +1043,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
pr_err("Invalid DH-CHAP secret %s\n", p);
+ kfree_sensitive(p);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/nvme/host/fault_inject.c b/drivers/nvme/host/fault_inject.c
index 105d6cb41c72..783e1999fef4 100644
--- a/drivers/nvme/host/fault_inject.c
+++ b/drivers/nvme/host/fault_inject.c
@@ -42,9 +42,11 @@ void nvme_fault_inject_init(struct nvme_fault_inject *fault_inj,
}
fault_inj->parent = parent;
- /* create debugfs for status code and dont_retry */
+ /* create debugfs for opcode, status code, and dont_retry */
+ fault_inj->opcode = 0xffff;
fault_inj->status = NVME_SC_INVALID_OPCODE;
fault_inj->dont_retry = true;
+ debugfs_create_x16("opcode", 0600, dir, &fault_inj->opcode);
debugfs_create_x16("status", 0600, dir, &fault_inj->status);
debugfs_create_bool("dont_retry", 0600, dir, &fault_inj->dont_retry);
}
@@ -59,6 +61,7 @@ void nvme_should_fail(struct request *req)
{
struct gendisk *disk = req->q->disk;
struct nvme_fault_inject *fault_inject = NULL;
+ struct nvme_command *cmd = nvme_req(req)->cmd;
u16 status;
if (disk) {
@@ -72,7 +75,14 @@ void nvme_should_fail(struct request *req)
fault_inject = &nvme_req(req)->ctrl->fault_inject;
}
- if (fault_inject && should_fail(&fault_inject->attr, 1)) {
+ if (!fault_inject)
+ return;
+
+ if (fault_inject->opcode <= 0xff &&
+ fault_inject->opcode != cmd->common.opcode)
+ return;
+
+ if (should_fail(&fault_inject->attr, 1)) {
/* inject status code and DNR bit */
status = fault_inject->status;
if (fault_inject->dont_retry)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 023710e08e0d..48454cb7a0fc 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2364,9 +2364,15 @@ nvme_fc_ctrl_free(struct kref *ref)
struct nvme_fc_ctrl *ctrl =
container_of(ref, struct nvme_fc_ctrl, ref);
unsigned long flags;
+ bool owns_opts;
- /* remove from rport list */
+ /*
+ * Presence on the rport list means nvme_fc_init_ctrl() completed,
+ * and with it ownership of the fabrics options passed to it. If it
+ * failed instead, the options still belong to nvmf_create_ctrl().
+ */
spin_lock_irqsave(&ctrl->rport->lock, flags);
+ owns_opts = !list_empty(&ctrl->ctrl_list);
list_del(&ctrl->ctrl_list);
spin_unlock_irqrestore(&ctrl->rport->lock, flags);
@@ -2376,7 +2382,7 @@ nvme_fc_ctrl_free(struct kref *ref)
nvme_fc_rport_put(ctrl->rport);
ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
- if (ctrl->ctrl.opts)
+ if (owns_opts)
nvmf_free_options(ctrl->ctrl.opts);
kfree(ctrl);
}
@@ -3575,14 +3581,14 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum);
- goto fail_ctrl;
+ goto fail_unlist;
}
if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to schedule initial connect\n",
ctrl->cnum);
- goto fail_ctrl;
+ goto fail_unlist;
}
flush_delayed_work(&ctrl->connect_work);
@@ -3593,14 +3599,22 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
return &ctrl->ctrl;
+fail_unlist:
+ /*
+ * Leaving the list hands the options back to nvmf_create_ctrl();
+ * see nvme_fc_ctrl_free(). Re-init so that list_empty() there
+ * reports the controller as unlisted.
+ */
+ spin_lock_irqsave(&rport->lock, flags);
+ list_del_init(&ctrl->ctrl_list);
+ spin_unlock_irqrestore(&rport->lock, flags);
+
fail_ctrl:
nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
cancel_work_sync(&ctrl->ioerr_work);
cancel_work_sync(&ctrl->ctrl.reset_work);
cancel_delayed_work_sync(&ctrl->connect_work);
- ctrl->ctrl.opts = NULL;
-
if (ctrl->ctrl.admin_tagset)
nvme_remove_admin_tag_set(&ctrl->ctrl);
/* initiate nvme ctrl ref counting teardown */
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 75e5d5a8a77c..2cff9fcbf740 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -323,6 +323,7 @@ struct nvme_fault_inject {
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
struct fault_attr attr;
struct dentry *parent;
+ u16 opcode;
bool dont_retry; /* DNR, do not retry */
u16 status; /* status code */
#endif
@@ -571,6 +572,7 @@ struct nvme_ns_head {
u16 nr_plids;
u16 *plids;
+ u32 write_stream_granularity;
#ifdef CONFIG_NVME_MULTIPATH
struct bio_list requeue_list
__guarded_by(&requeue_lock);
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 538385138e45..9cb811a2ce1f 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -2036,7 +2036,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
struct ib_device *dev;
bool queue_ready = test_bit(NVME_RDMA_Q_LIVE, &queue->flags);
blk_status_t ret;
- int err;
+ int err = 0;
WARN_ON_ONCE(rq->tag < 0);
@@ -2092,16 +2092,18 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
err_unmap:
nvme_rdma_unmap_data(queue, rq);
err:
- if (err == -EIO)
- ret = nvme_host_path_error(rq);
- else if (err == -ENOMEM || err == -EAGAIN)
- ret = BLK_STS_RESOURCE;
- else
- ret = BLK_STS_IOERR;
- nvme_cleanup_cmd(rq);
+ if (err != -EIO) {
+ nvme_cleanup_cmd(rq);
+ if (err == -ENOMEM || err == -EAGAIN)
+ ret = BLK_STS_RESOURCE;
+ else
+ ret = BLK_STS_IOERR;
+ }
unmap_qe:
ib_dma_unmap_single(dev, req->sqe.dma, sizeof(struct nvme_command),
DMA_TO_DEVICE);
+ if (err == -EIO)
+ return nvme_host_path_error(rq);
return ret;
}
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index abf8edaae371..02a2490a9ed7 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -166,7 +166,7 @@ static DEVICE_ATTR_RO(eui);
static ssize_t nsid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id);
+ return sysfs_emit(buf, "%u\n", dev_to_ns_head(dev)->ns_id);
}
static DEVICE_ATTR_RO(nsid);
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 5fda9661bdb7..921934028e0b 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -413,8 +413,13 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
* if we're the first on the send_list and we can try to send
* directly, otherwise queue io_work. Also, only do that if we
* are on the same cpu, so we don't introduce contention.
+ *
+ * TLS kTLS send takes ctx->tx_lock while blk_mq holds set->srcu.
+ * lockdep reports circular locking via elevator_lock. Defer TLS
+ * sends to the io workqueue instead of inline from this path.
*/
if (queue->io_cpu == raw_smp_processor_id() &&
+ !nvme_tcp_queue_tls(queue) &&
empty && mutex_trylock(&queue->send_mutex)) {
nvme_tcp_send_all(queue);
mutex_unlock(&queue->send_mutex);
@@ -684,6 +689,13 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
return -ENOENT;
}
+ if (rq_data_dir(rq) != READ) {
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d tag %#x unexpected data for a write\n",
+ nvme_tcp_queue_id(queue), rq->tag);
+ return -EPROTO;
+ }
+
req = blk_mq_rq_to_pdu(rq);
if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) {
dev_err(queue->ctrl->ctrl.device,
diff --git a/drivers/nvme/host/zns.c b/drivers/nvme/host/zns.c
index 2a152e87bd76..e31ec6f4f94f 100644
--- a/drivers/nvme/host/zns.c
+++ b/drivers/nvme/host/zns.c
@@ -48,12 +48,12 @@ int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf,
NVME_CMD_EFFECTS_CSUPP)) {
if (test_and_clear_bit(NVME_NS_FORCE_RO, &ns->flags))
dev_warn(ns->ctrl->device,
- "Zone Append supported for zoned namespace:%d. Remove read-only mode\n",
+ "Zone Append supported for zoned namespace:%u. Remove read-only mode\n",
ns->head->ns_id);
} else {
set_bit(NVME_NS_FORCE_RO, &ns->flags);
dev_warn(ns->ctrl->device,
- "Zone Append not supported for zoned namespace:%d. Forcing to read-only mode\n",
+ "Zone Append not supported for zoned namespace:%u. Forcing to read-only mode\n",
ns->head->ns_id);
}
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index edb9627d97b0..a55319bcdbd1 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -238,6 +238,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
sq->dhchap_skey = NULL;
}
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq)
+{
+ cancel_delayed_work_sync(&sq->auth_expired_work);
+ nvmet_auth_sq_free(sq);
+}
+
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
{
ctrl->shash_id = 0;
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 413ee2d16d29..6286e38436dd 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -814,7 +814,7 @@ static ssize_t nvmet_ns_resv_enable_store(struct config_item *item,
mutex_lock(&ns->subsys->lock);
if (ns->enabled) {
- pr_err("the ns:%d is already enabled.\n", ns->nsid);
+ pr_err("the ns:%u is already enabled.\n", ns->nsid);
mutex_unlock(&ns->subsys->lock);
return -EINVAL;
}
@@ -880,7 +880,7 @@ static struct config_group *nvmet_ns_make(struct config_group *group,
goto out;
config_group_init_type_name(&ns->group, name, &nvmet_ns_type);
- pr_info("adding nsid %d to subsystem %s\n", nsid, subsys->subsysnqn);
+ pr_info("adding nsid %u to subsystem %s\n", nsid, subsys->subsysnqn);
return &ns->group;
out:
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index d74c01c98f19..43871a8f56ca 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -558,7 +558,7 @@ static void nvmet_p2pmem_ns_add_p2p(struct nvmet_ctrl *ctrl,
if (ret < 0)
pci_dev_put(p2p_dev);
- pr_info("using p2pmem on %s for nsid %d\n", pci_name(p2p_dev),
+ pr_info("using p2pmem on %s for nsid %u\n", pci_name(p2p_dev),
ns->nsid);
}
@@ -591,6 +591,11 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
if (ns->enabled)
goto out_unlock;
+ if (!ns->device_path) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
ret = nvmet_bdev_ns_enable(ns);
if (ret == -ENOTBLK)
ret = nvmet_file_ns_enable(ns);
@@ -980,7 +985,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
wait_for_completion(&sq->confirm_done);
wait_for_completion(&sq->free_done);
percpu_ref_exit(&sq->ref);
- nvmet_auth_sq_free(sq);
+ nvmet_auth_sq_destroy(sq);
nvmet_cq_put(sq->cq);
/*
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index e362d7913a38..dbda55895f4f 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -924,6 +924,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset);
void nvmet_auth_sq_init(struct nvmet_sq *sq);
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_free(struct nvmet_sq *sq);
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq);
int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id);
bool nvmet_check_auth_status(struct nvmet_req *req);
int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
@@ -950,6 +951,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq)
}
static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {};
static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
+static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {};
static inline bool nvmet_check_auth_status(struct nvmet_req *req)
{
return true;
diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c
index 0948a690a1c0..09d8c63f5680 100644
--- a/drivers/nvme/target/pr.c
+++ b/drivers/nvme/target/pr.c
@@ -145,7 +145,7 @@ static void nvmet_pr_add_resv_log(struct nvmet_ctrl *ctrl, u8 log_type,
log.nsid = cpu_to_le32(nsid);
if (!kfifo_put(&log_mgr->log_queue, log)) {
- pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%d\n",
+ pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%u\n",
ctrl->cntlid, log_type, nsid);
log_mgr->lost_count++;
}
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 49cba690914a..ffa1e172c259 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1631,19 +1631,13 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
mutex_unlock(&nvmet_rdma_queue_mutex);
if (pending > NVMET_RDMA_BACKLOG) {
ret = NVME_SC_CONNECT_CTRL_BUSY;
- goto put_device;
+ goto free_queue;
}
}
ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);
- if (ret) {
- /*
- * Don't destroy the cm_id in free path, as we implicitly
- * destroy the cm_id here with non-zero ret code.
- */
- queue->cm_id = NULL;
+ if (ret)
goto free_queue;
- }
mutex_lock(&nvmet_rdma_queue_mutex);
list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list);
@@ -1652,6 +1646,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
return 0;
free_queue:
+ /*
+ * Don't destroy the cm_id in free path, as we implicitly
+ * destroy the cm_id here with non-zero ret code.
+ */
+ queue->cm_id = NULL;
nvmet_rdma_free_queue(queue);
put_device:
kref_put(&ndev->ref, nvmet_rdma_free_dev);
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index e4f603b2ace7..e59810175262 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -103,6 +103,7 @@ enum nvmet_tcp_recv_state {
enum {
NVMET_TCP_F_INIT_FAILED = (1 << 0),
+ NVMET_TCP_F_R2T_SENT = (1 << 1),
};
struct nvmet_tcp_cmd {
@@ -776,6 +777,7 @@ static int nvmet_try_send_r2t(struct nvmet_tcp_cmd *cmd, bool last_in_batch)
return -EAGAIN;
cmd->queue->snd_cmd = NULL;
+ cmd->flags |= NVMET_TCP_F_R2T_SENT;
return 1;
}
@@ -1009,6 +1011,12 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
cmd = &queue->connect;
}
+ if (unlikely(!(cmd->flags & NVMET_TCP_F_R2T_SENT))) {
+ pr_err("queue %d: unsolicited H2CData (ttag %u)\n",
+ queue->idx, data->ttag);
+ goto err_proto;
+ }
+
if (le32_to_cpu(data->data_offset) != cmd->rbytes_done) {
pr_err("ttag %u unexpected data offset %u (expected %u)\n",
data->ttag, le32_to_cpu(data->data_offset),
@@ -1244,6 +1252,8 @@ recv:
}
queue->left = hdr->hlen - queue->offset + hdgst;
+ if (queue->left > sizeof(queue->pdu) - queue->offset)
+ return -EPROTO;
goto recv;
}
diff --git a/include/linux/nvme-tcp.h b/include/linux/nvme-tcp.h
index e435250fcb4d..859338da8573 100644
--- a/include/linux/nvme-tcp.h
+++ b/include/linux/nvme-tcp.h
@@ -77,7 +77,7 @@ struct nvme_tcp_hdr {
__le32 plen;
};
-/**
+/*
* struct nvme_tcp_icreq_pdu - nvme tcp initialize connection request pdu
*
* @hdr: pdu generic header
@@ -95,7 +95,7 @@ struct nvme_tcp_icreq_pdu {
__u8 rsvd2[112];
};
-/**
+/*
* struct nvme_tcp_icresp_pdu - nvme tcp initialize connection response pdu
*
* @hdr: pdu common header
@@ -113,12 +113,13 @@ struct nvme_tcp_icresp_pdu {
__u8 rsvd[112];
};
-/**
+/*
* struct nvme_tcp_term_pdu - nvme tcp terminate connection pdu
*
* @hdr: pdu common header
* @fes: fatal error status
- * @fei: fatal error information
+ * @feil: fatal error information (low 16 bits)
+ * @feih: fatal error information (high 16 bits)
*/
struct nvme_tcp_term_pdu {
struct nvme_tcp_hdr hdr;
@@ -128,7 +129,7 @@ struct nvme_tcp_term_pdu {
__u8 rsvd[10];
};
-/**
+/*
* struct nvme_tcp_cmd_pdu - nvme tcp command capsule pdu
*
* @hdr: pdu common header
@@ -139,10 +140,9 @@ struct nvme_tcp_cmd_pdu {
struct nvme_command cmd;
};
-/**
+/*
* struct nvme_tcp_rsp_pdu - nvme tcp response capsule pdu
*
- * @hdr: pdu common header
* @hdr: nvme-tcp generic header
* @cqe: nvme completion queue entry
*/
@@ -151,7 +151,7 @@ struct nvme_tcp_rsp_pdu {
struct nvme_completion cqe;
};
-/**
+/*
* struct nvme_tcp_r2t_pdu - nvme tcp ready-to-transfer pdu
*
* @hdr: pdu common header
@@ -169,7 +169,7 @@ struct nvme_tcp_r2t_pdu {
__u8 rsvd[4];
};
-/**
+/*
* struct nvme_tcp_data_pdu - nvme tcp data pdu
*
* @hdr: pdu common header