mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support
@ 2026-09-16 23:09 Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands Tyrel Datwyler
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

This series addresses a collection of bugs and corner cases introduced with in
the recently merged NVMe/FC support for the ibmvfc driver.

The NVMe/FC code added a parallel target management path, a new set of
sub-CRQ channels (nvme_scrqs), and integration with the nvme-fc transport
layer alongside the existing SCSI infrastructure. Several issues crept in
during that integration: missing initialisation, incorrect locking, blocking
calls in atomic context, and logic errors in shared code paths that were not
previously exercised by the SCSI-only driver.

The fixes fall into a few broad categories:

  - Correctness in the NVMe FCP/LS I/O path: initialise evt->tgt, fix trace
    logging reading SCSI IU fields for NVMe events, and ensure H_CLOSED send
    failures call fcp_req->done() so the transport is not silently stalled.

  - Locking and context fixes: defer nvme_fc_register_localport() out of the
    MAD completion handler (which runs under host_lock with IRQs disabled),
    and make the FCP and LS abort callbacks fully asynchronous to eliminate
    blocking wait_for_completion() calls on the same path.

  - Data races and UAF: protect tgt->nvme_remote_port reads and writes with
    host_lock, fix a use-after-free and stall in the LS abort timeout handler,
    and handle ibmvfc_send_event() failure in ibmvfc_cancel_all_mq() to
    prevent a hang and UAF on queued cancel events.

  - Shared-path bugs exposed by NVMe: fix an uninitialised _done dereference
    for SCSI TMF events on send failure, an uninitialised shwqs stack variable
    in ibmvfc_purge_requests(), a u16 overflow of max_cmds in
    ibmvfc_set_login_info() reachable with combined SCSI and NVMe queue
    counts, and a wrong target action in the implicit logout NULL-evt error
    path that caused an infinite reset loop.

  - Lifecycle and resource management: call ibmvfc_nvme_unregister() on
    adapter removal and before ibmvfc_release_sub_crqs() to close a TOCTOU
    race in ibmvfc_nvme_create_queue(), guard against local port leaks on
    link bounce, and fix NVMe sub-queue registration failure incorrectly
    disabling SCSI multiqueue.

  - Concurrency in target discovery: serialise the SCSI and NVMe
    discover-targets MADs so both completions have written their target counts
    before ibmvfc_alloc_targets() runs.

  - Minor fixes: correct an inverted suppress-ABTS capability check in the
    NVMe TMF path, fix an uninitialised status variable logged on LS abort
    send failure, and honour the nr_nvme_hw_queues module parameter when
    sizing NVMe queues at probe time.

Tyrel Datwyler (20):
  scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands
  scsi: ibmvfc: fix trace logging for NVMe FCP commands
  scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure
  scsi: ibmvfc: defer NVMe local port registration out of atomic context
  scsi: ibmvfc: fix uninitialized _done dereference for TMF events on
    send failure
  scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests()
  scsi: ibmvfc: fix uninitialized status logged on LS abort send failure
  scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF
    path
  scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout
    path
  scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info()
  scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send
    failure
  scsi: ibmvfc: fix data race on tgt->nvme_remote_port
  scsi: ibmvfc: make NVMe FCP abort callback asynchronous
  scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback
  scsi: ibmvfc: unregister NVMe local port on adapter removal
  scsi: ibmvfc: fix NVMe local port leak on fabric link bounce
  scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter
    removal
  scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI
    multiqueue
  scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race
    dropping targets
  scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe
    queue sizing

 drivers/scsi/ibmvscsi/ibmvfc-core.c |  79 +++++++++++++-----
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 123 +++++++++++++++-------------
 drivers/scsi/ibmvscsi/ibmvfc.h      |   2 +
 3 files changed, 127 insertions(+), 77 deletions(-)

-- 
2.55.0


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

* [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 02/20] scsi: ibmvfc: fix trace logging " Tyrel Datwyler
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_nvme_fcp_io() submits NVMe FCP requests without setting evt->tgt
on the allocated event. ibmvfc_init_event() does not initialize this
field, so it retains whatever value was left in the pool-allocated event
structure.

When an FCP request times out the NVMe-FC transport calls
ibmvfc_nvme_fcp_abort(), which in turn calls ibmvfc_init_fcp_abort() to
build the cancel MAD. That function derives the target pointer from
abt_evt->tgt:

  struct ibmvfc_target *tgt = abt_evt->tgt;
  ...
  tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
  tmf->assoc_id    = cpu_to_be64(tgt->assoc_id);

Because abt_evt->tgt was never initialized this is a guaranteed NULL or
stale-pointer dereference on the first NVMe I/O timeout, resulting in a
kernel panic.

Fix this by assigning evt->tgt = rport->private immediately after
ibmvfc_init_event(), consistent with how ibmvfc_nvme_ls_req() already
sets evt->tgt for LS requests. This also has the benefit of making the
associated remote port target visible on in-flight FCP events, which is
useful for live debugging (e.g. reading tgt->wwpn from a hung command
in a crash/kdump context).

Fixes: 4e70b8795ee3 ("scsi: ibmvfc: implement nvme-fc FCP abort callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 9ac2671a3567..9ed6522c2e9e 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -359,6 +359,7 @@ static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
 	ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq);
 
 	ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT);
+	evt->tgt = rport->private;
 	evt->fcp_req = fcp_req;
 	fcp_req->private = evt;
 
-- 
2.55.0


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

* [PATCH 02/20] scsi: ibmvfc: fix trace logging for NVMe FCP commands
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 03/20] scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure Tyrel Datwyler
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_trc_start() and ibmvfc_trc_end() handle IBMVFC_CMD_FORMAT events
by reading SCSI-specific fields via ibmvfc_get_fcp_iu() and
ibmvfc_get_fcp_rsp(). These helpers return a pointer into the v3scsi
union member of struct ibmvfc_cmd, but NVMe FCP commands store their
command IU in the v3nvme union member. Reading SCSI FCP fields out of an
NVMe IU produces garbage trace entries (wrong opcode, lun, tmf_flags,
xfer_len, rsp_code, and scsi_status).

Fix this by gating the SCSI-specific field reads on evt->cmnd within the
IBMVFC_CMD_FORMAT branch. For NVMe FCP events (evt->fcp_req set) record
the NVMe SQE opcode from v3nvme.iu.sqe.common.opcode and the payload
length from vfc_cmd->payload_len. The common fields that are valid for
both protocols (tgt_scsi_id, vfc_cmd->status, vfc_cmd->error) are
logged unconditionally.

Fixes: 73c13e30c56f ("scsi: ibmvfc: implement nvme-fc IO command submission callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 29 +++++++++++++++++++----------
 1 file changed, 19 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index aa7ae81df41b..6c243b2a84d7 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -257,11 +257,16 @@ static void ibmvfc_trc_start(struct ibmvfc_event *evt)
 
 	switch (entry->fmt) {
 	case IBMVFC_CMD_FORMAT:
-		entry->op_code = iu->cdb[0];
 		entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
-		entry->lun = scsilun_to_int(&iu->lun);
-		entry->tmf_flags = iu->tmf_flags;
-		entry->u.start.xfer_len = be32_to_cpu(iu->xfer_len);
+		if (evt->cmnd) {
+			entry->op_code = iu->cdb[0];
+			entry->lun = scsilun_to_int(&iu->lun);
+			entry->tmf_flags = iu->tmf_flags;
+			entry->u.start.xfer_len = be32_to_cpu(iu->xfer_len);
+		} else if (evt->fcp_req) {
+			entry->op_code = vfc_cmd->v3nvme.iu.sqe.common.opcode;
+			entry->u.start.xfer_len = be32_to_cpu(vfc_cmd->payload_len);
+		}
 		break;
 	case IBMVFC_MAD_FORMAT:
 		entry->op_code = be32_to_cpu(mad->opcode);
@@ -294,15 +299,19 @@ static void ibmvfc_trc_end(struct ibmvfc_event *evt)
 
 	switch (entry->fmt) {
 	case IBMVFC_CMD_FORMAT:
-		entry->op_code = iu->cdb[0];
 		entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
-		entry->lun = scsilun_to_int(&iu->lun);
-		entry->tmf_flags = iu->tmf_flags;
 		entry->u.end.status = be16_to_cpu(vfc_cmd->status);
 		entry->u.end.error = be16_to_cpu(vfc_cmd->error);
-		entry->u.end.fcp_rsp_flags = rsp->flags;
-		entry->u.end.rsp_code = rsp->data.info.rsp_code;
-		entry->u.end.scsi_status = rsp->scsi_status;
+		if (evt->cmnd) {
+			entry->op_code = iu->cdb[0];
+			entry->lun = scsilun_to_int(&iu->lun);
+			entry->tmf_flags = iu->tmf_flags;
+			entry->u.end.fcp_rsp_flags = rsp->flags;
+			entry->u.end.rsp_code = rsp->data.info.rsp_code;
+			entry->u.end.scsi_status = rsp->scsi_status;
+		} else if (evt->fcp_req) {
+			entry->op_code = vfc_cmd->v3nvme.iu.sqe.common.opcode;
+		}
 		break;
 	case IBMVFC_MAD_FORMAT:
 		entry->op_code = be32_to_cpu(mad->opcode);
-- 
2.55.0


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

* [PATCH 03/20] scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 02/20] scsi: ibmvfc: fix trace logging " Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context Tyrel Datwyler
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

When ibmvfc_send_event() returns H_CLOSED the event is freed without
notifying the NVMe-FC transport. The caller ibmvfc_nvme_fcp_io() returns
SCSI_MLQUEUE_HOST_BUSY back to the transport, which is a SCSI midlayer
concept the NVMe-FC transport does not interpret. The result is that the
NVMe-FC transport is left waiting on a request that was silently dropped,
causing the I/O to hang permanently.

Fix this by calling fcp_req->done() with a -EBUSY status before freeing
the event in the H_CLOSED path, consistent with how other FC-NVMe LLDDs
(e.g. lpfc) signal a retryable transport-level failure to the NVMe-FC
transport.

Fixes: 73c13e30c56f ("scsi: ibmvfc: implement nvme-fc IO command submission callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 6c243b2a84d7..26a73db66254 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1845,6 +1845,10 @@ int ibmvfc_send_event(struct ibmvfc_event *evt,
 				dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n");
 			if (evt->cmnd)
 				scsi_dma_unmap(evt->cmnd);
+			else if (evt->fcp_req) {
+				evt->fcp_req->status = -EBUSY;
+				evt->fcp_req->done(evt->fcp_req);
+			}
 			ibmvfc_free_event(evt);
 			return SCSI_MLQUEUE_HOST_BUSY;
 		}
-- 
2.55.0


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

* [PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (2 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 03/20] scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 05/20] scsi: ibmvfc: fix uninitialized _done dereference for TMF events on send failure Tyrel Datwyler
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_fabric_login_nvme_done() is invoked under vhost->host->host_lock
via the ibmvfc_locked_done() wrapper, which acquires the spinlock with
IRQs disabled before calling the MAD completion handler.

On the IBMVFC_MAD_SUCCESS path the handler called ibmvfc_nvme_register()
directly. That function calls nvme_fc_register_localport(), which
performs GFP_KERNEL memory allocations and may sleep — both illegal
inside a spinlock-held (atomic) context — resulting in a 'scheduling
while atomic' kernel panic during NVMe/FC fabric login.

Fix this by replacing the direct call with a new do_nvme_register flag
that mirrors the existing do_nvme_login pattern already used in the
driver. The flag is consumed in ibmvfc_do_work() under the
IBMVFC_HOST_ACTION_QUERY case, which runs in the kernel thread context
(ibmvfc_work) where sleeping allocations are safe. The lock is dropped
before calling ibmvfc_nvme_register() and re-acquired on return so the
rest of the QUERY case proceeds normally.

Fixes: 4eb644adc42f ("ibmvfc: register local nvme fc port after fabric login")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 8 +++++++-
 drivers/scsi/ibmvscsi/ibmvfc.h      | 1 +
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 26a73db66254..420ddb958466 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5245,7 +5245,7 @@ static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt)
 	switch (mad_status) {
 	case IBMVFC_MAD_SUCCESS:
 		fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
-		ibmvfc_nvme_register(vhost);
+		vhost->do_nvme_register = 1;
 		ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
 		break;
 	case IBMVFC_MAD_FAILED:
@@ -6072,6 +6072,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
 			vhost->job_step(vhost);
 		break;
 	case IBMVFC_HOST_ACTION_QUERY:
+		if (vhost->do_nvme_register) {
+			vhost->do_nvme_register = 0;
+			spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+			ibmvfc_nvme_register(vhost);
+			spin_lock_irqsave(&vhost->host->host_lock, flags);
+		}
 		list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
 			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
 		list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index ca80ceffe53a..df0775183d72 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1005,6 +1005,7 @@ struct ibmvfc_host {
 	unsigned int nvme_enabled:1;
 	unsigned int do_scsi_login:1;
 	unsigned int do_nvme_login:1;
+	unsigned int do_nvme_register:1;
 	unsigned int aborting_passthru:1;
 	unsigned int scan_complete:1;
 	int scan_timeout;
-- 
2.55.0


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

* [PATCH 05/20] scsi: ibmvfc: fix uninitialized _done dereference for TMF events on send failure
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (3 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 06/20] scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests() Tyrel Datwyler
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

In ibmvfc_send_event(), the non-H_CLOSED error path uses a bare else
clause to handle the case where evt->cmnd is NULL, assuming the event
must be a MAD and reassigning evt->done = evt->_done before calling it.

However, SCSI Task Management Function (TMF) events created by
ibmvfc_init_tmf() are initialised with IBMVFC_CMD_FORMAT, not
IBMVFC_MAD_FORMAT. ibmvfc_init_event() only populates evt->_done for
IBMVFC_MAD_FORMAT events; for IBMVFC_CMD_FORMAT events evt->_done is
never set. Since TMF events also have evt->cmnd == NULL (cleared by
ibmvfc_init_event()), they fall through to the bare else branch,
copying the uninitialised evt->_done into evt->done and immediately
calling it — a wild function-pointer dereference that results in a
kernel panic during SCSI error recovery under SAN error conditions.

Fix this by replacing the bare else with
'else if (evt->crq.format == IBMVFC_MAD_FORMAT)', gating the _done
reassignment strictly on the MAD format where evt->_done is guaranteed
to be valid. TMF events (IBMVFC_CMD_FORMAT, cmnd==NULL) no longer reach
this branch; their evt->done (ibmvfc_locked_done wrapping
ibmvfc_sync_completion) remains correct as initialised, allowing the
waiting ibmvfc_cancel_all_sq/mq paths to receive the completion
normally.

Fixes: 98e0f82a0d3f ("ibmvfc: split NVMe support into separate source file and add transport stubs")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 420ddb958466..89d271adccd0 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1859,7 +1859,7 @@ int ibmvfc_send_event(struct ibmvfc_event *evt,
 			evt->done = ibmvfc_vfc_eh_done;
 		} else if (evt->fcp_req || evt->ls_req) {
 			evt->done = ibmvfc_vfc_eh_done;
-		} else {
+		} else if (evt->crq.format == IBMVFC_MAD_FORMAT) {
 			evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
 			evt->done = evt->_done;
 		}
-- 
2.55.0


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

* [PATCH 06/20] scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests()
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (4 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 05/20] scsi: ibmvfc: fix uninitialized _done dereference for TMF events on send failure Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 07/20] scsi: ibmvfc: fix uninitialized status logged on LS abort send failure Tyrel Datwyler
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

In ibmvfc_purge_requests() the variables shwqs and nhwqs are declared as:

    int shwqs, nhwqs = 0;

This initialises only nhwqs to zero; shwqs is left uninitialised. Both
are assigned inside the 'if (vhost->using_channels)' block, so when
using_channels is false the block is skipped and shwqs retains its
garbage stack value. The subsequent loop

    for (i = 0; i < shwqs; i++)

then iterates an arbitrary number of times over scsi_scrqs.scrqs[], which
may be NULL on the non-channel path, resulting in out-of-bounds heap
accesses and a kernel crash during any adapter reset or shutdown that
occurs before channels are established.

Fix by initialising shwqs to 0 in the declaration so that both loop
bounds are zero when using_channels is false and neither sub-queue loop
executes.

Fixes: ffc022564293 ("ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 89d271adccd0..18e7a462e32d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1188,7 +1188,7 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
 	struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs;
 	struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs;
 	unsigned long flags;
-	int shwqs, nhwqs = 0;
+	int shwqs = 0, nhwqs = 0;
 	int i;
 
 	if (vhost->using_channels) {
-- 
2.55.0


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

* [PATCH 07/20] scsi: ibmvfc: fix uninitialized status logged on LS abort send failure
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (5 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 06/20] scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests() Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 08/20] scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF path Tyrel Datwyler
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

In ibmvfc_nvme_ls_abort(), 'status' is declared without an initialiser.
It is only assigned at line 223 on the ibmvfc_send_event() success path:

    wait_for_completion(&evt->comp);
    status = be16_to_cpu(rsp.mad_common.status);

When ibmvfc_send_event() fails the code jumps via 'goto out', skipping
the assignment entirely. The ibmvfc_dbg() call that immediately follows
the out: label then reads uninitialised stack memory and logs a
meaningless status value.

Fix by initialising status to IBMVFC_MAD_CRQ_ERROR at its declaration.
This sentinel accurately reflects that the underlying CRQ send failed
and produces a meaningful log message on the error path.

Fixes: 508e0371279894 ("ibmvfc: implement nvme-fc LS abort handling callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 9ed6522c2e9e..ddd750f1cf33 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -202,7 +202,7 @@ static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
 	struct ibmvfc_event *evt;
 	union ibmvfc_iu rsp;
 	unsigned long flags;
-	u16 status;
+	u16 status = IBMVFC_MAD_CRQ_ERROR;
 
 	evt = ibmvfc_get_event(&vhost->crq);
 	if (!vhost->logged_in || !evt)
-- 
2.55.0


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

* [PATCH 08/20] scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF path
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (6 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 07/20] scsi: ibmvfc: fix uninitialized status logged on LS abort send failure Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 09/20] scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout path Tyrel Datwyler
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_nvme_setup_fcp_abort() sets the IBMVFC_TMF_SUPPRESS_ABTS flag
only when the adapter does NOT have the IBMVFC_CAN_SUPPRESS_ABTS
capability, which is the exact opposite of the intended behaviour.

Remove the erroneous '!' negation so the flag is set when the adapter
actually reports the capability, consistent with how the SCSI TMF path
in ibmvfc_cancel_all() handles the same check.

Fixes: 508e0371279894 ("ibmvfc: implement nvme-fc LS abort handling callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index ddd750f1cf33..7fdf27fbe7e7 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -184,7 +184,7 @@ static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req
 	tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
 	tmf->common.length = cpu_to_be16(sizeof(*tmf));
 	if (vhost->state != IBMVFC_ACTIVE)
-		if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
+		if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
 			tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS);
 	tmf->cancel_key = cpu_to_be32((u64)abt_evt);
 	tmf->my_cancel_key = cpu_to_be32((u64)evt);
-- 
2.55.0


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

* [PATCH 09/20] scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout path
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (7 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 08/20] scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF path Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 10/20] scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info() Tyrel Datwyler
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_tgt_implicit_logout_and_del() calls ibmvfc_set_tgt_action() with
IBMVFC_TGT_ACTION_NONE in the NULL evt error path. However the state
machine in ibmvfc_set_tgt_action() explicitly handles LOGOUT_RPORT and
only permits transitions to LOGOUT_RPORT_WAIT or DEL_RPORT from that
state; NONE is silently rejected and returns -EINVAL, leaving the target
stuck in LOGOUT_RPORT.

With the target permanently in LOGOUT_RPORT, ibmvfc_dev_logo_to_do()
keeps returning 1, causing the host to spin in an infinite reset loop.

Use IBMVFC_TGT_ACTION_DEL_RPORT instead, which is the same transition
taken by the !vhost->logged_in early-exit path in the same function and
is the correct valid transition to unblock target cleanup.

Fixes: c8380c3ff651 ("ibmvfc: check for NULL evt in implicit LOGO and target delete path")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 18e7a462e32d..e5eb0997d507 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4542,7 +4542,7 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
 
 	if (!evt) {
 		vhost->discovery_threads--;
-		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
 		kref_put(&tgt->kref, ibmvfc_release_tgt);
 		__ibmvfc_reset_host(vhost);
 		return;
-- 
2.55.0


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

* [PATCH 10/20] scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info()
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (8 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 09/20] scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout path Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 11/20] scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send failure Tyrel Datwyler
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

max_cmds is declared as u16 but the calculation:

  scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ +
  (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
  (scsi_scrqs.desired_queues + nvme_scrqs.desired_queues)

can exceed 65535 at non-extreme configurations. With scsi_qdepth=2048
and 32 total desired queues (16 SCSI + 16 NVMe maximum), the subqueue
term alone evaluates to 2052 * 32 = 65664, which silently wraps to 128
in a u16, causing the NPIV login MAD to advertise a badly corrupted
command slot count to the VIOS.

The wire field login_info->max_cmds is already __be32, so widen the
local variable to u32 to match, making the arithmetic safe across all
supported module parameter combinations.

Fixes: bffd16f59061 ("ibmvfc: add logic for protocol specific fabric logins")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index e5eb0997d507..e2f2fa42bcfd 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1562,7 +1562,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
 	struct ibmvfc_queue *async_crq = &vhost->async_crq;
 	struct device_node *of_node = vhost->dev->of_node;
 	const char *location;
-	u16 max_cmds;
+	u32 max_cmds;
 
 	max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
 	if (mq_enabled)
-- 
2.55.0


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

* [PATCH 11/20] scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send failure
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (9 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 10/20] scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info() Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 12/20] scsi: ibmvfc: fix data race on tgt->nvme_remote_port Tyrel Datwyler
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_cancel_all_mq() discards the return value of ibmvfc_send_event().
When ibmvfc_send_event() fails due to H_CLOSED it internally frees the
event before returning SCSI_MLQUEUE_HOST_BUSY. The caller then performs
list_add_tail() on the freed event — a use-after-free — and subsequently
blocks indefinitely on wait_for_completion(&evt->comp) for a completion
that will never arrive.

Fix by capturing the return value. On failure, drop the locks, log the
error (consistent with ibmvfc_cancel_all_sq()), drain and free any
cancel events already queued from earlier loop iterations, then return 0.
Returning 0 on send failure is correct: when the adapter closes the CRQ
the firmware delivers a transport event through ibmvfc_handle_crq() which
completes all outstanding commands, so the error recovery caller will
naturally see them return.

Fixes: 5ca8f4e826ff ("ibmvfc: don't call locked done variant for MADs on send failure")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index e2f2fa42bcfd..190410847efb 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -2693,6 +2693,7 @@ static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
 	unsigned long flags;
 	int num_hwq, i;
 	int fail = 0;
+	int rc;
 	LIST_HEAD(cancelq);
 	u16 status;
 
@@ -2719,7 +2720,18 @@ static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
 				return -ENOMEM;
 			}
 			evt->sync_iu = &queues[i].cancel_rsp;
-			ibmvfc_send_event(evt, vhost, default_timeout);
+			rc = ibmvfc_send_event(evt, vhost, default_timeout);
+			if (rc) {
+				spin_unlock(queues[i].q_lock);
+				spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+				sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rc);
+				list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
+					wait_for_completion(&evt->comp);
+					list_del(&evt->cancel);
+					ibmvfc_free_event(evt);
+				}
+				return 0;
+			}
 			list_add_tail(&evt->cancel, &cancelq);
 		}
 
-- 
2.55.0


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

* [PATCH 12/20] scsi: ibmvfc: fix data race on tgt->nvme_remote_port
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (10 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 11/20] scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send failure Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 13/20] scsi: ibmvfc: make NVMe FCP abort callback asynchronous Tyrel Datwyler
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

tgt->nvme_remote_port is written in two places without holding host_lock:

1. ibmvfc_nvme_register_remoteport() calls nvme_fc_register_remoteport()
   and stores the result directly into tgt->nvme_remote_port with no lock
   held, racing against locked readers in ibmvfc_tgt_add_nvme_rport() and
   ibmvfc_do_work().

2. The ibmvfc_nvme_remoteport_delete() callback writes
   tgt->nvme_remote_port = NULL from the NVMe core's thread context with
   no lock held, again racing against every locked reader of the field.

All readers of tgt->nvme_remote_port acquire host_lock before reading the
field, so both writers must do the same.

For ibmvfc_nvme_register_remoteport(): nvme_fc_register_remoteport() may
sleep so it must be called before acquiring the spinlock. Use a local
pointer to capture the result, then take host_lock and store the pointer
into tgt->nvme_remote_port and set ->private under the lock.

For ibmvfc_nvme_remoteport_delete(): acquire host_lock around the
tgt->nvme_remote_port = NULL store. The callback is always invoked after
ibmvfc_nvme_unregister_remoteport() drops host_lock before waiting for
completion, so taking the lock here is safe.

Fixes: 696d1cc2aaa2 ("scsi: ibmvfc: process NVMe/FC rports in work thread")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 7fdf27fbe7e7..d9f280c658eb 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -26,8 +26,11 @@ static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
 static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
 {
 	struct ibmvfc_target *tgt = rport->private;
+	unsigned long flags;
 
+	spin_lock_irqsave(&tgt->vhost->host->host_lock, flags);
 	tgt->nvme_remote_port = NULL;
+	spin_unlock_irqrestore(&tgt->vhost->host->host_lock, flags);
 	complete(&tgt->nvme_delete_done);
 }
 
@@ -469,7 +472,9 @@ static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
 int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
 {
 	struct ibmvfc_host *vhost = tgt->vhost;
+	struct nvme_fc_remote_port *rport;
 	struct nvme_fc_port_info pinfo;
+	unsigned long flags;
 	int rc;
 
 	if (!IS_ENABLED(CONFIG_NVME_FC))
@@ -486,14 +491,16 @@ int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
 	pinfo.port_id = tgt->ids.port_id;
 	pinfo.port_role = FC_PORT_ROLE_NVME_TARGET;
 
-	rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo,
-					 &tgt->nvme_remote_port);
+	rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo, &rport);
 
+	spin_lock_irqsave(&vhost->host->host_lock, flags);
 	if (!rc) {
 		ibmvfc_log(vhost, 2, "register_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
 			   pinfo.node_name, pinfo.port_name, pinfo.port_id);
-		tgt->nvme_remote_port->private = tgt;
+		rport->private = tgt;
+		tgt->nvme_remote_port = rport;
 	}
+	spin_unlock_irqrestore(&vhost->host->host_lock, flags);
 
 	return rc;
 }
-- 
2.55.0


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

* [PATCH 13/20] scsi: ibmvfc: make NVMe FCP abort callback asynchronous
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (11 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 12/20] scsi: ibmvfc: fix data race on tgt->nvme_remote_port Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 14/20] scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback Tyrel Datwyler
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

The nvme_fc_port_template fcp_abort callback is called by the NVMe-FC
transport from the block layer timeout workqueue. The current
implementation submits a cancel MAD and then blocks on
wait_for_completion() until the VIOS response arrives. This stalls the
timeout workqueue for the full MAD round-trip, preventing any other I/O
timeout from being processed in the meantime.

The nvme-fc-driver.h API contract is explicit: the LLDD must initiate
the abort and return immediately; the ABTS does not need to be complete
on return. The transport waits for the original exchange to complete
independently via fcp_req->done().

Replace the synchronous ibmvfc_sync_nvme_completion callback with a
dedicated ibmvfc_nvme_fcp_abort_done() that runs asynchronously when
the cancel MAD response arrives. The new callback logs any non-zero MAD
status, drops the target kref, and frees the event. ibmvfc_send_event()
guarantees the done callback is invoked on both success and failure
paths, so event ownership is fully transferred and ibmvfc_nvme_fcp_abort()
returns immediately after ibmvfc_send_event().

Drop the now-unnecessary init_completion() from ibmvfc_init_fcp_abort()
and the sync_iu / wait_for_completion / second lock-cycle from the abort
function itself.

Fixes: 4e70b8795ee3 ("scsi: ibmvfc: implement nvme-fc FCP abort callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 35 ++++++++++++-----------------
 1 file changed, 14 insertions(+), 21 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index d9f280c658eb..52e2621a4342 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -396,8 +396,18 @@ static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt,
 	tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
 	tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
 	tmf->task_tag = cpu_to_be64((u64)abt_evt);
+}
 
-	init_completion(&evt->comp);
+static void ibmvfc_nvme_fcp_abort_done(struct ibmvfc_event *evt)
+{
+	u16 status = be16_to_cpu(evt->xfer_iu->mad_common.status);
+
+	if (status)
+		ibmvfc_dbg(evt->vhost, "fcp_abort: cancel MAD failed with rc=%x\n",
+			   status);
+
+	kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
 }
 
 static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
@@ -409,9 +419,7 @@ static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
 	struct ibmvfc_target *tgt = rport->private;
 	struct ibmvfc_event *evt, *abt_evt = abort_req->private;
 	struct ibmvfc_queue *queue;
-	union ibmvfc_iu rsp;
 	unsigned long flags;
-	u16 status = 0;
 
 	if (!abt_evt)
 		return;
@@ -426,27 +434,12 @@ static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
 
 	spin_lock_irqsave(queue->q_lock, flags);
 	kref_get(&tgt->kref);
-	ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+	ibmvfc_init_event(evt, ibmvfc_nvme_fcp_abort_done, IBMVFC_MAD_FORMAT);
 	ibmvfc_init_fcp_abort(evt, abort_req);
-	evt->sync_iu = &rsp;
-
-	if (ibmvfc_send_event(evt, vhost, default_timeout))
-		goto out;
+	evt->tgt = tgt;
 
+	ibmvfc_send_event(evt, vhost, default_timeout);
 	spin_unlock_irqrestore(queue->q_lock, flags);
-
-	wait_for_completion(&evt->comp);
-	status = be16_to_cpu(rsp.mad_common.status);
-
-	spin_lock_irqsave(queue->q_lock, flags);
-	ibmvfc_free_event(evt);
-out:
-	spin_unlock_irqrestore(queue->q_lock, flags);
-
-	if (status)
-		ibmvfc_dbg(vhost, "fcp_abort: cancel failed with rc=%x\n", status);
-
-	kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
-- 
2.55.0


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

* [PATCH 14/20] scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (12 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 13/20] scsi: ibmvfc: make NVMe FCP abort callback asynchronous Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 15/20] scsi: ibmvfc: unregister NVMe local port on adapter removal Tyrel Datwyler
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

Two problems in ibmvfc_nvme_ls_abort():

1. Use-after-free / stale pointer dereference.
   ibmvfc_init_ls_abort() reads abt_evt = ls_abort->private and
   immediately dereferences abt_evt->tgt.  ibmvfc_ls_req_done() calls
   ibmvfc_free_event() under host_lock, which returns the event slot to
   the pool.  If the LS completes naturally just before ls_abort is
   called, abt_evt points to a freed (and potentially reused) event,
   making the dereference a UAF.

   Fix by taking host_lock before reading ls_abort->private and checking
   evt->free (set to 1 by ibmvfc_free_event() under host_lock) to detect
   whether the original LS has already completed.  If so, there is
   nothing to cancel and we return early.  ibmvfc_get_event() is also
   moved inside the lock so the validity check and event allocation are
   atomic with respect to the completion path.

2. Blocking wait on timeout workqueue (same class as the FCP abort fix).
   The original code called wait_for_completion() from ls_abort, which
   is invoked by the NVMe-FC transport from a context that must not
   block.

   Fix by replacing ibmvfc_sync_nvme_completion with a dedicated async
   callback ibmvfc_nvme_ls_abort_done() that logs any non-zero MAD
   status, drops the target kref, and frees the event.
   ibmvfc_send_event() guarantees the callback fires on both success and
   failure paths, so ibmvfc_nvme_ls_abort() returns immediately after
   ibmvfc_send_event().

ibmvfc_sync_nvme_completion is now unused and is removed.
ibmvfc_init_ls_abort() is updated to take the validated abt_evt pointer
directly instead of deriving it from ls_abort->private.

Fixes: 20bec08f0208 ("scsi: ibmvfc: implement nvme-fc LS abort handling callback")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 68 +++++++++++++++--------------
 1 file changed, 35 insertions(+), 33 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 52e2621a4342..7e18b79ae4cc 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -164,21 +164,12 @@ static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
 	return 0;
 }
 
-static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt)
+static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt,
+				 struct ibmvfc_event *abt_evt)
 {
-	/* copy the response back */
-	if (evt->sync_iu)
-		*evt->sync_iu = *evt->xfer_iu;
-
-	complete(&evt->comp);
-}
-
-static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_abort)
-{
-	struct ibmvfc_tmf *tmf;
-	struct ibmvfc_event *abt_evt = ls_abort->private;
 	struct ibmvfc_target *tgt = abt_evt->tgt;
 	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_tmf *tmf;
 
 	tmf = &evt->iu.tmf;
 	memset(tmf, 0, sizeof(*tmf));
@@ -192,8 +183,18 @@ static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req
 	tmf->cancel_key = cpu_to_be32((u64)abt_evt);
 	tmf->my_cancel_key = cpu_to_be32((u64)evt);
 	tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+}
+
+static void ibmvfc_nvme_ls_abort_done(struct ibmvfc_event *evt)
+{
+	u16 status = be16_to_cpu(evt->xfer_iu->mad_common.status);
+
+	if (status)
+		ibmvfc_dbg(evt->vhost, "ls_abort: cancel MAD failed with rc=%x\n",
+			   status);
 
-	init_completion(&evt->comp);
+	kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
 }
 
 static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
@@ -202,34 +203,35 @@ static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
 {
 	struct ibmvfc_host *vhost = lport->private;
 	struct ibmvfc_target *tgt = rport->private;
-	struct ibmvfc_event *evt;
-	union ibmvfc_iu rsp;
+	struct ibmvfc_event *evt, *abt_evt;
 	unsigned long flags;
-	u16 status = IBMVFC_MAD_CRQ_ERROR;
+
+	spin_lock_irqsave(&vhost->host->host_lock, flags);
+
+	/*
+	 * If the original LS has already completed naturally, abt_evt will
+	 * have been freed back to the pool (evt->free set to 1 under
+	 * host_lock by ibmvfc_free_event()).  Nothing left to cancel.
+	 */
+	abt_evt = ls_abort->private;
+	if (!abt_evt || atomic_read(&abt_evt->free)) {
+		spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+		return;
+	}
 
 	evt = ibmvfc_get_event(&vhost->crq);
-	if (!vhost->logged_in || !evt)
+	if (!vhost->logged_in || !evt) {
+		spin_unlock_irqrestore(&vhost->host->host_lock, flags);
 		return;
+	}
 
-	spin_lock_irqsave(&vhost->host->host_lock, flags);
 	kref_get(&tgt->kref);
-	ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
-	ibmvfc_init_ls_abort(evt, ls_abort);
-	evt->sync_iu = &rsp;
-
-	if (ibmvfc_send_event(evt, vhost, default_timeout))
-		goto out;
-
-	spin_unlock_irqrestore(&vhost->host->host_lock, flags);
+	ibmvfc_init_event(evt, ibmvfc_nvme_ls_abort_done, IBMVFC_MAD_FORMAT);
+	ibmvfc_init_ls_abort(evt, abt_evt);
+	evt->tgt = tgt;
 
-	wait_for_completion(&evt->comp);
-	status = be16_to_cpu(rsp.mad_common.status);
-	spin_lock_irqsave(&vhost->host->host_lock, flags);
-	ibmvfc_free_event(evt);
-out:
+	ibmvfc_send_event(evt, vhost, default_timeout);
 	spin_unlock_irqrestore(&vhost->host->host_lock, flags);
-	ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
-	kref_put(&tgt->kref, ibmvfc_release_tgt);
 }
 
 static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
-- 
2.55.0


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

* [PATCH 15/20] scsi: ibmvfc: unregister NVMe local port on adapter removal
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (13 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 14/20] scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 16/20] scsi: ibmvfc: fix NVMe local port leak on fabric link bounce Tyrel Datwyler
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_remove() tears down the CRQ and frees all host memory but never
calls ibmvfc_nvme_unregister().  The NVMe-FC transport therefore retains
a live reference to the local port whose private pointer points into the
now-freed ibmvfc_host, leading to a use-after-free whenever the transport
subsequently touches the port (queue creation, port scanning, etc.).

ibmvfc_nvme_unregister() is already called from the NPIV logout and host
reset paths during normal operation; driver removal simply missed it.

Add the call after ibmvfc_release_crq_queue() — at which point the CRQ
is torn down so no further completions can arrive — and before
ibmvfc_free_mem(), so host memory is still valid while
nvme_fc_unregister_localport() runs and waits for the
localport_delete callback to complete.

Fixes: 86e495358096 ("scsi: ibmvfc: implement LLDD callbacks for mapping nvme-fc queues")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 190410847efb..7af1867b69c5 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6995,6 +6995,8 @@ static void ibmvfc_remove(struct vio_dev *vdev)
 	ibmvfc_release_sub_crqs(vhost);
 	ibmvfc_release_crq_queue(vhost);
 
+	ibmvfc_nvme_unregister(vhost);
+
 	ibmvfc_free_mem(vhost);
 	spin_lock(&ibmvfc_driver_lock);
 	list_del(&vhost->queue);
-- 
2.55.0


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

* [PATCH 16/20] scsi: ibmvfc: fix NVMe local port leak on fabric link bounce
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (14 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 15/20] scsi: ibmvfc: unregister NVMe local port on adapter removal Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 17/20] scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter removal Tyrel Datwyler
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

