* [PATCH v2 1/7] nvme: add NVME_CTRL_MARGINAL flag
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 2/7] nvme-multipath: numa support for marginal paths Jesse Taube
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block
From: Bryan Gurney <bgurney@redhat.com>
Add a new controller flag, NVME_CTRL_MARGINAL, to help multipath I/O
policies to react to a path that is set to a "marginal" state.
The "marginal" flag is initialized to false in `nvme_init_ctrl` and
cleared in `nvme_fc_ctrl_connectivity_loss` before re-association.
It is only cleared there, not in the generic `nvme_reset_ctrl` path,
because the marginal condition reflects a physical link problem.
An arbitrary controller reset does not signal that a hardware fault has
been resolved. Calling `nvme_fc_ctrl_connectivity_loss` means the FC
association has failed and will be re-established, so the prior link
quality state is no longer meaningful.
Signed-off-by: Bryan Gurney <bgurney@redhat.com>
---
V10 -> V1:
- No change
V1 -> V2:
- Add nvme_ctrl_assign_marginal
- Fix nvme_fc_ctrl_connectivity_loss clearing wrong bit.
- Update commit message
---
drivers/nvme/host/core.c | 1 +
drivers/nvme/host/fc.c | 4 ++++
drivers/nvme/host/nvme.h | 11 +++++++++++
3 files changed, 16 insertions(+)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 453c1f0b2dd0..4ce3c0573707 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -5165,6 +5165,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
WRITE_ONCE(ctrl->state, NVME_CTRL_NEW);
ctrl->passthru_err_log_enabled = false;
clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags);
+ nvme_ctrl_assign_marginal(ctrl, false);
spin_lock_init(&ctrl->lock);
mutex_init(&ctrl->namespaces_lock);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 04363b9c4489..7886c0dcc626 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -788,6 +788,10 @@ nvme_fc_ctrl_connectivity_loss(struct nvme_fc_ctrl *ctrl)
"Reconnect", ctrl->cnum);
set_bit(ASSOC_FAILED, &ctrl->flags);
+
+ /* clear 'marginal' flag as controller will be reset */
+ nvme_ctrl_assign_marginal(&ctrl->ctrl, false);
+
nvme_reset_ctrl(&ctrl->ctrl);
}
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 824651cc898d..640cb4747ba2 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -329,6 +329,7 @@ enum nvme_ctrl_flags {
NVME_CTRL_SKIP_ID_CNS_CS = 4,
NVME_CTRL_DIRTY_CAPABILITY = 5,
NVME_CTRL_FROZEN = 6,
+ NVME_CTRL_MARGINAL = 7,
};
struct nvme_ctrl {
@@ -479,6 +480,16 @@ static inline enum nvme_ctrl_state nvme_ctrl_state(struct nvme_ctrl *ctrl)
return READ_ONCE(ctrl->state);
}
+static inline bool nvme_ctrl_is_marginal(struct nvme_ctrl *ctrl)
+{
+ return test_bit(NVME_CTRL_MARGINAL, &ctrl->flags);
+}
+
+static inline void nvme_ctrl_assign_marginal(struct nvme_ctrl *ctrl, bool marginal)
+{
+ assign_bit(NVME_CTRL_MARGINAL, &ctrl->flags, marginal);
+}
+
enum nvme_iopolicy {
NVME_IOPOLICY_NUMA,
NVME_IOPOLICY_RR,
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 2/7] nvme-multipath: numa support for marginal paths
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
2026-09-02 20:05 ` [PATCH v2 1/7] nvme: add NVME_CTRL_MARGINAL flag Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 3/7] nvme-multipath: queue-depth " Jesse Taube
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block
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@redhat.com>
---
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
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 3/7] nvme-multipath: queue-depth support for marginal paths
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
2026-09-02 20:05 ` [PATCH v2 1/7] nvme: add NVME_CTRL_MARGINAL flag Jesse Taube
2026-09-02 20:05 ` [PATCH v2 2/7] nvme-multipath: numa support for marginal paths Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 4/7] nvme-multipath: round-robin " Jesse Taube
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block
From: John Meneghini <jmeneghi@redhat.com>
Exclude marginal paths from queue-depth io policy. In the case where all
paths are marginal and no optimized or non-optimized path is found, we
fall back and select the best marginal path.
Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
Signed-off-by: John Meneghini <jmeneghi@redhat.com>
---
V10 -> V1:
- New commit
V1 -> V2:
- Rewrite
---
drivers/nvme/host/multipath.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index dbf09cdda815..f28797deab68 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -439,12 +439,17 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
struct nvme_ns *best_opt = NULL, *best_nonopt = NULL, *ns;
unsigned int min_depth_opt = UINT_MAX, min_depth_nonopt = UINT_MAX;
unsigned int depth;
+ 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;
+
depth = atomic_read(&ns->ctrl->nr_active);
switch (ns->ana_state) {
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 4/7] nvme-multipath: round-robin support for marginal paths
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
` (2 preceding siblings ...)
2026-09-02 20:05 ` [PATCH v2 3/7] nvme-multipath: queue-depth " Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 5/7] nvme: sysfs: emit the marginal path state in show_state() Jesse Taube
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block
Exclude marginal paths from round-robin io policy. In the case where all
paths are marginal and no optimized or non-optimized path is found, we
fall back and perform round-robin on the marginal paths.
Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
V10 -> V1:
- New commit
V1 -> V2:
- Rewrite
---
drivers/nvme/host/multipath.c | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index f28797deab68..738e3c742b74 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -392,22 +392,29 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
int node = numa_node_id();
struct nvme_ns *old = srcu_dereference(head->current_path[node],
&head->srcu);
+ bool need_marginal;
if (unlikely(!old))
return __nvme_find_path(head, node);
if (list_is_singular(&head->list)) {
- if (nvme_path_is_disabled(old))
- return NULL;
- return old;
+ if (nvme_path_is_usable(old))
+ return old;
+ return NULL;
}
+ need_marginal = nvme_all_paths_marginal(head);
+
for (ns = nvme_next_ns(head, old);
ns && ns != old;
ns = nvme_next_ns(head, ns)) {
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->ana_state == NVME_ANA_OPTIMIZED) {
found = ns;
goto out;
@@ -419,12 +426,23 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
/*
* The loop above skips the current path for round-robin semantics.
* Fall back to the current path if either:
- * - no other optimized path found and current is optimized,
+ * - no other optimized path found and current is,
+ * optimized and not marginal.
+ * - no other non-marginal path found and current is,
+ * optimized and marginal.
* - no other usable path found and current is usable.
*/
- if (!nvme_path_is_disabled(old) &&
- (old->ana_state == NVME_ANA_OPTIMIZED ||
- (!found && old->ana_state == NVME_ANA_NONOPTIMIZED)))
+ /* no other usable path found and current is usable. */
+ if (nvme_path_is_usable(old) && !found)
+ return old;
+ /*
+ * - no other optimized path found and current is,
+ * optimized and not marginal.
+ * - no other non-marginal path found and current is,
+ * optimized and marginal.
+ */
+ if (nvme_path_is_usable(old) && old->ana_state == NVME_ANA_OPTIMIZED &&
+ (!nvme_ctrl_is_marginal(old->ctrl) || need_marginal))
return old;
if (!found)
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 5/7] nvme: sysfs: emit the marginal path state in show_state()
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
` (3 preceding siblings ...)
2026-09-02 20:05 ` [PATCH v2 4/7] nvme-multipath: round-robin " Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 6/7] nvme-fc: add nvme_fc_set_remoteport_fpin() Jesse Taube
2026-09-02 20:05 ` [PATCH v2 7/7] nvme: fcloop: Add set_marginal_rport to sysfs Jesse Taube
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block, Muneendra Kumar
From: Bryan Gurney <bgurney@redhat.com>
If a controller has received a link integrity or congestion event, and
has the NVME_CTRL_MARGINAL flag set, emit "marginal" in the state
instead of "live", to identify the marginal paths.
Co-developed-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: John Meneghini <jmeneghi@redhat.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
Tested-by: Muneendra Kumar <muneendra.kumar@broadcom.com>
Signed-off-by: Bryan Gurney <bgurney@redhat.com>
---
V10 -> V1:
- No change
V1 -> V2:
- Only emit "marginal" if port is "live"
---
drivers/nvme/host/sysfs.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index 75b2d69b5957..9ede6b37f27a 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -526,6 +526,9 @@ static ssize_t nvme_sysfs_show_state(struct device *dev,
[NVME_CTRL_DEAD] = "dead",
};
+ if (state == NVME_CTRL_LIVE && nvme_ctrl_is_marginal(ctrl))
+ return sysfs_emit(buf, "%s\n", "marginal");
+
if (state < ARRAY_SIZE(state_name) && state_name[state])
return sysfs_emit(buf, "%s\n", state_name[state]);
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 6/7] nvme-fc: add nvme_fc_set_remoteport_fpin()
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
` (4 preceding siblings ...)
2026-09-02 20:05 ` [PATCH v2 5/7] nvme: sysfs: emit the marginal path state in show_state() Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
2026-09-02 20:05 ` [PATCH v2 7/7] nvme: fcloop: Add set_marginal_rport to sysfs Jesse Taube
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block, Hannes Reinecke
Add nvme_fc_set_remoteport_fpin() and supporting functions. This
function is called by the SCSI FC transport and driver layer to set or
clear the 'marginal' path status for a specific rport.
Co-developed-by: Hannes Reinecke <hare@kernel.org>
Signed-off-by: Hannes Reinecke <hare@kernel.org>
Signed-off-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
V10 -> V1:
- Remove nvme_fc_modify_rport_fpin_state
- Use struct nvme_fc_remote_port instead of wwpn and wwnn
V1 -> V2:
- Use nvme_ctrl_assign_marginal over set/clear_bit
---
drivers/nvme/host/fc.c | 13 +++++++++++++
include/linux/nvme-fc-driver.h | 2 ++
2 files changed, 15 insertions(+)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 7886c0dcc626..c4810d048b87 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -895,6 +895,19 @@ nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *portptr,
}
EXPORT_SYMBOL_GPL(nvme_fc_set_remoteport_devloss);
+void
+nvme_fc_set_remoteport_fpin(struct nvme_fc_remote_port *portptr, bool marginal)
+{
+ struct nvme_fc_rport *rport = remoteport_to_rport(portptr);
+ struct nvme_fc_ctrl *ctrl;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rport->lock, flags);
+ list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list)
+ nvme_ctrl_assign_marginal(&ctrl->ctrl, marginal);
+ spin_unlock_irqrestore(&rport->lock, flags);
+}
+EXPORT_SYMBOL_GPL(nvme_fc_set_remoteport_fpin);
/* *********************** FC-NVME DMA Handling **************************** */
diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h
index 9f6acadfe0c8..95d79386d126 100644
--- a/include/linux/nvme-fc-driver.h
+++ b/include/linux/nvme-fc-driver.h
@@ -536,6 +536,8 @@ void nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport);
int nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *remoteport,
u32 dev_loss_tmo);
+void nvme_fc_set_remoteport_fpin(struct nvme_fc_remote_port *portptr, bool marginal);
+
/*
* Routine called to pass a NVME-FC LS request, received by the lldd,
* to the nvme-fc transport.
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v2 7/7] nvme: fcloop: Add set_marginal_rport to sysfs
2026-09-02 20:05 [PATCH v2 0/7] nvme-fc: FPIN link integrity handling Jesse Taube
` (5 preceding siblings ...)
2026-09-02 20:05 ` [PATCH v2 6/7] nvme-fc: add nvme_fc_set_remoteport_fpin() Jesse Taube
@ 2026-09-02 20:05 ` Jesse Taube
6 siblings, 0 replies; 8+ messages in thread
From: Jesse Taube @ 2026-09-02 20:05 UTC (permalink / raw)
To: linux-nvme
Cc: linux-scsi, Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
Christoph Hellwig, Sagi Grimberg, Justin Tee, Naresh Gottumukkala,
Paul Ely, Chaitanya Kulkarni, James E.J. Bottomley,
Martin K. Petersen, Nilesh Javali, GR-QLogic-Storage-Upstream,
Hannes Reinecke, Jesse Taube, Gustavo A. R. Silva, John Meneghini,
Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
linux-doc, linux-kernel, linux-block
To allow testing of multipath failover, add a sysfs attribute to set a
remote port as marginal. This will allow the fcloop LLDD to set the
marginal flag on a remote port, simulating a marginal link.
Example:
Turn on marginal for a remote port matching wwnn and wwpn:
`echo 'wwnn=0x200000109b5f2956,wwpn=0x100000109b5f2956,marginal=1' >
/sys/class/fcloop/ctl/set_marginal_rport`
Turn off marginal for a remote port matching wwnn and wwpn:
`echo 'wwnn=0x200000109b5f2956,wwpn=0x100000109b5f2956,marginal=0' >
/sys/class/fcloop/ctl/set_marginal_rport`
Suggested-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
V10 -> V1:
- New patch
V1 -> V2:
- Fix reference count leak in error path
---
drivers/nvme/target/fcloop.c | 50 ++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index b63af3b643a6..9977c2a71bd5 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/parser.h>
#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_els.h>
#include "../host/nvme.h"
#include "../target/nvmet.h"
@@ -21,6 +22,7 @@ enum {
NVMF_OPT_FCADDR = 1 << 3,
NVMF_OPT_LPWWNN = 1 << 4,
NVMF_OPT_LPWWPN = 1 << 5,
+ NVMF_OPT_MARGINAL = 1 << 6,
};
struct fcloop_ctrl_options {
@@ -31,6 +33,7 @@ struct fcloop_ctrl_options {
u32 fcaddr;
u64 lpwwnn;
u64 lpwwpn;
+ u32 marginal;
};
static const match_table_t opt_tokens = {
@@ -40,6 +43,7 @@ static const match_table_t opt_tokens = {
{ NVMF_OPT_FCADDR, "fcaddr=%x" },
{ NVMF_OPT_LPWWNN, "lpwwnn=%s" },
{ NVMF_OPT_LPWWPN, "lpwwpn=%s" },
+ { NVMF_OPT_MARGINAL, "marginal=%d" },
{ NVMF_OPT_ERR, NULL }
};
@@ -120,6 +124,13 @@ fcloop_parse_options(struct fcloop_ctrl_options *opts,
}
opts->lpwwpn = token64;
break;
+ case NVMF_OPT_MARGINAL:
+ if (match_int(args, &token)) {
+ ret = -EINVAL;
+ goto out_free_options;
+ }
+ opts->marginal = token;
+ break;
default:
pr_warn("unknown parameter or missing value '%s'\n", p);
ret = -EINVAL;
@@ -199,6 +210,9 @@ fcloop_parse_nm_options(struct device *dev, u64 *nname, u64 *pname,
#define TGTPORT_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN)
+#define MARGINAL_OPTS (NVMF_OPT_WWNN | NVMF_OPT_WWPN | \
+ NVMF_OPT_MARGINAL)
+
static DEFINE_SPINLOCK(fcloop_lock);
static LIST_HEAD(fcloop_lports);
@@ -1663,6 +1677,40 @@ fcloop_set_cmd_drop(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t
+fcloop_set_marginal_rport(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fcloop_nport *nport;
+ struct fcloop_ctrl_options opts = {};
+ unsigned long flags;
+ int ret;
+
+ ret = fcloop_parse_options(&opts, buf);
+ if (ret)
+ return ret;
+
+ /* everything there ? */
+ if ((opts.mask & MARGINAL_OPTS) != MARGINAL_OPTS)
+ return -EINVAL;
+
+ nport = fcloop_nport_lookup(opts.wwnn, opts.wwpn);
+ if (!nport)
+ return -ENOENT;
+
+ spin_lock_irqsave(&fcloop_lock, flags);
+ if (!nport->rport || !nport->rport->remoteport) {
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+ fcloop_nport_put(nport);
+ return -ENOENT;
+ }
+
+ nvme_fc_set_remoteport_fpin(nport->rport->remoteport, opts.marginal);
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+ fcloop_nport_put(nport);
+
+ return count;
+}
static DEVICE_ATTR(add_local_port, 0200, NULL, fcloop_create_local_port);
static DEVICE_ATTR(del_local_port, 0200, NULL, fcloop_delete_local_port);
@@ -1671,6 +1719,7 @@ static DEVICE_ATTR(del_remote_port, 0200, NULL, fcloop_delete_remote_port);
static DEVICE_ATTR(add_target_port, 0200, NULL, fcloop_create_target_port);
static DEVICE_ATTR(del_target_port, 0200, NULL, fcloop_delete_target_port);
static DEVICE_ATTR(set_cmd_drop, 0200, NULL, fcloop_set_cmd_drop);
+static DEVICE_ATTR(set_marginal_rport, 0200, NULL, fcloop_set_marginal_rport);
static struct attribute *fcloop_dev_attrs[] = {
&dev_attr_add_local_port.attr,
@@ -1680,6 +1729,7 @@ static struct attribute *fcloop_dev_attrs[] = {
&dev_attr_add_target_port.attr,
&dev_attr_del_target_port.attr,
&dev_attr_set_cmd_drop.attr,
+ &dev_attr_set_marginal_rport.attr,
NULL
};
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread