summaryrefslogtreecommitdiff
path: root/drivers/nvme/host/core.c
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2026-06-05 05:18:58 -0600
committerJens Axboe <axboe@kernel.dk>2026-06-05 05:18:58 -0600
commited60c09f292f1383bbcf79dcf61b6257bbb3a503 (patch)
treefeb17db78c3ee8d8680e75b24eefd56530b0332f /drivers/nvme/host/core.c
parent3f1eccd37282de91efd0575ee8e212af4bde39b1 (diff)
parent3c8c284dfcdfce81a02fe3c911196d9876468ae4 (diff)
Merge tag 'nvme-7.2-2026-06-04' of git://git.infradead.org/nvme into for-7.2/block
Pull NVMe updates from Keith: "- Per-controller timeouts - Multipath telemetry - Namespace format validation - Various other fixes" * tag 'nvme-7.2-2026-06-04' of git://git.infradead.org/nvme: (34 commits) nvme: export controller reconnect event count via sysfs nvme: export controller reset event count via sysfs nvme: export I/O failure count when no path is available via sysfs nvme: export I/O requeue count when no path is usable via sysfs nvme: export command error counters via sysfs nvme: export multipath failover count via sysfs nvme: export command retry count via sysfs nvme: add diag attribute group under sysfs nvme-tcp: lockdep: use dynamic lockdep keys per socket instance nvme-tcp: move nvme_tcp_reclassify_socket() nvme: validate FDP configuration descriptor sizes nvmet-auth: validate reply message payload bounds against transfer length nvme: refresh multipath head zoned limits from path limits nvme: fix FDP fdpcidx bounds check nvme-tcp: Use WQ_PERCPU explicitly if wq_unbound is false. nvmet: fix pre-auth out-of-bounds heap read in Discovery Get Log Page nvme-multipath: set BIO_REMAPPED on bios remapped to per-path namespace disks nvme-multipath: require exact iopolicy names for module parameter nvme-multipath: pass NS head to nvme_mpath_revalidate_paths() nvme-pci: fix out-of-bounds access in nvme_setup_descriptor_pools ...
Diffstat (limited to 'drivers/nvme/host/core.c')
-rw-r--r--drivers/nvme/host/core.c74
1 files changed, 53 insertions, 21 deletions
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index dc388e24caad..efaddab8296e 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -323,6 +323,7 @@ static void nvme_retry_req(struct request *req)
{
unsigned long delay = 0;
u16 crd;
+ struct nvme_ns *ns = req->q->queuedata;
/* The mask and shift result must be <= 3 */
crd = (nvme_req(req)->status & NVME_STATUS_CRD) >> 11;
@@ -330,6 +331,9 @@ static void nvme_retry_req(struct request *req)
delay = nvme_req(req)->ctrl->crdt[crd - 1] * 100;
nvme_req(req)->retries++;
+ if (ns)
+ atomic_long_inc(&ns->retries);
+
blk_mq_requeue_request(req, false);
blk_mq_delay_kick_requeue_list(req->q, delay);
}
@@ -434,11 +438,19 @@ static inline void nvme_end_req_zoned(struct request *req)
static inline void __nvme_end_req(struct request *req)
{
- if (unlikely(nvme_req(req)->status && !(req->rq_flags & RQF_QUIET))) {
+ struct nvme_ns *ns = req->q->queuedata;
+ struct nvme_request *nr = nvme_req(req);
+
+ if (unlikely(nr->status && !(req->rq_flags & RQF_QUIET))) {
if (blk_rq_is_passthrough(req))
nvme_log_err_passthru(req);
else
nvme_log_error(req);
+
+ if (ns)
+ atomic_long_inc(&ns->errors);
+ else
+ atomic_long_inc(&nr->ctrl->errors);
}
nvme_end_req_zoned(req);
nvme_trace_bio_complete(req);
@@ -584,6 +596,7 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
case NVME_CTRL_NEW:
case NVME_CTRL_LIVE:
changed = true;
+ atomic_long_inc(&ctrl->nr_reset);
fallthrough;
default:
break;
@@ -729,10 +742,8 @@ void nvme_init_request(struct request *req, struct nvme_command *cmd)
struct nvme_ns *ns = req->q->disk->private_data;
logging_enabled = ns->head->passthru_err_log_enabled;
- req->timeout = NVME_IO_TIMEOUT;
} else { /* no queuedata implies admin queue */
logging_enabled = nr->ctrl->passthru_err_log_enabled;
- req->timeout = NVME_ADMIN_TIMEOUT;
}
if (!logging_enabled)
@@ -2263,7 +2274,7 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
}
n = le16_to_cpu(h->numfdpc) + 1;
- if (fdp_idx > n) {
+ if (fdp_idx >= n) {
dev_warn(ctrl->device, "FDP index:%d out of range:%d\n",
fdp_idx, n);
/* Proceed without registering FDP streams */
@@ -2275,14 +2286,16 @@ static int nvme_query_fdp_granularity(struct nvme_ctrl *ctrl,
desc = log;
end = log + size - sizeof(*h);
for (i = 0; i < fdp_idx; i++) {
- log += le16_to_cpu(desc->dsze);
- desc = log;
- if (log >= end) {
+ u16 dsze = le16_to_cpu(desc->dsze);
+
+ if (!dsze || log + dsze > end) {
dev_warn(ctrl->device,
- "FDP invalid config descriptor list\n");
+ "FDP invalid config descriptor at index %d\n", i);
ret = 0;
goto out;
}
+ log += dsze;
+ desc = log;
}
if (le32_to_cpu(desc->nrg) > 1) {
@@ -2409,12 +2422,22 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
goto out;
}
+ if (id->lbaf[lbaf].ds < SECTOR_SHIFT ||
+ check_shl_overflow(le64_to_cpu(id->nsze),
+ id->lbaf[lbaf].ds - SECTOR_SHIFT,
+ &capacity)) {
+ dev_warn_once(ns->ctrl->device,
+ "invalid LBA data size %u, skipping namespace\n",
+ id->lbaf[lbaf].ds);
+ ret = -ENODEV;
+ goto out;
+ }
+
lim = queue_limits_start_update(ns->disk->queue);
memflags = blk_mq_freeze_queue(ns->disk->queue);
ns->head->lba_shift = id->lbaf[lbaf].ds;
ns->head->nuse = le64_to_cpu(id->nuse);
- capacity = nvme_lba_to_sect(ns->head, le64_to_cpu(id->nsze));
nvme_set_ctrl_limits(ns->ctrl, &lim, false);
nvme_configure_metadata(ns->ctrl, ns->head, id, nvm, info);
nvme_set_chunk_sectors(ns, id, &lim);
@@ -2483,6 +2506,14 @@ out:
return ret;
}
+static void nvme_stack_zone_resources(struct queue_limits *t,
+ const struct queue_limits *b)
+{
+ t->max_open_zones = min_not_zero(t->max_open_zones, b->max_open_zones);
+ t->max_active_zones =
+ min_not_zero(t->max_active_zones, b->max_active_zones);
+}
+
static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
{
bool unsupported = false;
@@ -2549,6 +2580,8 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
lim.io_opt = ns_lim->io_opt;
queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
ns->head->disk->disk_name);
+ if (lim.features & BLK_FEAT_ZONED)
+ nvme_stack_zone_resources(&lim, ns_lim);
if (unsupported)
ns->head->disk->flags |= GENHD_FL_HIDDEN;
else
@@ -2559,7 +2592,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
set_capacity_and_notify(ns->head->disk, get_capacity(ns->disk));
set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
- nvme_mpath_revalidate_paths(ns);
+ nvme_mpath_revalidate_paths(ns->head);
blk_mq_unfreeze_queue(ns->head->disk->queue, memflags);
}
@@ -3922,7 +3955,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
int ret = -ENOMEM;
#ifdef CONFIG_NVME_MULTIPATH
- size += num_possible_nodes() * sizeof(struct nvme_ns *);
+ size += nr_node_ids * sizeof(struct nvme_ns *);
#endif
head = kzalloc(size, GFP_KERNEL);
@@ -4205,6 +4238,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info)
mutex_unlock(&ctrl->namespaces_lock);
goto out_unlink_ns;
}
+ blk_queue_rq_timeout(ns->queue, ctrl->io_timeout);
nvme_ns_add_to_ctrl_list(ns);
mutex_unlock(&ctrl->namespaces_lock);
synchronize_srcu(&ctrl->srcu);
@@ -4890,12 +4924,7 @@ int nvme_alloc_admin_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
if (ret)
return ret;
- /*
- * If a previous admin queue exists (e.g., from before a reset),
- * put it now before allocating a new one to avoid orphaning it.
- */
- if (ctrl->admin_q)
- blk_put_queue(ctrl->admin_q);
+ WARN_ON_ONCE(ctrl->admin_q);
ctrl->admin_q = blk_mq_alloc_queue(set, NULL, NULL);
if (IS_ERR(ctrl->admin_q)) {
@@ -4933,10 +4962,8 @@ void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl)
*/
nvme_stop_keep_alive(ctrl);
blk_mq_destroy_queue(ctrl->admin_q);
- if (ctrl->ops->flags & NVME_F_FABRICS) {
+ if (ctrl->fabrics_q)
blk_mq_destroy_queue(ctrl->fabrics_q);
- blk_put_queue(ctrl->fabrics_q);
- }
blk_mq_free_tag_set(ctrl->admin_tagset);
}
EXPORT_SYMBOL_GPL(nvme_remove_admin_tag_set);
@@ -5078,6 +5105,8 @@ static void nvme_free_ctrl(struct device *dev)
if (ctrl->admin_q)
blk_put_queue(ctrl->admin_q);
+ if (ctrl->fabrics_q)
+ blk_put_queue(ctrl->fabrics_q);
if (!subsys || ctrl->instance != subsys->instance)
ida_free(&nvme_instance_ida, ctrl->instance);
nvme_free_cels(ctrl);
@@ -5142,6 +5171,8 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
memset(&ctrl->ka_cmd, 0, sizeof(ctrl->ka_cmd));
ctrl->ka_cmd.common.opcode = nvme_admin_keep_alive;
ctrl->ka_last_check_time = jiffies;
+ ctrl->admin_timeout = NVME_ADMIN_TIMEOUT;
+ ctrl->io_timeout = NVME_IO_TIMEOUT;
BUILD_BUG_ON(NVME_DSM_MAX_RANGES * sizeof(struct nvme_dsm_range) >
PAGE_SIZE);
@@ -5248,8 +5279,9 @@ void nvme_unfreeze(struct nvme_ctrl *ctrl)
}
EXPORT_SYMBOL_GPL(nvme_unfreeze);
-int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl, long timeout)
+int nvme_wait_freeze_timeout(struct nvme_ctrl *ctrl)
{
+ long timeout = ctrl->io_timeout;
struct nvme_ns *ns;
int srcu_idx;