When the fabric link goes down, ibmvfc_link_down() does not call
ibmvfc_nvme_unregister(), so vhost->nvme_local_port remains set across
the link bounce.  When the adapter re-initializes and completes fabric
login, ibmvfc_nvme_register() is called again unconditionally.
nvme_fc_register_localport() finds the still-live port in the transport's
list (FC_OBJSTATE_ONLINE) and returns -EEXIST, writing NULL into
*portptr.  The original local port is now orphaned in the transport with
no way to reach it, while vhost->nvme_local_port is NULL, leaving NVMe
functionality permanently broken for the lifetime of the adapter.

Fix by unregistering any existing local port at the top of
ibmvfc_nvme_register() before attempting to register a new one.
ibmvfc_nvme_register() is called from ibmvfc_do_work() with host_lock
dropped, so ibmvfc_nvme_unregister()'s wait_for_completion() is safe
there.  This makes ibmvfc_nvme_register() idempotent across link bounces
without requiring changes to the link-down path.

Fixes: 3831863f9f56 ("scsi: ibmvfc: register local nvme fc port after fabric login")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 7e18b79ae4cc..a757a3d4fe27 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -530,6 +530,10 @@ int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
 	if (!IS_ENABLED(CONFIG_NVME_FC))
 		return 0;
 
+	/* Unregister any stale local port left from a previous link-up */
+	if (vhost->nvme_local_port)
+		ibmvfc_nvme_unregister(vhost);
+
 	pinfo.node_name = fc_host_node_name(vhost->host);
 	pinfo.port_name = fc_host_port_name(vhost->host);
 	pinfo.port_id = fc_host_port_id(vhost->host);
-- 
2.55.0


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

* [PATCH 17/20] scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter removal
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (15 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 16/20] scsi: ibmvfc: fix NVMe local port leak on fabric link bounce Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 18/20] scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI multiqueue Tyrel Datwyler
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_remove() called ibmvfc_release_sub_crqs() before
ibmvfc_nvme_unregister().  ibmvfc_release_sub_crqs() frees the
nvme_scrqs.scrqs array and sets active_queues to 0, but the nvme-fc
transport is still live at that point and can call back into
ibmvfc_nvme_create_queue().  That callback reads active_queues and
dereferences scrqs without any lock, so the window between the guard
check and the actual use can race with the teardown, leading to:

  - divide-by-zero in '(qidx - 1) % vhost->nvme_scrqs.active_queues'
    when active_queues has been zeroed concurrently, and
  - NULL pointer dereference on 'vhost->nvme_scrqs.scrqs[qhandle->index]'
    when scrqs has been freed and set to NULL concurrently.

ibmvfc_nvme_unregister() already calls nvme_fc_unregister_localport()
followed by wait_for_completion(), which blocks until the transport has
fully quiesced all queue operations.  It just needs to happen before the
sub-CRQ memory is released.

Move ibmvfc_nvme_unregister() before ibmvfc_release_sub_crqs() in
ibmvfc_remove() so the transport is quiesced before the sub-CRQ arrays
are freed.  The reset path (ibmvfc_reset_crq) is unaffected: it only
deregisters sub-CRQs at the hypervisor level and never frees them, so
active_queues and scrqs remain valid across a reset.

Fixes: 186f72e38029 ("scsi: ibmvfc: implement LLDD callbacks for mapping nvme-fc queues")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 7af1867b69c5..93189692191e 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6992,11 +6992,10 @@ static void ibmvfc_remove(struct vio_dev *vdev)
 	list_splice_init(&vhost->purge, &purge);
 	spin_unlock_irqrestore(&vhost->host->host_lock, flags);
 	ibmvfc_complete_purge(&purge);
+	ibmvfc_nvme_unregister(vhost);
 	ibmvfc_release_sub_crqs(vhost);
 	ibmvfc_release_crq_queue(vhost);
 
-	ibmvfc_nvme_unregister(vhost);
-
 	ibmvfc_free_mem(vhost);
 	spin_lock(&ibmvfc_driver_lock);
 	list_del(&vhost->queue);
-- 
2.55.0


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

* [PATCH 18/20] scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI multiqueue
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (16 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 17/20] scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter removal Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 19/20] scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race dropping targets Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 20/20] scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe queue sizing Tyrel Datwyler
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_reg_sub_crqs() clears vhost->do_enquiry unconditionally when any
channel registration fails.  The function is called for SCSI channels
first and then for NVMe channels (in ibmvfc_init_sub_crqs(),
ibmvfc_reset_crq(), and ibmvfc_reenable_crq_queue()).  If the NVMe
channel registration fails, do_enquiry is cleared even though SCSI
channels registered successfully, preventing the IBMVFC_CHANNEL_ENQUIRY
MAD from being sent and silently disabling multiqueue support for SCSI.

ibmvfc_init_sub_crqs() already handles NVMe *allocation* failure
correctly — it only clears nvme_enabled without touching do_enquiry.
The same logic needs to apply when NVMe *registration* fails.

Fix ibmvfc_reg_sub_crqs() to check channels->protocol in the failure
path: clear do_enquiry only for a SCSI channel failure (preserving
existing behaviour), and clear nvme_enabled instead for an NVMe channel
failure.  This covers all three call sites in one place.

Fixes: bf86ac3ca0d2 ("scsi: ibmvfc: alloc/dealloc sub-queues for nvme channels")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 93189692191e..d79a6ae278a2 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6527,7 +6527,10 @@ static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost,
 		if (ibmvfc_register_channel(vhost, channels, i)) {
 			for (j = i; j > 0; j--)
 				ibmvfc_deregister_channel(vhost, channels, j - 1);
-			vhost->do_enquiry = 0;
+			if (channels->protocol == IBMVFC_PROTO_SCSI)
+				vhost->do_enquiry = 0;
+			else
+				vhost->nvme_enabled = 0;
 			return;
 		}
 	}
-- 
2.55.0


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

