Linux block layer
 help / color / mirror / Atom feed
* [PATCH 00/13] nvme-fc: FPIN link integrity handling
@ 2026-08-12 18:12 Jesse Taube
  2026-08-12 18:12 ` [PATCH 01/13] fc_els: use 'union fc_tlv_desc' Jesse Taube
                   ` (14 more replies)
  0 siblings, 15 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 as a last resort.
This patch adds a new controller flag 'NVME_CTRL_MARGINAL' which will be
checked during multipath path selection, causing the path to be skipped
when checking for paths. If no other paths are available the 'marginal'
paths are considered for path selection.

The testing for this patch set was performed by Bryan Gurney, using the
process outlined by John Meneghini's presentation at LSFMM 2024, where
the fibre channel switch sends an FPIN notification on a specific switch
port, and the following is checked on the initiator:

1. The controllers corresponding to the paths on the port that has
received the notification are showing a set NVME_CTRL_MARGINAL flag.

   \
    +- nvme4 fc traddr=c,host_traddr=e live optimized
    +- nvme5 fc traddr=8,host_traddr=e live non-optimized
    +- nvme8 fc traddr=e,host_traddr=f marginal optimized
    +- nvme9 fc traddr=a,host_traddr=f marginal non-optimized

2. The I/O statistics of the test namespace show no I/O activity on the
controllers with NVME_CTRL_MARGINAL set.

   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     25001.00         0.00        97.66         0.00
   nvme4c9n1     25000.00         0.00        97.66         0.00
   nvme4n1       50011.00         0.00       195.36         0.00


   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     48360.00         0.00       188.91         0.00
   nvme4c9n1      1642.00         0.00         6.41         0.00
   nvme4n1       49981.00         0.00       195.24         0.00


   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     50001.00         0.00       195.32         0.00
   nvme4c9n1         0.00         0.00         0.00         0.00
   nvme4n1       50016.00         0.00       195.38         0.00

Link: https://people.redhat.com/jmeneghi/LSFMM_2024/LSFMM_2024_NVMe_Cancel_and_FPIN.pdf

Testing has been performed by sending all FPIN LI ELS messages from the
switch to the Host and verifying the proper nvme multi-pathing behavior
is effected with each of the eight different FPIN link integrity events.
Results were verified with iostat and with the nvme list-subsys command.

These tests were run with all scenarios including where there were only
non-optimized paths available, and where all paths were
marginal/degraded. All multi-path io-policies were tested including:
numa, round-robin and queue-depth. When all paths on the host are
marginal/degraded, I/O continues on the optimized path that was most
recently non-marginal.  If both of the optimized paths are down, I/O
properly continues on one of the marginal/degraded non-optimized paths.

Testing has been complete with both Broadcom (lpfc) and Marvell (qla2xx)
32GB HBAs.  Both HBAs successfully complete all tests.

For a complete description of the tests that were run, please see
bugzilla 220329.

To test the path handeling use:
https://github.com/linux-blktests/blktests/pull/264
or
https://lore.kernel.org/linux-nvme/20260812174503.3705830-1-jtaubepe@redhat.com/

To test the full set including FPIN decoding use:
https://github.com/johnmeneghini/fpin_li_tests

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220329

New refactored implementation enables administrators to manually control
port marginal states via sysfs. For example:

# Set remote port to marginal state
echo "Marginal" > /sys/class/fc_remote_ports/rport-4:0-1/port_state

# Clear marginal state (set to online)
echo "Online" > /sys/class/fc_remote_ports/rport-4:0-1/port_state

Changes to the original submission:
- Changed flag name to 'marginal'
- Do not block marginal path; influence path selection instead
  to de-prioritize marginal paths

Changes to v2:
- Split off driver-specific modifications
- Introduce 'union fc_tlv_desc' to avoid casts

Changes to v3:
- Include reviews from Justin Tee
- Split marginal path handling patch

Changes to v4:
- Change 'u8' to '__u8' on fc_tlv_desc to fix a failure to build
- Print 'marginal' instead of 'live' in the state of controllers
  when they are marginal

Changes to v5:
- Minor spelling corrections to patch descriptions

Changes to v6:
- No code changes; added note about additional testing

Changes to v7:
- Split nvme core marginal flag addition into its own patch
- Add patch for queue_depth marginal path support

Changes to v8:
- Rebased patch series to nvme-6.17.
- Added patch from Gustavo Silva, "scsi: qla2xxx: Fix memcpy field-spanning
  write issue", which resolves the field-spanning write issue
- We decided to leave the "marginal" state as is, because the transport
  driver uses the term "marginal".

Changes to v9:
- Rebased patch series to nvme-6.18.
- Refactor and fix a patch from Gustavo Silva, "scsi: qla2xxx: Fix 2 memcpy
  field-spanning write issue", which resolves the field-spanning write
  issue.
  This new version of Gustavo's patch fixes a bug found in testing.
- Refactored original implementation
  New functions added:
    nvme_fc_lport_from_wwpn() - Find local port by WWPN
    nvme_fc_fpin_set_state() - Set marginal state on controllers
    nvme_fc_modify_rport_fpin_state() - Main API function
  Functions removed:
    nvme_fc_fpin_li_lport_update() - FPIN processing logic
    nvme_fc_fpin_rcv() - Direct FPIN message processing
  Functions modified:
    fc_rport_set_marginal_state - allows administrative control

Changes to v10:
 - Rebase onto 7.2-rc1
 - Add marginal support to fcloop driver (new patch)
 - Rewrite multipath handeling ("nvme-fc: marginal path handling") and
     ("nvme-multipath: queue-depth support for marginal paths") replacing
     them with ("nvme-multipath: round-robin...")
     ("nvme-fc: marginal path handling...") and
     ("nvme-multipath: queue-depth support")
 - New patch ("scsi: scsi_transport_fc: Add set_rport_marginal to ")
     replaces ("scsi: scsi_transport_fc: add fc_host_fpin_set_nvme").
     Uses existing function template to add a new callback for setting
     the marginal state on a remote port, similar to the existing
     set_rport_dev_loss_tmo() callback.
 - Replace `nvme_fc_modify_rport_fpin_state` with
     `nvme_fc_set_remoteport_fpin` which takes
     `struct nvme_fc_remote_port` insead of wwpn and wwnn
 - Rewrite the "enable FPIN notification for NVMe" patches
      to use the new callback methods.

This patch series is based upon 7.3/scsi-staging at
Commit 376a3960e5ef ("scsi: fnic: Fix built-in NVMe/FC build")

https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git/commit/?h=7.3/scsi-staging&id=376a3960e5efe85ff765abfb5b5b7e4655ad6aed

Bryan Gurney (2):
  nvme: add NVME_CTRL_MARGINAL flag
  nvme: sysfs: emit the marginal path state in show_state()

Hannes Reinecke (1):
  fc_els: use 'union fc_tlv_desc'

Jesse Taube (8):
  nvme-multipath: numa support for marginal paths
  nvme-multipath: round-robin support for marginal paths
  scsi: scsi_transport_fc: Add set_rport_marginal to
    fc_function_template
  nvme-fc: add nvme_fc_set_remoteport_fpin()
  scsi: qla2xxx: enable FPIN notification for NVMe
  scsi: lpfc: enable FPIN notification for NVMe
  nvme: fcloop: Add set_rport_marginal to sysfs
  docs: nvme-multipath: Add FC-NVMe marginal state

John Meneghini (2):
  nvme-multipath: queue-depth support for marginal paths
  scsi: scsi_transport_fc: user support for clearing NVME_CTRL_MARGINAL

 Documentation/admin-guide/nvme-multipath.rst |  17 ++
 drivers/nvme/host/core.c                     |   1 +
 drivers/nvme/host/fc.c                       |  20 +++
 drivers/nvme/host/multipath.c                | 122 +++++++++++--
 drivers/nvme/host/nvme.h                     |   6 +
 drivers/nvme/host/sysfs.c                    |   4 +-
 drivers/nvme/target/fcloop.c                 |  41 +++++
 drivers/scsi/lpfc/lpfc_attr.c                |  28 +++
 drivers/scsi/lpfc/lpfc_els.c                 |  77 ++++----
 drivers/scsi/lpfc/lpfc_hw4.h                 |  12 +-
 drivers/scsi/qla2xxx/qla_attr.c              |  13 ++
 drivers/scsi/scsi_transport_fc.c             |  89 +++++++---
 include/linux/nvme-fc-driver.h               |   2 +
 include/scsi/scsi_transport_fc.h             |   1 +
 include/uapi/scsi/fc/fc_els.h                | 174 ++++++++++---------
 15 files changed, 441 insertions(+), 166 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 01/13] fc_els: use 'union fc_tlv_desc'
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 02/13] nvme: add NVME_CTRL_MARGINAL flag Jesse Taube
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block, Hannes Reinecke

From: Hannes Reinecke <hare@kernel.org>

Introduce 'union fc_tlv_desc' to have a common structure for all FC
ELS TLV structures and avoid type casts.

Commit 44b6169ada7f ("scsi: fc: Avoid -Wflex-array-member-not-at-end
warnings")
conflicts with this change. To solve the problem I moved the
fc_els_rdf_hdr and fc_els_rdf_resp_hdr definitions out of fc_els.h and
into lpfc_hw4.h.

Fixes: 44b6169ada7f ("scsi: fc: Avoid -Wflex-array-member-not-at-end warnings")
Cc: Gustavo A. R. Silva <gustavoars@kernel.org>
Co-developed-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: Hannes Reinecke <hare@kernel.org>
Tested-by: Bryan Gurney <bgurney@redhat.com>
---
V10 -> V11:
 - No change/Rebase
---
 drivers/scsi/lpfc/lpfc_els.c     |  77 +++++++-------
 drivers/scsi/lpfc/lpfc_hw4.h     |  12 ++-
 drivers/scsi/scsi_transport_fc.c |  27 ++---
 include/uapi/scsi/fc/fc_els.h    | 174 ++++++++++++++++---------------
 4 files changed, 146 insertions(+), 144 deletions(-)

diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 52fc5058976d..11143da24b54 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -3888,7 +3888,7 @@ lpfc_issue_els_rdf(struct lpfc_vport *vport, uint8_t retry)
 	memset(prdf, 0, cmdsize);
 	prdf->rdf.fpin_cmd = ELS_RDF;
 	prdf->rdf.desc_len = cpu_to_be32(sizeof(struct lpfc_els_rdf_req) -
-					 sizeof(struct fc_els_rdf_hdr));
+					 offsetof(struct fc_els_rdf, desc));
 	prdf->reg_d1.reg_desc.desc_tag = cpu_to_be32(ELS_DTAG_FPIN_REGISTER);
 	prdf->reg_d1.reg_desc.desc_len = cpu_to_be32(
 				FC_TLV_DESC_LENGTH_FROM_SZ(prdf->reg_d1));
@@ -4075,7 +4075,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 {
 	IOCB_t *irsp_iocb;
 	struct fc_els_edc_resp *edc_rsp;
-	struct fc_tlv_desc *tlv;
+	union fc_tlv_desc *tlv;
 	struct fc_diag_cg_sig_desc *pcgd;
 	struct fc_diag_lnkflt_desc *plnkflt;
 	struct lpfc_dmabuf *pcmd, *prsp;
@@ -4166,7 +4166,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 			goto out;
 		}
 
-		dtag = be32_to_cpu(tlv->desc_tag);
+		dtag = be32_to_cpu(tlv->hdr.desc_tag);
 		switch (dtag) {
 		case ELS_DTAG_LNK_FAULT_CAP:
 			if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
@@ -4181,7 +4181,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 					sizeof(struct fc_diag_lnkflt_desc));
 				goto out;
 			}
-			plnkflt = (struct fc_diag_lnkflt_desc *)tlv;
+			plnkflt = &tlv->lnkflt;
 			lpfc_printf_log(phba, KERN_INFO,
 				LOG_ELS | LOG_LDS_EVENT,
 				"4617 Link Fault Desc Data: 0x%08x 0x%08x "
@@ -4208,7 +4208,7 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 				goto out;
 			}
 
-			pcgd = (struct fc_diag_cg_sig_desc *)tlv;
+			pcgd = &tlv->cg_sig;
 			lpfc_printf_log(
 				phba, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
 				"4616 CGN Desc Data: 0x%08x 0x%08x "
@@ -4263,10 +4263,8 @@ lpfc_cmpl_els_edc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 }
 
 static void
-lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_diag_lnkflt_desc *lft)
 {
-	struct fc_diag_lnkflt_desc *lft = (struct fc_diag_lnkflt_desc *)tlv;
-
 	lft->desc_tag = cpu_to_be32(ELS_DTAG_LNK_FAULT_CAP);
 	lft->desc_len = cpu_to_be32(
 		FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_diag_lnkflt_desc));
@@ -4279,10 +4277,8 @@ lpfc_format_edc_lft_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
 }
 
 static void
-lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_format_edc_cgn_desc(struct lpfc_hba *phba, struct fc_diag_cg_sig_desc *cgd)
 {
-	struct fc_diag_cg_sig_desc *cgd = (struct fc_diag_cg_sig_desc *)tlv;
-
 	/* We are assuming cgd was zero'ed before calling this routine */
 
 	/* Configure the congestion detection capability */
@@ -4381,7 +4377,7 @@ lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry)
 	struct lpfc_hba  *phba = vport->phba;
 	struct lpfc_iocbq *elsiocb;
 	struct fc_els_edc *edc_req;
-	struct fc_tlv_desc *tlv;
+	union fc_tlv_desc *tlv;
 	u16 cmdsize;
 	struct lpfc_nodelist *ndlp;
 	u8 *pcmd = NULL;
@@ -4420,13 +4416,13 @@ lpfc_issue_els_edc(struct lpfc_vport *vport, uint8_t retry)
 	tlv = edc_req->desc;
 
 	if (cgn_desc_size) {
-		lpfc_format_edc_cgn_desc(phba, tlv);
+		lpfc_format_edc_cgn_desc(phba, &tlv->cg_sig);
 		phba->cgn_sig_freq = lpfc_fabric_cgn_frequency;
 		tlv = fc_tlv_next_desc(tlv);
 	}
 
 	if (lft_desc_size)
-		lpfc_format_edc_lft_desc(phba, tlv);
+		lpfc_format_edc_lft_desc(phba, &tlv->lnkflt);
 
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_CGN_MGMT,
 			 "4623 Xmit EDC to remote "
@@ -5999,7 +5995,7 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 {
 	struct lpfc_hba  *phba = vport->phba;
 	struct fc_els_edc_resp *edc_rsp;
-	struct fc_tlv_desc *tlv;
+	union fc_tlv_desc *tlv;
 	struct lpfc_iocbq *elsiocb;
 	IOCB_t *icmd, *cmd;
 	union lpfc_wqe128 *wqe;
@@ -6043,10 +6039,10 @@ lpfc_issue_els_edc_rsp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 		FC_TLV_DESC_LENGTH_FROM_SZ(struct fc_els_lsri_desc));
 	edc_rsp->lsri.rqst_w0.cmd = ELS_EDC;
 	tlv = edc_rsp->desc;
-	lpfc_format_edc_cgn_desc(phba, tlv);
+	lpfc_format_edc_cgn_desc(phba, &tlv->cg_sig);
 	tlv = fc_tlv_next_desc(tlv);
 	if (lft_desc_size)
-		lpfc_format_edc_lft_desc(phba, tlv);
+		lpfc_format_edc_lft_desc(phba, &tlv->lnkflt);
 
 	lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_RSP,
 			      "Issue EDC ACC:      did:x%x flg:x%lx refcnt %d",
@@ -9439,7 +9435,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 {
 	struct lpfc_hba  *phba = vport->phba;
 	struct fc_els_edc *edc_req;
-	struct fc_tlv_desc *tlv;
+	union fc_tlv_desc *tlv;
 	uint8_t *payload;
 	uint32_t *ptr, dtag;
 	const char *dtag_nm;
@@ -9482,7 +9478,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 			goto out;
 		}
 
-		dtag = be32_to_cpu(tlv->desc_tag);
+		dtag = be32_to_cpu(tlv->hdr.desc_tag);
 		switch (dtag) {
 		case ELS_DTAG_LNK_FAULT_CAP:
 			if (bytes_remain < FC_TLV_DESC_SZ_FROM_LENGTH(tlv) ||
@@ -9497,7 +9493,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 					sizeof(struct fc_diag_lnkflt_desc));
 				goto out;
 			}
-			plnkflt = (struct fc_diag_lnkflt_desc *)tlv;
+			plnkflt = &tlv->lnkflt;
 			lpfc_printf_log(phba, KERN_INFO,
 				LOG_ELS | LOG_LDS_EVENT,
 				"4626 Link Fault Desc Data: x%08x len x%x "
@@ -9534,7 +9530,7 @@ lpfc_els_rcv_edc(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 			phba->cgn_sig_freq = lpfc_fabric_cgn_frequency;
 
 			lpfc_least_capable_settings(
-				phba, (struct fc_diag_cg_sig_desc *)tlv);
+				phba, &tlv->cg_sig);
 			break;
 		default:
 			dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
@@ -10124,14 +10120,13 @@ lpfc_display_fpin_wwpn(struct lpfc_hba *phba, __be64 *wwnlist, u32 cnt)
 /**
  * lpfc_els_rcv_fpin_li - Process an FPIN Link Integrity Event.
  * @phba: Pointer to phba object.
- * @tlv:  Pointer to the Link Integrity Notification Descriptor.
+ * @li:  Pointer to the Link Integrity Notification Descriptor.
  *
  * This function processes a Link Integrity FPIN event by logging a message.
  **/
 static void
-lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_fn_li_desc *li)
 {
-	struct fc_fn_li_desc *li = (struct fc_fn_li_desc *)tlv;
 	const char *li_evt_str;
 	u32 li_evt, cnt;
 
@@ -10155,14 +10150,13 @@ lpfc_els_rcv_fpin_li(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
 /**
  * lpfc_els_rcv_fpin_del - Process an FPIN Delivery Event.
  * @phba: Pointer to hba object.
- * @tlv:  Pointer to the Delivery Notification Descriptor TLV
+ * @del:  Pointer to the Delivery Notification Descriptor TLV
  *
  * This function processes a Delivery FPIN event by logging a message.
  **/
 static void
-lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_fn_deli_desc *del)
 {
-	struct fc_fn_deli_desc *del = (struct fc_fn_deli_desc *)tlv;
 	const char *del_rsn_str;
 	u32 del_rsn;
 	__be32 *frame;
@@ -10193,14 +10187,14 @@ lpfc_els_rcv_fpin_del(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
 /**
  * lpfc_els_rcv_fpin_peer_cgn - Process a FPIN Peer Congestion Event.
  * @phba: Pointer to hba object.
- * @tlv:  Pointer to the Peer Congestion Notification Descriptor TLV
+ * @pc:  Pointer to the Peer Congestion Notification Descriptor TLV
  *
  * This function processes a Peer Congestion FPIN event by logging a message.
  **/
 static void
-lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba,
+			   struct fc_fn_peer_congn_desc *pc)
 {
-	struct fc_fn_peer_congn_desc *pc = (struct fc_fn_peer_congn_desc *)tlv;
 	const char *pc_evt_str;
 	u32 pc_evt, cnt;
 
@@ -10228,7 +10222,7 @@ lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
 /**
  * lpfc_els_rcv_fpin_cgn - Process an FPIN Congestion notification
  * @phba: Pointer to hba object.
- * @tlv:  Pointer to the Congestion Notification Descriptor TLV
+ * @cgn:  Pointer to the Congestion Notification Descriptor TLV
  *
  * This function processes an FPIN Congestion Notifiction.  The notification
  * could be an Alarm or Warning.  This routine feeds that data into driver's
@@ -10237,10 +10231,9 @@ lpfc_els_rcv_fpin_peer_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
  * to the upper layer or 0 to indicate don't deliver it.
  **/
 static int
-lpfc_els_rcv_fpin_cgn(struct lpfc_hba *phba, struct fc_tlv_desc *tlv)
+lpfc_els_rcv_fpin_cgn(struct lpfc_hba *phba, struct fc_fn_congn_desc *cgn)
 {
 	struct lpfc_cgn_info *cp;
-	struct fc_fn_congn_desc *cgn = (struct fc_fn_congn_desc *)tlv;
 	const char *cgn_evt_str;
 	u32 cgn_evt;
 	const char *cgn_sev_str;
@@ -10341,7 +10334,7 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length)
 {
 	struct lpfc_hba *phba = vport->phba;
 	struct fc_els_fpin *fpin = (struct fc_els_fpin *)p;
-	struct fc_tlv_desc *tlv, *first_tlv, *current_tlv;
+	union fc_tlv_desc *tlv, *first_tlv, *current_tlv;
 	const char *dtag_nm;
 	int desc_cnt = 0, bytes_remain, cnt;
 	u32 dtag, deliver = 0;
@@ -10366,7 +10359,7 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length)
 		return;
 	}
 
-	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+	tlv = &fpin->fpin_desc[0];
 	first_tlv = tlv;
 	bytes_remain = fpin_length - offsetof(struct fc_els_fpin, fpin_desc);
 	bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));
@@ -10374,22 +10367,22 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length)
 	/* process each descriptor separately */
 	while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
 	       bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
-		dtag = be32_to_cpu(tlv->desc_tag);
+		dtag = be32_to_cpu(tlv->hdr.desc_tag);
 		switch (dtag) {
 		case ELS_DTAG_LNK_INTEGRITY:
-			lpfc_els_rcv_fpin_li(phba, tlv);
+			lpfc_els_rcv_fpin_li(phba, &tlv->li);
 			deliver = 1;
 			break;
 		case ELS_DTAG_DELIVERY:
-			lpfc_els_rcv_fpin_del(phba, tlv);
+			lpfc_els_rcv_fpin_del(phba, &tlv->deli);
 			deliver = 1;
 			break;
 		case ELS_DTAG_PEER_CONGEST:
-			lpfc_els_rcv_fpin_peer_cgn(phba, tlv);
+			lpfc_els_rcv_fpin_peer_cgn(phba, &tlv->peer_congn);
 			deliver = 1;
 			break;
 		case ELS_DTAG_CONGESTION:
-			deliver = lpfc_els_rcv_fpin_cgn(phba, tlv);
+			deliver = lpfc_els_rcv_fpin_cgn(phba, &tlv->congn);
 			break;
 		default:
 			dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
@@ -10402,12 +10395,12 @@ lpfc_els_rcv_fpin(struct lpfc_vport *vport, void *p, u32 fpin_length)
 			return;
 		}
 		lpfc_cgn_update_stat(phba, dtag);
-		cnt = be32_to_cpu(tlv->desc_len);
+		cnt = be32_to_cpu(tlv->hdr.desc_len);
 
 		/* Sanity check descriptor length. The desc_len value does not
 		 * include space for the desc_tag and the desc_len fields.
 		 */
-		len -= (cnt + sizeof(struct fc_tlv_desc));
+		len -= (cnt + sizeof(struct fc_tlv_desc_hdr));
 		if (len < 0) {
 			dtag_nm = lpfc_get_tlv_dtag_nm(dtag);
 			lpfc_printf_log(phba, KERN_WARNING, LOG_CGN_MGMT,
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 41fa8f3329da..10490b3a05cd 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -4926,12 +4926,20 @@ struct lpfc_els_rdf_reg_desc {
 };
 
 struct lpfc_els_rdf_req {
-	struct fc_els_rdf_hdr		rdf;	   /* hdr up to descriptors */
+	struct {
+		__u8		fpin_cmd;	/* command (0x19) */
+		__u8		fpin_zero[3];	/* specified as zero - part of cmd */
+		__be32		desc_len;	/* Length of Descriptor List (in bytes) */
+	} rdf;	   /* hdr up to descriptors */
 	struct lpfc_els_rdf_reg_desc	reg_d1;	/* 1st descriptor */
 };
 
 struct lpfc_els_rdf_rsp {
-	struct fc_els_rdf_resp_hdr	rdf_resp;  /* hdr up to descriptors */
+	struct {
+		struct fc_els_ls_acc	acc_hdr;
+		__be32			desc_list_len;	/* Length of response (in bytes) */
+		struct fc_els_lsri_desc	lsri;
+	} rdf_resp;  /* hdr up to descriptors */
 	struct lpfc_els_rdf_reg_desc	reg_d1;	/* 1st descriptor */
 };
 
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 173ed6373f04..02c58f4591d8 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -776,12 +776,11 @@ fc_fpin_pname_stats_update(struct Scsi_Host *shost,
  *
  */
 static void
-fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
+fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_fn_li_desc *li_desc)
 {
 	struct fc_rport *rport = NULL;
 	struct fc_rport *attach_rport = NULL;
 	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
-	struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
 	u16 event_type = be16_to_cpu(li_desc->event_type);
 
 	rport = fc_find_rport_by_wwpn(shost,
@@ -812,12 +811,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
  */
 static void
 fc_fpin_delivery_stats_update(struct Scsi_Host *shost,
-			      struct fc_tlv_desc *tlv)
+			      struct fc_fn_deli_desc *dn_desc)
 {
 	struct fc_rport *rport = NULL;
 	struct fc_rport *attach_rport = NULL;
 	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
-	struct fc_fn_deli_desc *dn_desc = (struct fc_fn_deli_desc *)tlv;
 	u32 reason_code = be32_to_cpu(dn_desc->deli_reason_code);
 
 	rport = fc_find_rport_by_wwpn(shost,
@@ -843,12 +841,10 @@ fc_fpin_delivery_stats_update(struct Scsi_Host *shost,
  */
 static void
 fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
-				struct fc_tlv_desc *tlv)
+				struct fc_fn_peer_congn_desc *pc_desc)
 {
 	struct fc_rport *rport = NULL;
 	struct fc_rport *attach_rport = NULL;
-	struct fc_fn_peer_congn_desc *pc_desc =
-	    (struct fc_fn_peer_congn_desc *)tlv;
 	u16 event_type = be16_to_cpu(pc_desc->event_type);
 
 	rport = fc_find_rport_by_wwpn(shost,
@@ -876,10 +872,9 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
  */
 static void
 fc_fpin_congn_stats_update(struct Scsi_Host *shost,
-			   struct fc_tlv_desc *tlv)
+			   struct fc_fn_congn_desc *congn)
 {
 	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
-	struct fc_fn_congn_desc *congn = (struct fc_fn_congn_desc *)tlv;
 
 	fc_cn_stats_update(be16_to_cpu(congn->event_type),
 			   &fc_host->fpin_stats);
@@ -899,32 +894,32 @@ fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf,
 		u8 event_acknowledge)
 {
 	struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf;
-	struct fc_tlv_desc *tlv;
+	union fc_tlv_desc *tlv;
 	u32 bytes_remain;
 	u32 dtag;
 	enum fc_host_event_code event_code =
 		event_acknowledge ? FCH_EVT_LINK_FPIN_ACK : FCH_EVT_LINK_FPIN;
 
 	/* Update Statistics */
-	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+	tlv = &fpin->fpin_desc[0];
 	bytes_remain = fpin_len - offsetof(struct fc_els_fpin, fpin_desc);
 	bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));
 
 	while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
 	       bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
-		dtag = be32_to_cpu(tlv->desc_tag);
+		dtag = be32_to_cpu(tlv->hdr.desc_tag);
 		switch (dtag) {
 		case ELS_DTAG_LNK_INTEGRITY:
-			fc_fpin_li_stats_update(shost, tlv);
+			fc_fpin_li_stats_update(shost, &tlv->li);
 			break;
 		case ELS_DTAG_DELIVERY:
-			fc_fpin_delivery_stats_update(shost, tlv);
+			fc_fpin_delivery_stats_update(shost, &tlv->deli);
 			break;
 		case ELS_DTAG_PEER_CONGEST:
-			fc_fpin_peer_congn_stats_update(shost, tlv);
+			fc_fpin_peer_congn_stats_update(shost, &tlv->peer_congn);
 			break;
 		case ELS_DTAG_CONGESTION:
-			fc_fpin_congn_stats_update(shost, tlv);
+			fc_fpin_congn_stats_update(shost, &tlv->congn);
 		}
 
 		bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
diff --git a/include/uapi/scsi/fc/fc_els.h b/include/uapi/scsi/fc/fc_els.h
index dca6a28f4e86..b5653e1dee13 100644
--- a/include/uapi/scsi/fc/fc_els.h
+++ b/include/uapi/scsi/fc/fc_els.h
@@ -259,12 +259,12 @@ enum fc_ls_tlv_dtag {
 
 
 /*
- * Generic Link Service TLV Descriptor format
+ * Generic Link Service TLV Descriptor header
  *
  * This structure, as it defines no payload, will also be referred to
  * as the "tlv header" - which contains the tag and len fields.
  */
-struct fc_tlv_desc {
+struct fc_tlv_desc_hdr {
 	__be32		desc_tag;	/* Notification Descriptor Tag */
 	__be32		desc_len;	/* Length of Descriptor (in bytes).
 					 * Size of descriptor excluding
@@ -273,36 +273,6 @@ struct fc_tlv_desc {
 	__u8		desc_value[];  /* Descriptor Value */
 };
 
-/* Descriptor tag and len fields are considered the mandatory header
- * for a descriptor
- */
-#define FC_TLV_DESC_HDR_SZ	sizeof(struct fc_tlv_desc)
-
-/*
- * Macro, used when initializing payloads, to return the descriptor length.
- * Length is size of descriptor minus the tag and len fields.
- */
-#define FC_TLV_DESC_LENGTH_FROM_SZ(desc)	\
-		(sizeof(desc) - FC_TLV_DESC_HDR_SZ)
-
-/* Macro, used on received payloads, to return the descriptor length */
-#define FC_TLV_DESC_SZ_FROM_LENGTH(tlv)		\
-		(__be32_to_cpu((tlv)->desc_len) + FC_TLV_DESC_HDR_SZ)
-
-/*
- * This helper is used to walk descriptors in a descriptor list.
- * Given the address of the current descriptor, which minimally contains a
- * tag and len field, calculate the address of the next descriptor based
- * on the len field.
- */
-static inline void *fc_tlv_next_desc(void *desc)
-{
-	struct fc_tlv_desc *tlv = desc;
-
-	return (desc + FC_TLV_DESC_SZ_FROM_LENGTH(tlv));
-}
-
-
 /*
  * Link Service Request Information Descriptor
  */
@@ -1100,19 +1070,6 @@ struct fc_fn_congn_desc {
 	__u8		resv[3];	/* reserved - must be zero */
 };
 
-/*
- * ELS_FPIN - Fabric Performance Impact Notification
- */
-struct fc_els_fpin {
-	__u8		fpin_cmd;	/* command (0x16) */
-	__u8		fpin_zero[3];	/* specified as zero - part of cmd */
-	__be32		desc_len;	/* Length of Descriptor List (in bytes).
-					 * Size of ELS excluding fpin_cmd,
-					 * fpin_zero and desc_len fields.
-					 */
-	struct fc_tlv_desc	fpin_desc[];	/* Descriptor list */
-};
-
 /* Diagnostic Function Descriptor - FPIN Registration */
 struct fc_df_desc_fpin_reg {
 	/* New members MUST be added within the __struct_group() macro below. */
@@ -1136,42 +1093,6 @@ struct fc_df_desc_fpin_reg {
 _Static_assert(offsetof(struct fc_df_desc_fpin_reg, desc_tags) == sizeof(struct fc_df_desc_fpin_reg_hdr),
 	      "struct member likely outside of __struct_group()");
 
-/*
- * ELS_RDF - Register Diagnostic Functions
- */
-struct fc_els_rdf {
-	/* New members MUST be added within the __struct_group() macro below. */
-	__struct_group(fc_els_rdf_hdr, __hdr, /* no attrs */,
-		__u8		fpin_cmd;	/* command (0x19) */
-		__u8		fpin_zero[3];	/* specified as zero - part of cmd */
-		__be32		desc_len;	/* Length of Descriptor List (in bytes).
-						 * Size of ELS excluding fpin_cmd,
-						 * fpin_zero and desc_len fields.
-						 */
-	);
-	struct fc_tlv_desc	desc[];	/* Descriptor list */
-};
-_Static_assert(offsetof(struct fc_els_rdf, desc) == sizeof(struct fc_els_rdf_hdr),
-	       "struct member likely outside of __struct_group()");
-
-/*
- * ELS RDF LS_ACC Response.
- */
-struct fc_els_rdf_resp {
-	/* New members MUST be added within the __struct_group() macro below. */
-	__struct_group(fc_els_rdf_resp_hdr, __hdr, /* no attrs */,
-		struct fc_els_ls_acc	acc_hdr;
-		__be32			desc_list_len;	/* Length of response (in
-							 * bytes). Excludes acc_hdr
-							 * and desc_list_len fields.
-							 */
-		struct fc_els_lsri_desc	lsri;
-	);
-	struct fc_tlv_desc	desc[];	/* Supported Descriptor list */
-};
-_Static_assert(offsetof(struct fc_els_rdf_resp, desc) == sizeof(struct fc_els_rdf_resp_hdr),
-	       "struct member likely outside of __struct_group()");
-
 /*
  * Diagnostic Capability Descriptors for EDC ELS
  */
@@ -1241,6 +1162,65 @@ struct fc_diag_cg_sig_desc {
 	struct fc_diag_cg_sig_freq	rcv_signal_frequency;
 };
 
+/*
+ * Generic Link Service TLV Descriptor format
+ *
+ * This structure, as it defines no payload, will also be referred to
+ * as the "tlv header" - which contains the tag and len fields.
+ */
+union fc_tlv_desc {
+	struct fc_tlv_desc_hdr hdr;
+	struct fc_els_lsri_desc lsri;
+	struct fc_fn_li_desc li;
+	struct fc_fn_deli_desc deli;
+	struct fc_fn_peer_congn_desc peer_congn;
+	struct fc_fn_congn_desc congn;
+	struct fc_df_desc_fpin_reg fpin_reg;
+	struct fc_diag_lnkflt_desc lnkflt;
+	struct fc_diag_cg_sig_desc cg_sig;
+};
+
+/* Descriptor tag and len fields are considered the mandatory header
+ * for a descriptor
+ */
+#define FC_TLV_DESC_HDR_SZ	sizeof(struct fc_tlv_desc_hdr)
+
+/*
+ * Macro, used when initializing payloads, to return the descriptor length.
+ * Length is size of descriptor minus the tag and len fields.
+ */
+#define FC_TLV_DESC_LENGTH_FROM_SZ(desc)	\
+		(sizeof(desc) - FC_TLV_DESC_HDR_SZ)
+
+/* Macro, used on received payloads, to return the descriptor length */
+#define FC_TLV_DESC_SZ_FROM_LENGTH(tlv)		\
+		(__be32_to_cpu((tlv)->hdr.desc_len) + FC_TLV_DESC_HDR_SZ)
+
+/*
+ * This helper is used to walk descriptors in a descriptor list.
+ * Given the address of the current descriptor, which minimally contains a
+ * tag and len field, calculate the address of the next descriptor based
+ * on the len field.
+ */
+static inline union fc_tlv_desc *fc_tlv_next_desc(union fc_tlv_desc *desc)
+{
+	return (union fc_tlv_desc *)((__u8 *)desc + FC_TLV_DESC_SZ_FROM_LENGTH(desc));
+}
+
+
+/*
+ * ELS_FPIN - Fabric Performance Impact Notification
+ */
+struct fc_els_fpin {
+	__u8		fpin_cmd;	/* command (0x16) */
+	__u8		fpin_zero[3];	/* specified as zero - part of cmd */
+	__be32		desc_len;	/* Length of Descriptor List (in bytes).
+					 * Size of ELS excluding fpin_cmd,
+					 * fpin_zero and desc_len fields.
+					 */
+	union fc_tlv_desc	fpin_desc[];	/* Descriptor list */
+};
+
 /*
  * ELS_EDC - Exchange Diagnostic Capabilities
  */
@@ -1251,10 +1231,37 @@ struct fc_els_edc {
 					 * Size of ELS excluding edc_cmd,
 					 * edc_zero and desc_len fields.
 					 */
-	struct fc_tlv_desc	desc[];
+	union fc_tlv_desc	desc[];
 					/* Diagnostic Descriptor list */
 };
 
+/*
+ * ELS_RDF - Register Diagnostic Functions
+ */
+struct fc_els_rdf {
+	__u8		fpin_cmd;	/* command (0x19) */
+	__u8		fpin_zero[3];	/* specified as zero - part of cmd */
+	__be32		desc_len;	/* Length of Descriptor List (in bytes).
+					 * Size of ELS excluding fpin_cmd,
+					 * fpin_zero and desc_len fields.
+					 */
+	union fc_tlv_desc	desc[];	/* Descriptor list */
+};
+
+/*
+ * ELS RDF LS_ACC Response.
+ */
+struct fc_els_rdf_resp {
+	struct fc_els_ls_acc	acc_hdr;
+	__be32			desc_list_len;	/* Length of response (in
+						 * bytes). Excludes acc_hdr
+						 * and desc_list_len fields.
+						 */
+	struct fc_els_lsri_desc	lsri;
+	union fc_tlv_desc	desc[];	/* Supported Descriptor list */
+};
+
+
 /*
  * ELS EDC LS_ACC Response.
  */
@@ -1265,9 +1272,8 @@ struct fc_els_edc_resp {
 						 * and desc_list_len fields.
 						 */
 	struct fc_els_lsri_desc	lsri;
-	struct fc_tlv_desc	desc[];
+	union fc_tlv_desc	desc[];
 				    /* Supported Diagnostic Descriptor list */
 };
 
-
 #endif /* _FC_ELS_H_ */
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 02/13] nvme: add NVME_CTRL_MARGINAL flag
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
  2026-08-12 18:12 ` [PATCH 01/13] fc_els: use 'union fc_tlv_desc' Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 03/13] nvme-multipath: numa support for marginal paths Jesse Taube
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 flag is cleared on controller reset, which is often the case when
faulty cabling or transceiver hardware is replaced.

Signed-off-by: Bryan Gurney <bgurney@redhat.com>
---
V10 -> V11:
 - No change
---
 drivers/nvme/host/core.c | 1 +
 drivers/nvme/host/fc.c   | 4 ++++
 drivers/nvme/host/nvme.h | 6 ++++++
 3 files changed, 11 insertions(+)

diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 453c1f0b2dd0..957090af2171 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);
+	clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags);
 	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..d2ca65f70a0c 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 */
+	clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags);
+
 	nvme_reset_ctrl(&ctrl->ctrl);
 }
 
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 824651cc898d..3616742d6788 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,11 @@ 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);
+}
+
 enum nvme_iopolicy {
 	NVME_IOPOLICY_NUMA,
 	NVME_IOPOLICY_RR,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 03/13] nvme-multipath: numa support for marginal paths
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
  2026-08-12 18:12 ` [PATCH 01/13] fc_els: use 'union fc_tlv_desc' Jesse Taube
  2026-08-12 18:12 ` [PATCH 02/13] nvme: add NVME_CTRL_MARGINAL flag Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 04/13] nvme-multipath: queue-depth " Jesse Taube
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 -> V11:
 - New commit
---
 drivers/nvme/host/multipath.c | 53 +++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 9b9a657fa330..02936c580a37 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -305,10 +305,44 @@ static bool nvme_path_is_disabled(struct nvme_ns *ns)
 	return false;
 }
 
+/*
+ * Returns true if the new distance is better than the old one.
+ */
+static bool is_best_distance(bool found_is_marginal, bool marginal,
+			       int old_distance, int distance)
+{
+	if (found_is_marginal) {
+		if (marginal) {
+			/*
+			 * A marginal path has already been found,
+			 * or this is the first path found.
+			 * This one is also marginal, but closer
+			 * to the NUMA node, so prefer it.
+			 */
+			if (distance < old_distance)
+				return true;
+		} else {
+			/* Found a non-marginal path, use it over a marginal one. */
+			return true;
+		}
+	} else {
+		/* A non-marginal path has already found. This one is marginal, so skip it. */
+		if (marginal)
+			return false;
+
+		/* Found a closer non-marginal path, use it. */
+		if (distance < old_distance)
+			return true;
+	}
+
+	return false;
+}
+
 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 found_is_marginal = true, fallback_is_marginal = true;
 
 	list_for_each_entry_srcu(ns, &head->list, siblings,
 				 srcu_read_lock_held(&head->srcu)) {
@@ -323,15 +357,19 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
 
 		switch (ns->ana_state) {
 		case NVME_ANA_OPTIMIZED:
-			if (distance < found_distance) {
+			if (is_best_distance(found_is_marginal, nvme_ctrl_is_marginal(ns->ctrl),
+					     found_distance, distance)) {
 				found_distance = distance;
 				found = ns;
+				found_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
 			}
 			break;
 		case NVME_ANA_NONOPTIMIZED:
-			if (distance < fallback_distance) {
+			if (is_best_distance(fallback_is_marginal, nvme_ctrl_is_marginal(ns->ctrl),
+					     fallback_distance, distance)) {
 				fallback_distance = distance;
 				fallback = ns;
+				fallback_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
 			}
 			break;
 		default:
@@ -339,6 +377,14 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
 		}
 	}
 
+	/*
+	 * Use non-optimized path only if it is not marginal
+	 * and no optimized path is marginal.
+	 */
+	if (found_is_marginal && !fallback_is_marginal)
+		found = fallback;
+
+	/* No optimized path found, use the fallback */
 	if (!found)
 		found = fallback;
 	if (found)
@@ -444,7 +490,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.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 04/13] nvme-multipath: queue-depth support for marginal paths
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (2 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 03/13] nvme-multipath: numa support for marginal paths Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 05/13] nvme-multipath: round-robin " Jesse Taube
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 -> V11:
 - New commit
---
 drivers/nvme/host/multipath.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 02936c580a37..dc34634842be 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -453,7 +453,9 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
 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;
+	int min_depth_opt = INT_MAX, min_depth_nonopt = INT_MAX;
+	bool opt_is_marginal = true, nonopt_is_marginal = true, marginal;
+
 	unsigned int depth;
 
 	list_for_each_entry_srcu(ns, &head->list, siblings,
@@ -462,28 +464,40 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
 			continue;
 
 		depth = atomic_read(&ns->ctrl->nr_active);
+		marginal = nvme_ctrl_is_marginal(ns->ctrl);
 
 		switch (ns->ana_state) {
 		case NVME_ANA_OPTIMIZED:
-			if (depth < min_depth_opt) {
+			if (is_best_distance(opt_is_marginal, marginal,
+					     min_depth_opt, depth)) {
 				min_depth_opt = depth;
 				best_opt = ns;
+				opt_is_marginal = marginal;
 			}
 			break;
 		case NVME_ANA_NONOPTIMIZED:
-			if (depth < min_depth_nonopt) {
+			if (is_best_distance(nonopt_is_marginal, marginal,
+					     min_depth_nonopt, depth)) {
 				min_depth_nonopt = depth;
 				best_nonopt = ns;
+				nonopt_is_marginal = marginal;
 			}
 			break;
 		default:
 			break;
 		}
 
-		if (min_depth_opt == 0)
+		if (min_depth_opt == 0 && !opt_is_marginal)
 			return best_opt;
 	}
 
+	/*
+	 * Prefer non-marginal non-optimized path
+	 * over a marginal optimized path.
+	 */
+	if (opt_is_marginal && !nonopt_is_marginal && best_nonopt)
+		return best_nonopt;
+
 	return best_opt ? best_opt : best_nonopt;
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 05/13] nvme-multipath: round-robin support for marginal paths
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (3 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 04/13] nvme-multipath: queue-depth " Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 06/13] nvme: sysfs: emit the marginal path state in show_state() Jesse Taube
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 -> V11:
 - New commit
---
 drivers/nvme/host/multipath.c | 47 +++++++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 5 deletions(-)

diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index dc34634842be..11f474704fdd 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -405,6 +405,7 @@ static struct nvme_ns *nvme_next_ns(struct nvme_ns_head *head,
 static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
 {
 	struct nvme_ns *ns, *found = NULL;
+	bool found_is_marginal = true;
 	int node = numa_node_id();
 	struct nvme_ns *old = srcu_dereference(head->current_path[node],
 					       &head->srcu);
@@ -425,22 +426,58 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
 			continue;
 
 		if (ns->ana_state == NVME_ANA_OPTIMIZED) {
+			if (found_is_marginal && nvme_ctrl_is_marginal(ns->ctrl)) {
+				/*
+				 * A marginal path has already found,
+				 * or this is the first path found.
+				 * This one is also marginal, but optimized,
+				 * so prefer it.
+				 */
+				found = ns;
+				found_is_marginal = 1;
+				continue;
+			}
+
+
+			/*
+			 * A non-marginal path has already found.
+			 * This one is marginal, so skip it.
+			 */
+			if (nvme_ctrl_is_marginal(ns->ctrl))
+				continue;
+
+			/* Found a non-marginal, optimized path use it. */
 			found = ns;
 			goto out;
 		}
-		if (ns->ana_state == NVME_ANA_NONOPTIMIZED)
+		if (ns->ana_state == NVME_ANA_NONOPTIMIZED) {
+			/*
+			 * A path has already found. This one is marginal,
+			 * so skip it.
+			 */
+			if (found && nvme_ctrl_is_marginal(ns->ctrl))
+				continue;
 			found = ns;
+			found_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
+		}
 	}
 
 	/*
 	 * 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 non-marginal optimized path found and current is,
+	 *      optimized and not 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_disabled(old) && !found)
+		return old;
+	/*
+	 * no other non-marginal optimized path found and current is,
+	 *   optimized and not marginal.
+	 */
+	if (!nvme_path_is_disabled(old) && !nvme_ctrl_is_marginal(old->ctrl) &&
+	    (old->ana_state == NVME_ANA_OPTIMIZED || found_is_marginal))
 		return old;
 
 	if (!found)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 06/13] nvme: sysfs: emit the marginal path state in show_state()
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (4 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 05/13] nvme-multipath: round-robin " Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 07/13] scsi: scsi_transport_fc: Add set_rport_marginal to fc_function_template Jesse Taube
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 -> V11:
 - No change
---
 drivers/nvme/host/sysfs.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index 75b2d69b5957..01c771dc88cb 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -527,7 +527,9 @@ static ssize_t nvme_sysfs_show_state(struct device *dev,
 	};
 
 	if (state < ARRAY_SIZE(state_name) && state_name[state])
-		return sysfs_emit(buf, "%s\n", state_name[state]);
+		return sysfs_emit(buf, "%s\n",
+			(nvme_ctrl_is_marginal(ctrl)) ? "marginal" :
+			state_name[state]);
 
 	return sysfs_emit(buf, "unknown state\n");
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 07/13] scsi: scsi_transport_fc: Add set_rport_marginal to fc_function_template
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (5 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 06/13] nvme: sysfs: emit the marginal path state in show_state() Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 08/13] scsi: scsi_transport_fc: user support for clearing NVME_CTRL_MARGINAL Jesse Taube
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block

Add fc_fpin_set_marginal function to evaluate the FPIN LI TLV
information and set the 'marginal' path status for all
affected nvme rports. Then call set_rport_marginal() callback added
to fc_function_template.

The implementation of set_rport_marginal() is almost identical to
set_rport_dev_loss_tmo implementation.

Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
V10 -> V11:
 - New commit
 - Replaces scsi: scsi_transport_fc:
    add fc_host_fpin_set_nvme_rport_marginal()
---
 drivers/scsi/scsi_transport_fc.c | 20 ++++++++++++++++++++
 include/scsi/scsi_transport_fc.h |  1 +
 2 files changed, 21 insertions(+)

diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 02c58f4591d8..df1edf3c23fa 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -737,6 +737,25 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
 	}
 }
 
+static void fc_fpin_set_marginal(struct Scsi_Host *shost, struct fc_rport *rport)
+{
+	struct fc_internal *i = to_fc_internal(shost->transportt);
+	unsigned long flags;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+
+	if (rport->port_state == FC_PORTSTATE_ONLINE &&
+	    rport->roles & FC_PORT_ROLE_NVME_TARGET) {
+		rport->port_state = FC_PORTSTATE_MARGINAL;
+		spin_unlock_irqrestore(shost->host_lock, flags);
+		if (i->f->set_rport_marginal)
+			i->f->set_rport_marginal(rport, true);
+		return;
+	}
+
+	spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
 static void
 fc_fpin_pname_stats_update(struct Scsi_Host *shost,
 			   struct fc_rport *attach_rport, u16 event_type,
@@ -764,6 +783,7 @@ fc_fpin_pname_stats_update(struct Scsi_Host *shost,
 			if (rport == attach_rport)
 				continue;
 			stats_update(event_type, &rport->fpin_stats);
+			fc_fpin_set_marginal(shost, rport);
 		}
 	}
 }
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
index 9f30625aa0d3..bc7c3f832399 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -684,6 +684,7 @@ struct fc_host_attrs {
 struct fc_function_template {
 	void    (*get_rport_dev_loss_tmo)(struct fc_rport *);
 	void	(*set_rport_dev_loss_tmo)(struct fc_rport *, u32);
+	void	(*set_rport_marginal)(struct fc_rport *rport, bool marginal);
 
 	void	(*get_starget_node_name)(struct scsi_target *);
 	void	(*get_starget_port_name)(struct scsi_target *);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 08/13] scsi: scsi_transport_fc: user support for clearing NVME_CTRL_MARGINAL
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (6 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 07/13] scsi: scsi_transport_fc: Add set_rport_marginal to fc_function_template Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 09/13] nvme-fc: add nvme_fc_set_remoteport_fpin() Jesse Taube
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block

From: John Meneghini <jmeneghi@redhat.com>

Refactor and fc_rport_set_marginal_state smp safe by holding
`shost->host_lock` around all `rport->port_state` accesses.

Call nvme_fc_modify_rport_fpin_state() when FC_PORTSTATE_MARGINAL is set
or cleared.  This allows the user to quickly set or clear the
NVME_CTRL_MARGINAL state from sysfs.

E.g.:

 echo "Marginal" > /sys/class/fc_remote_ports/rport-13:0-5/port_state
 echo "Online" > /sys/class/fc_remote_ports/rport-13:0-5/port_state

Note: nvme_fc_modify_rport_fpin_state() will only affect rports that
      have FC_PORT_ROLE_NVME_TARGET set.

Signed-off-by: John Meneghini <jmeneghi@redhat.com>
---
V10 -> V11:
 - No change
---
 drivers/scsi/scsi_transport_fc.c | 42 +++++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index df1edf3c23fa..af309a7bdc22 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -1251,34 +1251,58 @@ static ssize_t fc_rport_set_marginal_state(struct device *dev,
 						const char *buf, size_t count)
 {
 	struct fc_rport *rport = transport_class_to_rport(dev);
+	struct Scsi_Host *shost = rport_to_shost(rport);
+	struct fc_internal *i = to_fc_internal(shost->transportt);
 	enum fc_port_state port_state;
 	int ret = 0;
+	unsigned long flags;
 
 	ret = get_fc_port_state_match(buf, &port_state);
 	if (ret)
 		return -EINVAL;
-	if (port_state == FC_PORTSTATE_MARGINAL) {
+
+	spin_lock_irqsave(shost->host_lock, flags);
+
+	switch (port_state) {
+	case FC_PORTSTATE_MARGINAL:
 		/*
 		 * Change the state to Marginal only if the
 		 * current rport state is Online
 		 * Allow only Online->Marginal
 		 */
-		if (rport->port_state == FC_PORTSTATE_ONLINE)
+		if (rport->port_state == FC_PORTSTATE_ONLINE) {
 			rport->port_state = port_state;
-		else if (port_state != rport->port_state)
-			return -EINVAL;
-	} else if (port_state == FC_PORTSTATE_ONLINE) {
+			spin_unlock_irqrestore(shost->host_lock, flags);
+			if (i->f->set_rport_marginal)
+				i->f->set_rport_marginal(rport, true);
+			return count;
+		}
+		break;
+
+	case FC_PORTSTATE_ONLINE:
 		/*
 		 * Change the state to Online only if the
 		 * current rport state is Marginal
 		 * Allow only Marginal->Online
 		 */
-		if (rport->port_state == FC_PORTSTATE_MARGINAL)
+		if (rport->port_state == FC_PORTSTATE_MARGINAL) {
 			rport->port_state = port_state;
-		else if (port_state != rport->port_state)
-			return -EINVAL;
-	} else
+			spin_unlock_irqrestore(shost->host_lock, flags);
+			if (i->f->set_rport_marginal)
+				i->f->set_rport_marginal(rport, false);
+			return count;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (port_state != rport->port_state) {
+		spin_unlock_irqrestore(shost->host_lock, flags);
 		return -EINVAL;
+	}
+
+	spin_unlock_irqrestore(shost->host_lock, flags);
 	return count;
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 09/13] nvme-fc: add nvme_fc_set_remoteport_fpin()
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (7 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 08/13] scsi: scsi_transport_fc: user support for clearing NVME_CTRL_MARGINAL Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 10/13] scsi: qla2xxx: enable FPIN notification for NVMe Jesse Taube
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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 -> V11:
 - Remove nvme_fc_modify_rport_fpin_state
 - Use struct nvme_fc_remote_port instead of wwpn and wwnn
---
 drivers/nvme/host/fc.c         | 16 ++++++++++++++++
 include/linux/nvme-fc-driver.h |  2 ++
 2 files changed, 18 insertions(+)

diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index d2ca65f70a0c..4acf23ea8aea 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -895,6 +895,22 @@ 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;
+
+	spin_lock_irq(&rport->lock);
+	list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) {
+		if (marginal)
+			set_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags);
+		else
+			clear_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags);
+	}
+	spin_unlock_irq(&rport->lock);
+}
+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.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 10/13] scsi: qla2xxx: enable FPIN notification for NVMe
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (8 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 09/13] nvme-fc: add nvme_fc_set_remoteport_fpin() Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 11/13] scsi: lpfc: " Jesse Taube
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block

Implement set_rport_marginal callback using
nvme_fc_set_remoteport_fpin() to enable FPIN notifications for NVMe.

Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
V10 -> V11:
 - Rewrite
---
 drivers/scsi/qla2xxx/qla_attr.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index a4ca22024ede..92db7d584eeb 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -2789,6 +2789,15 @@ qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
 					       rport->dev_loss_tmo);
 }
 
+static inline void
+qla2x00_set_rport_marginal(struct fc_rport *rport, bool marginal)
+{
+	fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+
+	if (IS_ENABLED(CONFIG_NVME_FC) && fcport && fcport->nvme_remote_port)
+		nvme_fc_set_remoteport_fpin(fcport->nvme_remote_port, marginal);
+}
+
 static void
 qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport)
 {
@@ -3379,6 +3388,8 @@ struct fc_function_template qla2xxx_transport_functions = {
 	.set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
 	.show_rport_dev_loss_tmo = 1,
 
+	.set_rport_marginal = qla2x00_set_rport_marginal,
+
 	.issue_fc_host_lip = qla2x00_issue_lip,
 	.dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
 	.terminate_rport_io = qla2x00_terminate_rport_io,
@@ -3427,6 +3438,8 @@ struct fc_function_template qla2xxx_transport_vport_functions = {
 	.set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
 	.show_rport_dev_loss_tmo = 1,
 
+	.set_rport_marginal = qla2x00_set_rport_marginal,
+
 	.issue_fc_host_lip = qla2x00_issue_lip,
 	.dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
 	.terminate_rport_io = qla2x00_terminate_rport_io,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 11/13] scsi: lpfc: enable FPIN notification for NVMe
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (9 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 10/13] scsi: qla2xxx: enable FPIN notification for NVMe Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:12 ` [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs Jesse Taube
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block

Implement set_rport_marginal callback using
nvme_fc_set_remoteport_fpin() to enable FPIN notifications for NVMe.

Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
V10 -> V11
 - Rewrite
---
 drivers/scsi/lpfc/lpfc_attr.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index f4e8164b94ab..d54899cceed9 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -7134,6 +7134,30 @@ lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
 #endif
 }
 
+static void
+lpfc_set_rport_marginal(struct fc_rport *rport, bool marginal)
+{
+	struct lpfc_rport_data *rdata = rport->dd_data;
+	struct lpfc_nodelist *ndlp = rdata->pnode;
+	struct lpfc_nvme_rport *nrport = NULL;
+
+	/* Break early if NVME_FC is not enabled */
+	if (!IS_ENABLED(CONFIG_NVME_FC))
+		return;
+
+	if (!ndlp) {
+		dev_info(&rport->dev, "Cannot find remote node to ");
+		dev_info(&rport->dev, "set rport marginal, port_id x%x\n",
+			 rport->port_id);
+		return;
+	}
+
+	nrport = lpfc_ndlp_get_nrport(ndlp);
+
+	if (nrport && nrport->remoteport)
+		nvme_fc_set_remoteport_fpin(nrport->remoteport, marginal);
+}
+
 /*
  * lpfc_rport_show_function - Return rport target information
  *
@@ -7244,6 +7268,8 @@ struct fc_function_template lpfc_transport_functions = {
 	.set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
 	.show_rport_dev_loss_tmo = 1,
 
+	.set_rport_marginal = lpfc_set_rport_marginal,
+
 	.get_starget_port_id  = lpfc_get_starget_port_id,
 	.show_starget_port_id = 1,
 
@@ -7315,6 +7341,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
 	.set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
 	.show_rport_dev_loss_tmo = 1,
 
+	.set_rport_marginal = lpfc_set_rport_marginal,
+
 	.get_starget_port_id  = lpfc_get_starget_port_id,
 	.show_starget_port_id = 1,
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (10 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 11/13] scsi: lpfc: " Jesse Taube
@ 2026-08-12 18:12 ` Jesse Taube
  2026-08-12 18:34   ` Jesse Taube
  2026-08-12 18:13 ` [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state Jesse Taube
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:12 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, 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_rport_marginal`

Turn off marginal for a remote port matching wwnn and wwpn:
`echo 'wwnn=0x200000109b5f2956,wwpn=0x100000109b5f2956,marginal=0' >
/sys/class/fcloop/ctl/set_rport_marginal`

Suggested-by: John Meneghini <jmeneghi@redhat.com>
Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
V10 -> V11
 - New patch
---
 drivers/nvme/target/fcloop.c | 41 ++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index b63af3b643a6..51a50743128d 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,31 @@ 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;
+	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 || !nport->tport || !nport->tport->remoteport)
+		return -ENOENT;
+
+	nvme_fc_set_remoteport_fpin(nport->tport->remoteport, opts.marginal);
+	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 +1710,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 +1720,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.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (11 preceding siblings ...)
  2026-08-12 18:12 ` [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs Jesse Taube
@ 2026-08-12 18:13 ` Jesse Taube
  2026-08-12 18:46   ` Randy Dunlap
  2026-08-13  4:53 ` [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling Nilesh Javali
  2026-08-14  6:36 ` Christoph Hellwig
  14 siblings, 1 reply; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:13 UTC (permalink / raw)
  To: linux-scsi
  Cc: 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-nvme, linux-block

Adds documentation for the new marginal state for
NVMe over Fibre Channel. Documents how to check and reset the state,
as well as a description of how it affects multipath policies.

Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
---
 Documentation/admin-guide/nvme-multipath.rst | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/Documentation/admin-guide/nvme-multipath.rst b/Documentation/admin-guide/nvme-multipath.rst
index 97ca1ccef459..8df4235d4d10 100644
--- a/Documentation/admin-guide/nvme-multipath.rst
+++ b/Documentation/admin-guide/nvme-multipath.rst
@@ -31,6 +31,23 @@ To set the desired policy (e.g., round-robin), use one of the following methods:
    1. echo -n "round-robin" > /sys/module/nvme_core/parameters/iopolicy
    2. or add the "nvme_core.iopolicy=round-robin" to cmdline.
 
+FC-NVMe
+----
+On NVMe over Fibre Channel there is an additional state called `marginal`
+All policies will treat this path as offline and will not use it unless
+all other paths are marginal or unavailable. In that case, the policy will
+select the best path based on the policy.
+
+To see the current path state, use one of the following methods:
+   1. cat /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
+   2. cat /sys/devices/virtual/nvme-subsystem/nvme-subsys*/nvme*/state
+To reset a path to online use:
+   1. echo "Online" > /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
+
+The marginal state is caused by a
+FPIN-LI (Fabric Performance Impact Notification - Link Impact) event.
+FPIN-LI event counters can be seen with:
+   1. cat /sys/class/fc_host/host*/statistics/fpin_li*
 
 NUMA
 ----
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs
  2026-08-12 18:12 ` [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs Jesse Taube
@ 2026-08-12 18:34   ` Jesse Taube
  0 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-12 18:34 UTC (permalink / raw)
  To: linux-scsi
  Cc: Jonathan Corbet, Shuah Khan, Keith Busch, Jens Axboe,
	Christoph Hellwig, Sagi Grimberg, Naresh Gottumukkala, Paul Ely,
	Chaitanya Kulkarni, James E.J. Bottomley, Martin K. Petersen,
	Nilesh Javali, GR-QLogic-Storage-Upstream, Hannes Reinecke,
	Gustavo A. R. Silva, John Meneghini, Bryan Gurney, Chris Leech,
	Ewan D . Milne, shinichiro.kawasaki, linux-doc, linux-kernel,
	linux-nvme, linux-block

On Wed, Aug 12, 2026 at 2:14 PM Jesse Taube <jtaubepe@redhat.com> wrote:
>
> 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_rport_marginal`
>
> Turn off marginal for a remote port matching wwnn and wwpn:
> `echo 'wwnn=0x200000109b5f2956,wwpn=0x100000109b5f2956,marginal=0' >
> /sys/class/fcloop/ctl/set_rport_marginal`
>
> Suggested-by: John Meneghini <jmeneghi@redhat.com>
> Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
> V10 -> V11
>  - New patch
> ---
>  drivers/nvme/target/fcloop.c | 41 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index b63af3b643a6..51a50743128d 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,31 @@ 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;
> +       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 || !nport->tport || !nport->tport->remoteport)
> +               return -ENOENT;
> +
> +       nvme_fc_set_remoteport_fpin(nport->tport->remoteport, opts.marginal);
> +       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 +1710,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);

Checkpatch is complaining about:
Consider renaming function(s) 'fcloop_set_marginal_rport' to
'set_marginal_rport_store'
I dont think deviating from the existing naming scheme to appease
checkpatch is a good idea though.

Thanks,
Jesse Taube

>
>  static struct attribute *fcloop_dev_attrs[] = {
>         &dev_attr_add_local_port.attr,
> @@ -1680,6 +1720,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.54.0
>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state
  2026-08-12 18:13 ` [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state Jesse Taube
@ 2026-08-12 18:46   ` Randy Dunlap
  2026-08-12 18:50     ` Randy Dunlap
  0 siblings, 1 reply; 20+ messages in thread
From: Randy Dunlap @ 2026-08-12 18:46 UTC (permalink / raw)
  To: Jesse Taube, linux-scsi
  Cc: 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, Gustavo A. R. Silva, John Meneghini,
	Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
	linux-doc, linux-kernel, linux-nvme, linux-block



On 8/12/26 11:13 AM, Jesse Taube wrote:
> Adds documentation for the new marginal state for
> NVMe over Fibre Channel. Documents how to check and reset the state,
> as well as a description of how it affects multipath policies.
> 
> Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
> ---
>  Documentation/admin-guide/nvme-multipath.rst | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 

'make htmldocs' build warnings:

linux-next/Documentation/admin-guide/nvme-multipath.rst:35: WARNING: Title underline too short.

FC-NVMe
---- [docutils]
linux-next/Documentation/admin-guide/nvme-multipath.rst:50: ERROR: Unexpected indentation. [docutils]

> diff --git a/Documentation/admin-guide/nvme-multipath.rst b/Documentation/admin-guide/nvme-multipath.rst
> index 97ca1ccef459..8df4235d4d10 100644
> --- a/Documentation/admin-guide/nvme-multipath.rst
> +++ b/Documentation/admin-guide/nvme-multipath.rst
> @@ -31,6 +31,23 @@ To set the desired policy (e.g., round-robin), use one of the following methods:
>     1. echo -n "round-robin" > /sys/module/nvme_core/parameters/iopolicy
>     2. or add the "nvme_core.iopolicy=round-robin" to cmdline.
>  
> +FC-NVMe
> +----
> +On NVMe over Fibre Channel there is an additional state called `marginal`
> +All policies will treat this path as offline and will not use it unless
> +all other paths are marginal or unavailable. In that case, the policy will
> +select the best path based on the policy.
> +
> +To see the current path state, use one of the following methods:
> +   1. cat /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
> +   2. cat /sys/devices/virtual/nvme-subsystem/nvme-subsys*/nvme*/state
> +To reset a path to online use:
> +   1. echo "Online" > /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
> +
> +The marginal state is caused by a
> +FPIN-LI (Fabric Performance Impact Notification - Link Impact) event.
> +FPIN-LI event counters can be seen with:
> +   1. cat /sys/class/fc_host/host*/statistics/fpin_li*
>  
>  NUMA
>  ----

-- 
~Randy


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state
  2026-08-12 18:46   ` Randy Dunlap
@ 2026-08-12 18:50     ` Randy Dunlap
  0 siblings, 0 replies; 20+ messages in thread
From: Randy Dunlap @ 2026-08-12 18:50 UTC (permalink / raw)
  To: Jesse Taube, linux-scsi
  Cc: 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, Gustavo A. R. Silva, John Meneghini,
	Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
	linux-doc, linux-kernel, linux-nvme, linux-block



On 8/12/26 11:46 AM, Randy Dunlap wrote:
> 
> 
> On 8/12/26 11:13 AM, Jesse Taube wrote:
>> Adds documentation for the new marginal state for
>> NVMe over Fibre Channel. Documents how to check and reset the state,
>> as well as a description of how it affects multipath policies.
>>
>> Signed-off-by: Jesse Taube <jtaubepe@redhat.com>
>> ---
>>  Documentation/admin-guide/nvme-multipath.rst | 17 +++++++++++++++++
>>  1 file changed, 17 insertions(+)
>>
> 
> 'make htmldocs' build warnings:
> 
> linux-next/Documentation/admin-guide/nvme-multipath.rst:35: WARNING: Title underline too short.
> 
> FC-NVMe
> ---- [docutils]
> linux-next/Documentation/admin-guide/nvme-multipath.rst:50: ERROR: Unexpected indentation. [docutils]

Some fixes below.

>> diff --git a/Documentation/admin-guide/nvme-multipath.rst b/Documentation/admin-guide/nvme-multipath.rst
>> index 97ca1ccef459..8df4235d4d10 100644
>> --- a/Documentation/admin-guide/nvme-multipath.rst
>> +++ b/Documentation/admin-guide/nvme-multipath.rst
>> @@ -31,6 +31,23 @@ To set the desired policy (e.g., round-robin), use one of the following methods:
>>     1. echo -n "round-robin" > /sys/module/nvme_core/parameters/iopolicy
>>     2. or add the "nvme_core.iopolicy=round-robin" to cmdline.
>>  
>> +FC-NVMe
>> +----

    -------

>> +On NVMe over Fibre Channel there is an additional state called `marginal`
>> +All policies will treat this path as offline and will not use it unless
>> +all other paths are marginal or unavailable. In that case, the policy will
>> +select the best path based on the policy.
>> +
>> +To see the current path state, use one of the following methods:
>> +   1. cat /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
>> +   2. cat /sys/devices/virtual/nvme-subsystem/nvme-subsys*/nvme*/state
>> +To reset a path to online use:
>> +   1. echo "Online" > /sys/class/fc_host/host*/device/rport-*/fc_remote_ports/rport-*/port_state
>> +
>> +The marginal state is caused by a
>> +FPIN-LI (Fabric Performance Impact Notification - Link Impact) event.
>> +FPIN-LI event counters can be seen with:
>> +   1. cat /sys/class/fc_host/host*/statistics/fpin_li*

The marginal state is caused by a
FPIN-LI (Fabric Performance Impact Notification - Link Impact) event.
FPIN-LI event counters can be seen with::

  cat /sys/class/fc_host/host*/statistics/fpin_li*


>>  NUMA
>>  ----
> 

-- 
~Randy


^ permalink raw reply	[flat|nested] 20+ messages in thread

* RE: [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (12 preceding siblings ...)
  2026-08-12 18:13 ` [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state Jesse Taube
@ 2026-08-13  4:53 ` Nilesh Javali
  2026-08-13 16:24   ` Jesse Taube
  2026-08-14  6:36 ` Christoph Hellwig
  14 siblings, 1 reply; 20+ messages in thread
From: Nilesh Javali @ 2026-08-13  4:53 UTC (permalink / raw)
  To: Jesse Taube, linux-scsi@vger.kernel.org
  Cc: 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, GR-QLogic-Storage-Upstream@marvell.com,
	Hannes Reinecke, Gustavo A. R. Silva, John Meneghini,
	Bryan Gurney, Chris Leech, Ewan D . Milne,
	shinichiro.kawasaki@wdc.com, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-nvme@lists.infradead.org,
	linux-block@vger.kernel.org


From: Jesse Taube <jtaubepe@redhat.com> 
Sent: Wednesday, August 12, 2026 11:43 PM
To: linux-scsi@vger.kernel.org
Cc: Jonathan Corbet <corbet@lwn.net>; Shuah Khan <skhan@linuxfoundation.org>; Keith Busch <kbusch@kernel.org>; Jens Axboe <axboe@kernel.dk>; Christoph Hellwig <hch@lst.de>; Sagi Grimberg <sagi@grimberg.me>; Justin Tee <justin.tee@broadcom.com>; Naresh Gottumukkala <nareshgottumukkala83@gmail.com>; Paul Ely <paul.ely@broadcom.com>; Chaitanya Kulkarni <kch@nvidia.com>; James E.J. Bottomley <James.Bottomley@HansenPartnership.com>; Martin K. Petersen <martin.petersen@oracle.com>; Nilesh Javali <njavali@marvell.com>; GR-QLogic-Storage-Upstream@marvell.com; Hannes Reinecke <hare@suse.de>; Jesse Taube <jtaubepe@redhat.com>; Gustavo A. R. Silva <gustavoars@kernel.org>; John Meneghini <jmeneghi@redhat.com>; Bryan Gurney <bgurney@redhat.com>; Chris Leech <cleech@redhat.com>; Ewan D . Milne <emilne@redhat.com>; shinichiro.kawasaki@wdc.com; linux-doc@vger.kernel.org; linux-kernel@vger.kernel.org; linux-nvme@lists.infradead.org; linux-block@vger.kernel.org
Subject: [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling

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 as a last resort. This patch adds a new controller
ZjQcmQRYFpfptBannerStart
Prioritize security for external emails: 
Confirm sender and content safety before clicking links or opening attachments 
  https://us-phishalarm-ewt.proofpoint.com/EWT/v1/CRVmXkqW!te3Z1f8UYnTa1E-duf06bvZlnS-1FrR2Jkcx8WQcjCWqN2c_NKqyKXnJoJcD4hWMKkUnua_-glfm3m67hWRah9Kf0UTKp-APm3w$  ‌ 


ZjQcmQRYFpfptBannerEnd
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 as a last resort.
This patch adds a new controller flag 'NVME_CTRL_MARGINAL' which will be
checked during multipath path selection, causing the path to be skipped
when checking for paths. If no other paths are available the 'marginal'
paths are considered for path selection.

The testing for this patch set was performed by Bryan Gurney, using the
process outlined by John Meneghini's presentation at LSFMM 2024, where
the fibre channel switch sends an FPIN notification on a specific switch
port, and the following is checked on the initiator:

1. The controllers corresponding to the paths on the port that has
received the notification are showing a set NVME_CTRL_MARGINAL flag.

   \
    +- nvme4 fc traddr=c,host_traddr=e live optimized
    +- nvme5 fc traddr=8,host_traddr=e live non-optimized
    +- nvme8 fc traddr=e,host_traddr=f marginal optimized
    +- nvme9 fc traddr=a,host_traddr=f marginal non-optimized

2. The I/O statistics of the test namespace show no I/O activity on the
controllers with NVME_CTRL_MARGINAL set.

   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     25001.00         0.00        97.66         0.00
   nvme4c9n1     25000.00         0.00        97.66         0.00
   nvme4n1       50011.00         0.00       195.36         0.00


   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     48360.00         0.00       188.91         0.00
   nvme4c9n1      1642.00         0.00         6.41         0.00
   nvme4n1       49981.00         0.00       195.24         0.00


   Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
   nvme4c4n1         0.00         0.00         0.00         0.00
   nvme4c5n1     50001.00         0.00       195.32         0.00
   nvme4c9n1         0.00         0.00         0.00         0.00
   nvme4n1       50016.00         0.00       195.38         0.00

Link: https://urldefense.proofpoint.com/v2/url?u=https-3A__people.redhat.com_jmeneghi_LSFMM-5F2024_LSFMM-5F2024-5FNVMe-5FCancel-5Fand-5FFPIN.pdf&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=0jhQeJW2swbeXGSnNtXSHqXkNX1eVjXmU7QSm6m2IpU&e=

Testing has been performed by sending all FPIN LI ELS messages from the
switch to the Host and verifying the proper nvme multi-pathing behavior
is effected with each of the eight different FPIN link integrity events.
Results were verified with iostat and with the nvme list-subsys command.

These tests were run with all scenarios including where there were only
non-optimized paths available, and where all paths were
marginal/degraded. All multi-path io-policies were tested including:
numa, round-robin and queue-depth. When all paths on the host are
marginal/degraded, I/O continues on the optimized path that was most
recently non-marginal.  If both of the optimized paths are down, I/O
properly continues on one of the marginal/degraded non-optimized paths.

Testing has been complete with both Broadcom (lpfc) and Marvell (qla2xx)
32GB HBAs.  Both HBAs successfully complete all tests.

For a complete description of the tests that were run, please see
bugzilla 220329.

To test the path handeling use:
https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_linux-2Dblktests_blktests_pull_264&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=XKJyyaOO9_UGG6uH5fv88U7bAke7X9H5Xz0qa39qubs&e=
or
https://urldefense.proofpoint.com/v2/url?u=https-3A__lore.kernel.org_linux-2Dnvme_20260812174503.3705830-2D1-2Djtaubepe-40redhat.com_&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=qPfemamvpsdH9yAG-9CRAILRyvMrzEaa_xjIaJTo1Ds&e=

To test the full set including FPIN decoding use:
https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_johnmeneghini_fpin-5Fli-5Ftests&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=SzJUICtyPkmr7xYyCu6_gZb5itPn8MZRQm4z0Zoewak&e=

Closes: https://urldefense.proofpoint.com/v2/url?u=https-3A__bugzilla.kernel.org_show-5Fbug.cgi-3Fid-3D220329&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=Ybu1spaO4qBWAVWu7hq8KHIP73xHRmGPfJCrZVoqacY&e=

New refactored implementation enables administrators to manually control
port marginal states via sysfs. For example:

# Set remote port to marginal state
echo "Marginal" > /sys/class/fc_remote_ports/rport-4:0-1/port_state

# Clear marginal state (set to online)
echo "Online" > /sys/class/fc_remote_ports/rport-4:0-1/port_state

Changes to the original submission:
- Changed flag name to 'marginal'
- Do not block marginal path; influence path selection instead
  to de-prioritize marginal paths

Changes to v2:
- Split off driver-specific modifications
- Introduce 'union fc_tlv_desc' to avoid casts

Changes to v3:
- Include reviews from Justin Tee
- Split marginal path handling patch

Changes to v4:
- Change 'u8' to '__u8' on fc_tlv_desc to fix a failure to build
- Print 'marginal' instead of 'live' in the state of controllers
  when they are marginal

Changes to v5:
- Minor spelling corrections to patch descriptions

Changes to v6:
- No code changes; added note about additional testing

Changes to v7:
- Split nvme core marginal flag addition into its own patch
- Add patch for queue_depth marginal path support

Changes to v8:
- Rebased patch series to nvme-6.17.
- Added patch from Gustavo Silva, "scsi: qla2xxx: Fix memcpy field-spanning
  write issue", which resolves the field-spanning write issue
- We decided to leave the "marginal" state as is, because the transport
  driver uses the term "marginal".

Changes to v9:
- Rebased patch series to nvme-6.18.
- Refactor and fix a patch from Gustavo Silva, "scsi: qla2xxx: Fix 2 memcpy
  field-spanning write issue", which resolves the field-spanning write
  issue.
  This new version of Gustavo's patch fixes a bug found in testing.

[NJ:] I could not find this qla2xxx fix in this series. Am I missing something.
This fix is certainly required to avoid few call traces reported, as discussed here,
https://lore.kernel.org/linux-nvme/97526d45-ec7d-48a0-bdc6-659f75839f53@embeddedor.com/#t

Thanks,
Nilesh

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling
  2026-08-13  4:53 ` [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling Nilesh Javali
@ 2026-08-13 16:24   ` Jesse Taube
  0 siblings, 0 replies; 20+ messages in thread
From: Jesse Taube @ 2026-08-13 16:24 UTC (permalink / raw)
  To: Nilesh Javali
  Cc: linux-scsi@vger.kernel.org, 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,
	GR-QLogic-Storage-Upstream@marvell.com, Hannes Reinecke,
	Gustavo A. R. Silva, John Meneghini, Bryan Gurney, Chris Leech,
	Ewan D . Milne, shinichiro.kawasaki@wdc.com,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-nvme@lists.infradead.org, linux-block@vger.kernel.org

On Thu, Aug 13, 2026 at 12:54 AM Nilesh Javali <njavali@marvell.com> wrote:
>
>
> From: Jesse Taube <jtaubepe@redhat.com>
> Sent: Wednesday, August 12, 2026 11:43 PM
> To: linux-scsi@vger.kernel.org
> Cc: Jonathan Corbet <corbet@lwn.net>; Shuah Khan <skhan@linuxfoundation.org>; Keith Busch <kbusch@kernel.org>; Jens Axboe <axboe@kernel.dk>; Christoph Hellwig <hch@lst.de>; Sagi Grimberg <sagi@grimberg.me>; Justin Tee <justin.tee@broadcom.com>; Naresh Gottumukkala <nareshgottumukkala83@gmail.com>; Paul Ely <paul.ely@broadcom.com>; Chaitanya Kulkarni <kch@nvidia.com>; James E.J. Bottomley <James.Bottomley@HansenPartnership.com>; Martin K. Petersen <martin.petersen@oracle.com>; Nilesh Javali <njavali@marvell.com>; GR-QLogic-Storage-Upstream@marvell.com; Hannes Reinecke <hare@suse.de>; Jesse Taube <jtaubepe@redhat.com>; Gustavo A. R. Silva <gustavoars@kernel.org>; John Meneghini <jmeneghi@redhat.com>; Bryan Gurney <bgurney@redhat.com>; Chris Leech <cleech@redhat.com>; Ewan D . Milne <emilne@redhat.com>; shinichiro.kawasaki@wdc.com; linux-doc@vger.kernel.org; linux-kernel@vger.kernel.org; linux-nvme@lists.infradead.org; linux-block@vger.kernel.org
> Subject: [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling
>
> 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 as a last resort. This patch adds a new controller
> ZjQcmQRYFpfptBannerStart
> Prioritize security for external emails:
> Confirm sender and content safety before clicking links or opening attachments
>   https://us-phishalarm-ewt.proofpoint.com/EWT/v1/CRVmXkqW!te3Z1f8UYnTa1E-duf06bvZlnS-1FrR2Jkcx8WQcjCWqN2c_NKqyKXnJoJcD4hWMKkUnua_-glfm3m67hWRah9Kf0UTKp-APm3w$  ‌
>
>
> ZjQcmQRYFpfptBannerEnd
> 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 as a last resort.
> This patch adds a new controller flag 'NVME_CTRL_MARGINAL' which will be
> checked during multipath path selection, causing the path to be skipped
> when checking for paths. If no other paths are available the 'marginal'
> paths are considered for path selection.
>
> The testing for this patch set was performed by Bryan Gurney, using the
> process outlined by John Meneghini's presentation at LSFMM 2024, where
> the fibre channel switch sends an FPIN notification on a specific switch
> port, and the following is checked on the initiator:
>
> 1. The controllers corresponding to the paths on the port that has
> received the notification are showing a set NVME_CTRL_MARGINAL flag.
>
>    \
>     +- nvme4 fc traddr=c,host_traddr=e live optimized
>     +- nvme5 fc traddr=8,host_traddr=e live non-optimized
>     +- nvme8 fc traddr=e,host_traddr=f marginal optimized
>     +- nvme9 fc traddr=a,host_traddr=f marginal non-optimized
>
> 2. The I/O statistics of the test namespace show no I/O activity on the
> controllers with NVME_CTRL_MARGINAL set.
>
>    Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
>    nvme4c4n1         0.00         0.00         0.00         0.00
>    nvme4c5n1     25001.00         0.00        97.66         0.00
>    nvme4c9n1     25000.00         0.00        97.66         0.00
>    nvme4n1       50011.00         0.00       195.36         0.00
>
>
>    Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
>    nvme4c4n1         0.00         0.00         0.00         0.00
>    nvme4c5n1     48360.00         0.00       188.91         0.00
>    nvme4c9n1      1642.00         0.00         6.41         0.00
>    nvme4n1       49981.00         0.00       195.24         0.00
>
>
>    Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s
>    nvme4c4n1         0.00         0.00         0.00         0.00
>    nvme4c5n1     50001.00         0.00       195.32         0.00
>    nvme4c9n1         0.00         0.00         0.00         0.00
>    nvme4n1       50016.00         0.00       195.38         0.00
>
> Link: https://urldefense.proofpoint.com/v2/url?u=https-3A__people.redhat.com_jmeneghi_LSFMM-5F2024_LSFMM-5F2024-5FNVMe-5FCancel-5Fand-5FFPIN.pdf&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=0jhQeJW2swbeXGSnNtXSHqXkNX1eVjXmU7QSm6m2IpU&e=
>
> Testing has been performed by sending all FPIN LI ELS messages from the
> switch to the Host and verifying the proper nvme multi-pathing behavior
> is effected with each of the eight different FPIN link integrity events.
> Results were verified with iostat and with the nvme list-subsys command.
>
> These tests were run with all scenarios including where there were only
> non-optimized paths available, and where all paths were
> marginal/degraded. All multi-path io-policies were tested including:
> numa, round-robin and queue-depth. When all paths on the host are
> marginal/degraded, I/O continues on the optimized path that was most
> recently non-marginal.  If both of the optimized paths are down, I/O
> properly continues on one of the marginal/degraded non-optimized paths.
>
> Testing has been complete with both Broadcom (lpfc) and Marvell (qla2xx)
> 32GB HBAs.  Both HBAs successfully complete all tests.
>
> For a complete description of the tests that were run, please see
> bugzilla 220329.
>
> To test the path handeling use:
> https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_linux-2Dblktests_blktests_pull_264&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=XKJyyaOO9_UGG6uH5fv88U7bAke7X9H5Xz0qa39qubs&e=
> or
> https://urldefense.proofpoint.com/v2/url?u=https-3A__lore.kernel.org_linux-2Dnvme_20260812174503.3705830-2D1-2Djtaubepe-40redhat.com_&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=qPfemamvpsdH9yAG-9CRAILRyvMrzEaa_xjIaJTo1Ds&e=
>
> To test the full set including FPIN decoding use:
> https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_johnmeneghini_fpin-5Fli-5Ftests&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=SzJUICtyPkmr7xYyCu6_gZb5itPn8MZRQm4z0Zoewak&e=
>
> Closes: https://urldefense.proofpoint.com/v2/url?u=https-3A__bugzilla.kernel.org_show-5Fbug.cgi-3Fid-3D220329&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=7My57sfPbDDcMFJyBD6G2keJbhsQZV7WIf8SjB-450c&m=ViZhyiiF6nkaVXOE1PpaKWe2JBe94gTGHA24wKzPP99uuwXoNoSYqcE55NQRqt6_&s=Ybu1spaO4qBWAVWu7hq8KHIP73xHRmGPfJCrZVoqacY&e=
>
> New refactored implementation enables administrators to manually control
> port marginal states via sysfs. For example:
>
> # Set remote port to marginal state
> echo "Marginal" > /sys/class/fc_remote_ports/rport-4:0-1/port_state
>
> # Clear marginal state (set to online)
> echo "Online" > /sys/class/fc_remote_ports/rport-4:0-1/port_state
>
> Changes to the original submission:
> - Changed flag name to 'marginal'
> - Do not block marginal path; influence path selection instead
>   to de-prioritize marginal paths
>
> Changes to v2:
> - Split off driver-specific modifications
> - Introduce 'union fc_tlv_desc' to avoid casts
>
> Changes to v3:
> - Include reviews from Justin Tee
> - Split marginal path handling patch
>
> Changes to v4:
> - Change 'u8' to '__u8' on fc_tlv_desc to fix a failure to build
> - Print 'marginal' instead of 'live' in the state of controllers
>   when they are marginal
>
> Changes to v5:
> - Minor spelling corrections to patch descriptions
>
> Changes to v6:
> - No code changes; added note about additional testing
>
> Changes to v7:
> - Split nvme core marginal flag addition into its own patch
> - Add patch for queue_depth marginal path support
>
> Changes to v8:
> - Rebased patch series to nvme-6.17.
> - Added patch from Gustavo Silva, "scsi: qla2xxx: Fix memcpy field-spanning
>   write issue", which resolves the field-spanning write issue
> - We decided to leave the "marginal" state as is, because the transport
>   driver uses the term "marginal".
>
> Changes to v9:
> - Rebased patch series to nvme-6.18.
> - Refactor and fix a patch from Gustavo Silva, "scsi: qla2xxx: Fix 2 memcpy
>   field-spanning write issue", which resolves the field-spanning write
>   issue.
>   This new version of Gustavo's patch fixes a bug found in testing.
>
> [NJ:] I could not find this qla2xxx fix in this series. Am I missing something.
> This fix is certainly required to avoid few call traces reported, as discussed here,
> https://lore.kernel.org/linux-nvme/97526d45-ec7d-48a0-bdc6-659f75839f53@embeddedor.com/#t

You fix:
Commit 7db93e3c580d ("scsi: qla2xxx: Enhance purex_entry handling for
29xx series")
fixes the issue.

I forgot to mention it in the change log for V11. i will include it in
the change log for V12.

Thanks,
Jesse Taube

>
> Thanks,
> Nilesh
>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 00/13] nvme-fc: FPIN link integrity handling
  2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
                   ` (13 preceding siblings ...)
  2026-08-13  4:53 ` [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling Nilesh Javali
@ 2026-08-14  6:36 ` Christoph Hellwig
  14 siblings, 0 replies; 20+ messages in thread
From: Christoph Hellwig @ 2026-08-14  6:36 UTC (permalink / raw)
  To: Jesse Taube
  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, Gustavo A. R. Silva, John Meneghini,
	Bryan Gurney, Chris Leech, Ewan D . Milne, shinichiro.kawasaki,
	linux-doc, linux-kernel, linux-nvme, linux-block

This touched a lot of core code for a fringe feature for a transport
that shouldn't even exist and is full of unmaintained and buggy code.

Why do you think we'd want to take it?


^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-08-14  6:36 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12 18:12 [PATCH 00/13] nvme-fc: FPIN link integrity handling Jesse Taube
2026-08-12 18:12 ` [PATCH 01/13] fc_els: use 'union fc_tlv_desc' Jesse Taube
2026-08-12 18:12 ` [PATCH 02/13] nvme: add NVME_CTRL_MARGINAL flag Jesse Taube
2026-08-12 18:12 ` [PATCH 03/13] nvme-multipath: numa support for marginal paths Jesse Taube
2026-08-12 18:12 ` [PATCH 04/13] nvme-multipath: queue-depth " Jesse Taube
2026-08-12 18:12 ` [PATCH 05/13] nvme-multipath: round-robin " Jesse Taube
2026-08-12 18:12 ` [PATCH 06/13] nvme: sysfs: emit the marginal path state in show_state() Jesse Taube
2026-08-12 18:12 ` [PATCH 07/13] scsi: scsi_transport_fc: Add set_rport_marginal to fc_function_template Jesse Taube
2026-08-12 18:12 ` [PATCH 08/13] scsi: scsi_transport_fc: user support for clearing NVME_CTRL_MARGINAL Jesse Taube
2026-08-12 18:12 ` [PATCH 09/13] nvme-fc: add nvme_fc_set_remoteport_fpin() Jesse Taube
2026-08-12 18:12 ` [PATCH 10/13] scsi: qla2xxx: enable FPIN notification for NVMe Jesse Taube
2026-08-12 18:12 ` [PATCH 11/13] scsi: lpfc: " Jesse Taube
2026-08-12 18:12 ` [PATCH 12/13] nvme: fcloop: Add set_rport_marginal to sysfs Jesse Taube
2026-08-12 18:34   ` Jesse Taube
2026-08-12 18:13 ` [PATCH 13/13] docs: nvme-multipath: Add FC-NVMe marginal state Jesse Taube
2026-08-12 18:46   ` Randy Dunlap
2026-08-12 18:50     ` Randy Dunlap
2026-08-13  4:53 ` [EXTERNAL] [PATCH 00/13] nvme-fc: FPIN link integrity handling Nilesh Javali
2026-08-13 16:24   ` Jesse Taube
2026-08-14  6:36 ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox