Skip to content

Commit 5053639

Browse files
sagigrimbergkeithbusch
authored andcommitted
nvmet: fix nvme status code when namespace is disabled
If the user disabled a nvmet namespace, it is removed from the subsystem namespaces list. When nvmet processes a command directed to an nsid that was disabled, it cannot differentiate between a nsid that is disabled vs. a non-existent namespace, and resorts to return NVME_SC_INVALID_NS with the dnr bit set. This translates to a non-retryable status for the host, which translates to a user error. We should expect disabled namespaces to not cause an I/O error in a multipath environment. Address this by searching a configfs item for the namespace nvmet failed to find, and if we found one, conclude that the namespace is disabled (perhaps temporarily). Return NVME_SC_INTERNAL_PATH_ERROR in this case and keep DNR bit cleared. Reported-by: Jirong Feng <[email protected]> Tested-by: Jirong Feng <[email protected]> Signed-off-by: Sagi Grimberg <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 6825bdd commit 5053639

File tree

3 files changed

+18
-1
lines changed

3 files changed

+18
-1
lines changed

drivers/nvme/target/configfs.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,19 @@ static struct configfs_attribute *nvmet_ns_attrs[] = {
754754
NULL,
755755
};
756756

757+
bool nvmet_subsys_nsid_exists(struct nvmet_subsys *subsys, u32 nsid)
758+
{
759+
struct config_item *ns_item;
760+
char name[4] = {};
761+
762+
if (sprintf(name, "%u", nsid) <= 0)
763+
return false;
764+
mutex_lock(&subsys->namespaces_group.cg_subsys->su_mutex);
765+
ns_item = config_group_find_item(&subsys->namespaces_group, name);
766+
mutex_unlock(&subsys->namespaces_group.cg_subsys->su_mutex);
767+
return ns_item != NULL;
768+
}
769+
757770
static void nvmet_ns_release(struct config_item *item)
758771
{
759772
struct nvmet_ns *ns = to_nvmet_ns(item);

drivers/nvme/target/core.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,13 @@ void nvmet_stop_keep_alive_timer(struct nvmet_ctrl *ctrl)
437437
u16 nvmet_req_find_ns(struct nvmet_req *req)
438438
{
439439
u32 nsid = le32_to_cpu(req->cmd->common.nsid);
440+
struct nvmet_subsys *subsys = nvmet_req_subsys(req);
440441

441-
req->ns = xa_load(&nvmet_req_subsys(req)->namespaces, nsid);
442+
req->ns = xa_load(&subsys->namespaces, nsid);
442443
if (unlikely(!req->ns)) {
443444
req->error_loc = offsetof(struct nvme_common_command, nsid);
445+
if (nvmet_subsys_nsid_exists(subsys, nsid))
446+
return NVME_SC_INTERNAL_PATH_ERROR;
444447
return NVME_SC_INVALID_NS | NVME_SC_DNR;
445448
}
446449

drivers/nvme/target/nvmet.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ void nvmet_subsys_disc_changed(struct nvmet_subsys *subsys,
543543
struct nvmet_host *host);
544544
void nvmet_add_async_event(struct nvmet_ctrl *ctrl, u8 event_type,
545545
u8 event_info, u8 log_page);
546+
bool nvmet_subsys_nsid_exists(struct nvmet_subsys *subsys, u32 nsid);
546547

547548
#define NVMET_MIN_QUEUE_SIZE 16
548549
#define NVMET_MAX_QUEUE_SIZE 1024

0 commit comments

Comments
 (0)