* [PATCH 19/20] scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race dropping targets
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (17 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 18/20] scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI multiqueue Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  2026-09-16 23:09 ` [PATCH 20/20] scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe queue sizing Tyrel Datwyler
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

ibmvfc_discover_targets() sends both SCSI and NVMe discover-targets MADs
concurrently.  ibmvfc_discover_targets_done() unconditionally set
IBMVFC_HOST_ACTION_ALLOC_TGTS and woke the work thread on the first
completion to arrive.  ibmvfc_alloc_targets() then ran immediately,
reading both channels' disc_buf and num_targets fields.  If the second
MAD had not yet completed, the slower protocol's num_targets was still
zero from the previous discovery cycle, and all targets for that protocol
were silently dropped.  The second completion then attempted to transition
to ALLOC_TGTS again, but the state machine had already advanced, so the
transition was a no-op and ibmvfc_alloc_targets() was never re-run.

Fix this by adding a pending_disc:2 counter to ibmvfc_host.
ibmvfc_discover_targets() sets it to 1 before sending the SCSI MAD and
increments it to 2 before sending the NVMe MAD (skipped when NVMe is not
active, leaving the counter at 1).  ibmvfc_discover_targets_done()
decrements the counter on success and only calls
ibmvfc_set_host_action(ALLOC_TGTS) when it reaches zero, ensuring both
discovery buffers are fully written before target allocation starts.
Error paths already call ibmvfc_retry_host_init() or ibmvfc_link_down()
which reset the state machine, so they do not need to interact with the
counter.

Fixes: bffd16f59061 ("scsi: ibmvfc: add logic for protocol specific fabric logins")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 6 +++++-
 drivers/scsi/ibmvscsi/ibmvfc.h      | 1 +
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index d79a6ae278a2..b0d9000b4a0d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5156,7 +5156,8 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
 					      max_targets);
 		ibmvfc_dbg(vhost, "%d %s targets found\n", channels->num_targets,
 			   proto_type[channels->protocol]);
-		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
+		if (!--vhost->pending_disc)
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
 		break;
 	case IBMVFC_MAD_FAILED:
 		level += ibmvfc_retry_host_init(vhost);
@@ -5220,6 +5221,7 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
 	}
 
 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+	vhost->pending_disc = 1;
 
 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
 		ibmvfc_dbg(vhost, "Sent discover SCSI targets\n");
@@ -5236,6 +5238,8 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
 		return;
 	}
 
+	vhost->pending_disc++;
+
 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
 		ibmvfc_dbg(vhost, "Sent discover NVMe targets\n");
 	else
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index df0775183d72..a984b81f1d23 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1006,6 +1006,7 @@ struct ibmvfc_host {
 	unsigned int do_scsi_login:1;
 	unsigned int do_nvme_login:1;
 	unsigned int do_nvme_register:1;
+	unsigned int pending_disc:2;
 	unsigned int aborting_passthru:1;
 	unsigned int scan_complete:1;
 	int scan_timeout;
-- 
2.55.0


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

* [PATCH 20/20] scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe queue sizing
  2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
                   ` (18 preceding siblings ...)
  2026-09-16 23:09 ` [PATCH 19/20] scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race dropping targets Tyrel Datwyler
@ 2026-09-16 23:09 ` Tyrel Datwyler
  19 siblings, 0 replies; 21+ messages in thread
From: Tyrel Datwyler @ 2026-09-16 23:09 UTC (permalink / raw)
  To: james.bottomley, martin.petersen
  Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq, Tyrel Datwyler

max_nvme_queues was computed as min(IBMVFC_MAX_NVME_QUEUES, online_cpus),
silently ignoring the nr_nvme_hw_queues module parameter.  The SCSI path
correctly caps shost->nr_hw_queues with nr_scsi_hw_queues, but the NVMe
equivalent was never applied, making the 'nvme_host_queues' parameter a
no-op.

Apply the same pattern as the SCSI side: incorporate nr_nvme_hw_queues
into the max_nvme_queues calculation at probe time so that
nvme_scrqs.max_queues and nvme_scrqs.desired_queues both respect the
administrator-provided limit.

Fixes: 99d71f6b2c78 ("scsi: ibmvfc: initialize NVMe channel configuration during driver probe")
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index b0d9000b4a0d..df7976918f8f 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6866,7 +6866,9 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
 	int rc = -ENOMEM;
 	unsigned int online_cpus = num_online_cpus();
 	unsigned int max_scsi_queues = min_t(unsigned int, IBMVFC_MAX_SCSI_QUEUES, online_cpus);
-	unsigned int max_nvme_queues = min_t(unsigned int, IBMVFC_MAX_NVME_QUEUES, online_cpus);
+	unsigned int max_nvme_queues = min_t(unsigned int,
+					     min(IBMVFC_MAX_NVME_QUEUES, nr_nvme_hw_queues),
+					     online_cpus);
 
 	ENTER;
 	shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
-- 
2.55.0


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

end of thread, other threads:[~2026-09-16 23:09 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-16 23:09 [PATCH 00/20] scsi: ibmvfc: Fixes and cleanup for NVMe/FC support Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 01/20] scsi: ibmvfc: initialize evt->tgt for NVMe FCP commands Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 02/20] scsi: ibmvfc: fix trace logging " Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 03/20] scsi: ibmvfc: complete NVMe FCP requests on H_CLOSED send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 04/20] scsi: ibmvfc: defer NVMe local port registration out of atomic context Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 05/20] scsi: ibmvfc: fix uninitialized _done dereference for TMF events on send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 06/20] scsi: ibmvfc: fix uninitialized shwqs in ibmvfc_purge_requests() Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 07/20] scsi: ibmvfc: fix uninitialized status logged on LS abort send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 08/20] scsi: ibmvfc: fix inverted suppress-ABTS capability check in NVMe TMF path Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 09/20] scsi: ibmvfc: fix infinite reset loop on NULL evt in implicit logout path Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 10/20] scsi: ibmvfc: fix u16 overflow of max_cmds in ibmvfc_set_login_info() Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 11/20] scsi: ibmvfc: fix UAF and hang in ibmvfc_cancel_all_mq() on send failure Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 12/20] scsi: ibmvfc: fix data race on tgt->nvme_remote_port Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 13/20] scsi: ibmvfc: make NVMe FCP abort callback asynchronous Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 14/20] scsi: ibmvfc: fix UAF and stall in NVMe LS abort callback Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 15/20] scsi: ibmvfc: unregister NVMe local port on adapter removal Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 16/20] scsi: ibmvfc: fix NVMe local port leak on fabric link bounce Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 17/20] scsi: ibmvfc: fix TOCTOU race in ibmvfc_nvme_create_queue() on adapter removal Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 18/20] scsi: ibmvfc: fix NVMe sub-queue registration failure disabling SCSI multiqueue Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 19/20] scsi: ibmvfc: fix concurrent SCSI and NVMe discover-targets race dropping targets Tyrel Datwyler
2026-09-16 23:09 ` [PATCH 20/20] scsi: ibmvfc: fix nr_nvme_hw_queues module parameter ignored for NVMe queue sizing Tyrel Datwyler

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

all inboxes | Powered by JetHome®