[PATCH v2 2/7] nvme-multipath: numa support for marginal paths

From: Jesse Taube

Date: Wed Sep 02 2026 - 16:31:27 EST


FPIN LI (link integrity) messages are received when the attached
fabric detects hardware errors. In response to these messages I/O
should be directed away from the affected ports, and only used
if no other non-marginal paths are available.
To handle this a new controller flag 'NVME_CTRL_MARGINAL' is added
which will cause the multipath scheduler to skip these paths when
checking for 'optimized' paths.

Signed-off-by: Jesse Taube <jtaubepe@xxxxxxxxxx>
---
This is a distinct change from the previous commit
which treated marginal paths as non-optimized but still usable.
This changes the priority of marginal paths to be lower than
non-optimized paths.
V10 -> V1:
- New commit
V1 -> V2:
- Rewrite
---
drivers/nvme/host/multipath.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 9b9a657fa330..dbf09cdda815 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -305,16 +305,45 @@ static bool nvme_path_is_disabled(struct nvme_ns *ns)
return false;
}

+static bool nvme_path_is_usable(struct nvme_ns *ns)
+{
+ /* Only NVME_ANA_OPTIMIZED and NVME_ANA_NONOPTIMIZED are usable */
+ return !nvme_path_is_disabled(ns) &&
+ (ns->ana_state == NVME_ANA_OPTIMIZED ||
+ ns->ana_state == NVME_ANA_NONOPTIMIZED);
+}
+
+static bool nvme_all_paths_marginal(struct nvme_ns_head *head)
+{
+ struct nvme_ns *ns;
+
+ list_for_each_entry_srcu(ns, &head->list, siblings,
+ srcu_read_lock_held(&head->srcu)) {
+ /* skip paths which can not be used */
+ if (!nvme_path_is_usable(ns))
+ continue;
+ if (!nvme_ctrl_is_marginal(ns->ctrl))
+ return false;
+ }
+
+ return true;
+}
+
static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
{
int found_distance = INT_MAX, fallback_distance = INT_MAX, distance;
struct nvme_ns *found = NULL, *fallback = NULL, *ns;
+ bool need_marginal = nvme_all_paths_marginal(head);

list_for_each_entry_srcu(ns, &head->list, siblings,
srcu_read_lock_held(&head->srcu)) {
if (nvme_path_is_disabled(ns))
continue;

+ /* Skip marginal paths unless we need to use them */
+ if (!need_marginal && nvme_ctrl_is_marginal(ns->ctrl))
+ continue;
+
if (ns->ctrl->numa_node != NUMA_NO_NODE &&
READ_ONCE(head->subsys->iopolicy) == NVME_IOPOLICY_NUMA)
distance = node_distance(node, ns->ctrl->numa_node);
@@ -339,6 +368,7 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
}
}

+ /* No optimized path found, use the fallback */
if (!found)
found = fallback;
if (found)
@@ -444,7 +474,8 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
static inline bool nvme_path_is_optimized(struct nvme_ns *ns)
{
return nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE &&
- ns->ana_state == NVME_ANA_OPTIMIZED;
+ ns->ana_state == NVME_ANA_OPTIMIZED &&
+ !nvme_ctrl_is_marginal(ns->ctrl);
}

static struct nvme_ns *nvme_numa_path(struct nvme_ns_head *head)
--
2.55.0