* [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC
@ 2026-07-23 0:01 Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure Tyrel Datwyler
` (32 more replies)
0 siblings, 33 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
This series adds NVMe/FC initiator support to the ibmvfc driver, enabling
IBM POWER virtual machines to discover and use NVMe namespaces presented by
the IBM Virtual I/O Server (VIOS) over the existing NPIV transport.
The ibmvfc driver communicates with the VIOS via a CRQ-based protocol. With
this series the VIOS can present both SCSI/FCP and NVMe/FC targets through
parallel sets of protocol-specific MAD opcodes, fabric login flows, and
sub-CRQ channels.
The series is organized into three phases:
Patches 1-5: Bug fixes and preparatory refactoring
Four pre-existing bugs are fixed before any NVMe/FC work is introduced:
a deadlock in the MAD send-failure path (locked done variant called with
host_lock already held), a race during driver teardown where
rport_add_work_q work items can outlive the FC host, a NULL event
dereference in ibmvfc_tgt_implicit_logout_and_del, and an allocator
mismatch where mempool-allocated ibmvfc_target structs are freed via kfree
rather than mempool_free. Patch 5 moves the target list and count from
struct ibmvfc_host into struct ibmvfc_channels as the structural
prerequisite for independent per-protocol target tracking.
Patches 6-27: Protocol interface and driver scaffolding
Patch 6 extends ibmvfc.h with NVMe/FC protocol definitions: MAD opcodes,
capability flags, the v3 command layout, the fabric login MAD, async
sub-CRQ event format, and updated channel enquiry/setup fields. Patch 7
splits ibmvfc.c into ibmvfc-core.c and the new ibmvfc-nvme.c/h, registers
an nvme_fc_port_template with stub callbacks, and adds NVMe module
parameters. The remaining patches build the NVMe/FC plumbing: NVMe
channel-group initialization, sub-CRQ lifecycle management, protocol-
specific fabric login flow, target discovery, PLOGI/PRLI/query-target,
implicit logout, move-login, protocol-driven target allocation, NVMe
target deletion, state machine updates, and local/remote port registration
with the NVMe-FC transport layer.
Patches 28-33: NVMe-FC LLDD callbacks and I/O path
Implements the full nvme_fc_port_template: create_queue/delete_queue map
NVMe controller queues to sub-CRQ handles; ls_req submits FC-LS frames via
the ibmvfc passthru MAD; fcp_io builds and submits NVMe FCP commands via
the NVMe sub-CRQ and completes them via nvme_fc_rcv_fcp_rsp(); ls_abort
and fcp_abort cancel outstanding requests via NVMF cancel MADs. Patch 33
extends the purge path to fail outstanding NVMe FCP and LS requests during
host reset and link-down events.
Changes since v1 (29 patches -> 33 patches)
--------------------------------------------
- Added four bug-fix patches (1-4) ahead of any NVMe/FC changes to ensure
the series is bisect-safe. These address issues identified in the v1
review: the mempool_free/kfree mismatch (previously flagged on multiple
patches), the rport_add_work_q teardown race, the NULL event dereference
in the implicit logout and delete path, and the MAD send-failure deadlock
due to the locked done handler being invoked with host_lock already held.
- Fixed the NULL pointer dereference in ibmvfc_nvme_unregister() and
ibmvfc_nvme_unregister_remoteport() by adding proper NULL guards before
dereferencing the local and remote port pointers (v1 patch 19/20).
- Fixed self-deadlock in ibmvfc_send_event() error path for MAD commands:
on send failure the driver now falls back to the unlocked evt->_done
handler instead of the locked variant (patch 1 in v2).
- Fixed NVMe targets being entirely ignored by the TGT_DEL and TGT_DEL_FAILED
work-loop actions; both the SCSI and NVMe target lists are now walked in
the relevant state machine helpers (v1 patches 18/21).
- Fixed the purge path omission: NVMe FCP and LS requests are now properly
failed during host reset (patch 33, expanded from v1 patch 29 which only
handled the transport reset case).
Tyrel Datwyler (33):
ibmvfc: don't call locked done variant for MADs on send failure
ibmvfc: flush rport_add_work_q during driver teardown
ibmvfc: check for NULL evt in implicit LOGO and target delete path
ibmvfc: free ibmvfc_target allocations with mempool_free
ibmvfc: move target list from host to protocol specific channel groups
ibmvfc: add NVMe/FC protocol interface definitions
ibmvfc: split NVMe support into separate source file and add transport stubs
ibmvfc: initialize NVMe channel configuration during probe
ibmvfc: alloc/dealloc sub-queues for nvme channels
ibmvfc: add logic for protocol specific fabric login
ibmvfc: add wrapper to get vhost associated with a channel group
ibmvfc: add helper for creating protocol specific discover target events
ibmvfc: add helper to check NVMe/FC support with active queues
ibmvfc: allocate and free NVMe channel group discovery buffers
ibmvfc: send NVMe target discovery MAD
ibmvfc: add NVMe/FC Implicit Logout and Move Login support
ibmvfc: add NVMe/FC Port Login support
ibmvfc: add NVMe/FC Process Login support
ibmvfc: add NVMe/FC Query Target support
ibmvfc: allocate targets based on protocol
ibmvfc: delete NVMe/FC targets as well as SCSI
ibmvfc: update state machine to process NVMe/FC targets
ibmvfc: implement NVMe/FC stubs for local/remote port registration
ibmvfc: register local nvme fc port after fabric login
ibmvfc: process NVMe/FC rports in work thread
ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.c
ibmvfc: declare global function definitions
ibmvfc: implement LLDD callbacks for mapping nvme-fc queues
ibmvfc: implement nvme-fc LS submission transport callback
ibmvfc: implement nvme-fc IO command submission callback
ibmvfc: implement nvme-fc LS abort handling callback
ibmvfc: implement nvme-fc FCP abort callback
ibmvfc: fail nvme-fc fcp-io and ls requests during host reset
drivers/scsi/ibmvscsi/Makefile | 2 +
drivers/scsi/ibmvscsi/{ibmvfc.c => ibmvfc-core.c} | ...
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | ...
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | ...
drivers/scsi/ibmvscsi/ibmvfc.h | ...
5 files changed
--
2.55.0
^ permalink raw reply [flat|nested] 62+ messages in thread
* [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:42 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown Tyrel Datwyler
` (31 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
MADs are processed with the requirement that the host_lock is held for
MAD sends and completions. When a MAD job step is called the host lock
is already held and there is an unlikely scenario where h_send_crq fails
with something other than H_CLOSED. If this happens there is a special
done handler for MADs that tries to take the host lock.
In this failure scenario fall back to the saved evt->_done unlocked
completion handler.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 3dd2adda195e..0a5f73df8d7d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1774,8 +1774,10 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
if (evt->cmnd) {
evt->cmnd->result = DID_ERROR << 16;
evt->done = ibmvfc_scsi_eh_done;
- } else
+ } else {
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
+ evt->done = evt->_done;
+ }
evt->done(evt);
} else {
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:35 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path Tyrel Datwyler
` (30 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
INIT_WORK is called on rport_add_work_q during driver probe. When the
adapter is removed, ibmvfc_remove() stops the work thread and proceeds
to tear down the FC host, but any work item queued via schedule_work()
that raced the teardown could still be running or pending in the
system workqueue.
Add a flush_work() call after kthread_stop() and before fc_remove_host()
to drain any outstanding rport add work before the FC host and its rports
are torn down.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 0a5f73df8d7d..72317d7840a5 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -6443,6 +6443,7 @@ static void ibmvfc_remove(struct vio_dev *vdev)
ibmvfc_wait_while_resetting(vhost);
kthread_stop(vhost->work_thread);
+ flush_work(&vhost->rport_add_work_q);
fc_remove_host(vhost->host);
scsi_remove_host(vhost->host);
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:30 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 04/33] ibmvfc: free ibmvfc_target allocations with mempool_free Tyrel Datwyler
` (29 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Unlike ibmvfc_tgt_implicit_logout the ibmvfc_tgt_implicit_logout_and_del
path fails to check for a NULL evt returned from
ibmvfc_get_reserved_event and passes that directly to ibmvfc_send_event
which will unconditionally derefence the evt.
Check for a NULL evt and perform same escalation as
ibmvfc_implicit_logout.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 72317d7840a5..59102dd1c787 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -4406,6 +4406,14 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
ibmvfc_tgt_implicit_logout_and_del_done);
+ if (!evt) {
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
+
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
vhost->discovery_threads--;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 04/33] ibmvfc: free ibmvfc_target allocations with mempool_free
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (2 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups Tyrel Datwyler
` (28 subsequent siblings)
32 siblings, 0 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
The ibmvfc_target *tgt structures for discovery are allocated using
mempool_alloc but in turn free'd via kfree in ibmvfc_release_target
breaking pool guarentees.
Fixup ibmvfc_release_target to call mempool_free instead so that memory
is properly returned to the mempool instead of the general allocator.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 59102dd1c787..412e946abcb7 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1366,7 +1366,7 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
static void ibmvfc_release_tgt(struct kref *kref)
{
struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
- kfree(tgt);
+ mempool_free(tgt, tgt->vhost->tgt_pool);
}
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (3 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 04/33] ibmvfc: free ibmvfc_target allocations with mempool_free Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:34 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions Tyrel Datwyler
` (27 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Prepare the driver for protocol-specific target management by moving
the target list and target count out of struct ibmvfc_host and into
struct ibmvfc_channels.
Today the driver only maintains a single SCSI target list, but NVMe/FC
support will require separate target tracking for each protocol-specific
channel group. Update the existing target iteration, allocation, and
discovery paths to use the SCSI channel group's target list instead of a
host-wide list.
This is a preparatory refactoring only. No functional change is intended
for existing SCSI operation.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 52 +++++++++++++++++-----------------
drivers/scsi/ibmvscsi/ibmvfc.h | 4 +--
2 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 412e946abcb7..78b4ac201a49 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -682,7 +682,7 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
ENTER;
scsi_block_requests(vhost->host);
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
ibmvfc_del_tgt(tgt);
ibmvfc_set_host_state(vhost, state);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
@@ -715,7 +715,7 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
vhost->async_crq.cur = 0;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (vhost->client_migrated)
tgt->need_login = 1;
else
@@ -1232,7 +1232,7 @@ static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
struct ibmvfc_host *vhost = shost_priv(shost);
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->target_id == starget->id) {
kref_get(&tgt->kref);
return tgt;
@@ -1834,7 +1834,7 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
unsigned long flags;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (rport == tgt->rport) {
ibmvfc_del_tgt(tgt);
break;
@@ -2132,7 +2132,7 @@ static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
ENTER;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->scsi_id == port_id) {
issue_login = 0;
break;
@@ -3104,7 +3104,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
spin_lock_irqsave(shost->host_lock, flags);
found = 0;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->scsi_id == rport->port_id) {
found++;
break;
@@ -3244,7 +3244,7 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
case IBMVFC_AE_ELS_PLOGI:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
break;
if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
@@ -4873,14 +4873,14 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
/* Look to see if we already have a target allocated for this SCSI ID or WWPN */
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->wwpn == wwpn) {
wtgt = tgt;
break;
}
}
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->scsi_id == scsi_id) {
stgt = tgt;
break;
@@ -4937,7 +4937,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
spin_lock_irqsave(vhost->host->host_lock, flags);
tgt->cancel_key = vhost->task_set++;
- list_add_tail(&tgt->queue, &vhost->targets);
+ list_add_tail(&tgt->queue, &vhost->scsi_scrqs.targets);
unlock_out:
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4955,7 +4955,7 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
{
int i, rc;
- for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
+ for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
return rc;
@@ -4976,8 +4976,8 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
- vhost->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
- max_targets);
+ vhost->scsi_scrqs.num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
+ max_targets);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
break;
case IBMVFC_MAD_FAILED:
@@ -5393,7 +5393,7 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 1;
@@ -5413,7 +5413,7 @@ static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 1;
@@ -5443,10 +5443,10 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_QUERY_TGTS:
if (vhost->discovery_threads == disc_threads)
return 0;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT)
return 1;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
return 1;
@@ -5454,10 +5454,10 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
if (vhost->discovery_threads == disc_threads)
return 0;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
return 1;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 0;
return 1;
@@ -5645,12 +5645,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
vhost->job_step(vhost);
break;
case IBMVFC_HOST_ACTION_QUERY:
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
break;
case IBMVFC_HOST_ACTION_QUERY_TGTS:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
tgt->job_step(tgt);
break;
@@ -5662,7 +5662,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
tgt->job_step(tgt);
break;
@@ -5674,7 +5674,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
return;
}
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
rport = tgt->rport;
@@ -5749,7 +5749,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_lock_irqsave(vhost->host->host_lock, flags);
break;
case IBMVFC_HOST_ACTION_TGT_INIT:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
tgt->job_step(tgt);
break;
@@ -6286,7 +6286,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
if (vhost->state != IBMVFC_ACTIVE)
break;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->add_rport) {
did_work = 1;
tgt->add_rport = 0;
@@ -6351,7 +6351,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1;
vhost = shost_priv(shost);
- INIT_LIST_HEAD(&vhost->targets);
+ INIT_LIST_HEAD(&vhost->scsi_scrqs.targets);
INIT_LIST_HEAD(&vhost->purge);
sprintf(vhost->name, IBMVFC_NAME);
vhost->host = shost;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index c73ed2314ad0..0e259e9d2e9b 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -828,6 +828,8 @@ struct ibmvfc_channels {
unsigned int active_queues;
unsigned int desired_queues;
unsigned int max_queues;
+ int num_targets;
+ struct list_head targets;
int disc_buf_sz;
struct ibmvfc_discover_targets_entry *disc_buf;
dma_addr_t disc_buf_dma;
@@ -871,8 +873,6 @@ struct ibmvfc_host {
#define IBMVFC_TRACE_SIZE (sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
struct ibmvfc_trace_entry *trace;
atomic_t trace_index;
- int num_targets;
- struct list_head targets;
struct list_head purge;
struct device *dev;
struct dma_pool *sg_pool;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (4 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:28 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs Tyrel Datwyler
` (26 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add the protocol definitions for client-VIOS interface updates needed to
support NVMe/FC over the ibmvfc NPIV transport.
Extend the ibmvfc interface with:
- NVMe/FC-specific capability bits and opcodes
- protocol-specific channel and queue definitions
- updated channel enquiry/setup fields for NVMe queues
- v3 command layout support for protocol-specific payloads
These changes provide the common header and interface plumbing needed by
later patches that add NVMe/FC login, discovery, remote-port handling,
and I/O submission.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.h | 153 +++++++++++++++++++++++++--------
1 file changed, 119 insertions(+), 34 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 0e259e9d2e9b..f8a2bf92da41 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -13,6 +13,8 @@
#include <linux/list.h>
#include <linux/types.h>
#include <scsi/viosrp.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
#define IBMVFC_NAME "ibmvfc"
#define IBMVFC_DRIVER_VERSION "1.0.11"
@@ -92,6 +94,7 @@ enum ibmvfc_cmd_status_flags {
IBMVFC_FC_SCSI_ERROR = 0x0008,
IBMVFC_HW_EVENT_LOGGED = 0x0010,
IBMVFC_VIOS_LOGGED = 0x0020,
+ IBMVFC_FC_NVME_STATUS = 0x0040,
};
enum ibmvfc_fabric_mapped_errors {
@@ -124,20 +127,37 @@ enum ibmvfc_vios_errors {
IBMVFC_COMMAND_FAILED = 0x8000,
};
+enum ibmvfc_fc_nvme_errors {
+ IBMVFC_NVMS_VALID_ERSP = 0x0001,
+ IBMVFC_NVMS_VALID_NODMA_CQE = 0x0002,
+};
+
enum ibmvfc_mad_types {
IBMVFC_NPIV_LOGIN = 0x0001,
- IBMVFC_DISC_TARGETS = 0x0002,
+ IBMVFC_DISC_TARGETS = 0x0002,
+ IBMVFC_DISC_NVMF_TARGETS = 0x0003,
IBMVFC_PORT_LOGIN = 0x0004,
- IBMVFC_PROCESS_LOGIN = 0x0008,
- IBMVFC_QUERY_TARGET = 0x0010,
+ IBMVFC_NVMF_PORT_LOGIN = 0x0005,
+ IBMVFC_PROCESS_LOGIN = 0x0008,
+ IBMVFC_NVMF_PROCESS_LOGIN = 0x0009,
+ IBMVFC_QUERY_TARGET = 0x0010,
+ IBMVFC_NVMF_QUERY_TARGET = 0x0011,
IBMVFC_MOVE_LOGIN = 0x0020,
- IBMVFC_IMPLICIT_LOGOUT = 0x0040,
- IBMVFC_PASSTHRU = 0x0200,
- IBMVFC_TMF_MAD = 0x0100,
- IBMVFC_NPIV_LOGOUT = 0x0800,
- IBMVFC_CHANNEL_ENQUIRY = 0x1000,
- IBMVFC_CHANNEL_SETUP = 0x2000,
- IBMVFC_CONNECTION_INFO = 0x4000,
+ IBMVFC_NVMF_MOVE_LOGIN = 0x0021,
+ IBMVFC_IMPLICIT_LOGOUT = 0x0040,
+ IBMVFC_NVMF_IMPLICIT_LOGOUT = 0x0041,
+ IBMVFC_RNID = 0x0080,
+ IBMVFC_NVMF_RNID = 0x0081,
+ IBMVFC_TMF_MAD = 0x0100,
+ IBMVFC_NVMF_TMF_MAD = 0x0101,
+ IBMVFC_PASSTHRU = 0x0200,
+ IBMVFC_NVMF_PASSTHRU = 0x0201,
+ IBMVFC_NPIV_LOGOUT = 0x0800,
+ IBMVFC_CHANNEL_ENQUIRY = 0x1000,
+ IBMVFC_CHANNEL_SETUP = 0x2000,
+ IBMVFC_CONNECTION_INFO = 0x4000,
+ IBMVFC_FABRIC_LOGIN = 0x8000,
+ IBMVFC_NVMF_FABRIC_LOGIN = 0x8001,
};
struct ibmvfc_mad_common {
@@ -175,11 +195,16 @@ struct ibmvfc_npiv_login {
#define IBMVFC_FLUSH_ON_HALT 0x02
__be32 max_cmds;
__be64 capabilities;
-#define IBMVFC_CAN_MIGRATE 0x01
-#define IBMVFC_CAN_USE_CHANNELS 0x02
-#define IBMVFC_CAN_HANDLE_FPIN 0x04
-#define IBMVFC_CAN_USE_MAD_VERSION 0x08
-#define IBMVFC_CAN_SEND_VF_WWPN 0x10
+#define IBMVFC_CAN_MIGRATE 0x001
+#define IBMVFC_CAN_USE_CHANNELS 0x002
+#define IBMVFC_CAN_HANDLE_FPIN 0x004
+#define IBMVFC_CAN_USE_MAD_VERSION 0x008
+#define IBMVFC_CAN_SEND_VF_WWPN 0x010
+#define IBMVFC_YES_NVMEOF 0x020
+#define IBMVFC_YES_SCSI 0x040
+#define IBMVFC_CAN_USE_WWPN_ALL 0x080
+#define IBMVFC_USE_ASYNC_SUBQ 0x100
+#define IBMVFC_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
@@ -219,13 +244,18 @@ struct ibmvfc_npiv_login_resp {
__be16 error;
__be32 flags;
#define IBMVFC_NATIVE_FC 0x01
- __be32 reserved;
+ __be32 possible_nports;
__be64 capabilities;
-#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
-#define IBMVFC_CAN_SUPPRESS_ABTS 0x10
-#define IBMVFC_MAD_VERSION_CAP 0x20
-#define IBMVFC_HANDLE_VF_WWPN 0x40
-#define IBMVFC_CAN_SUPPORT_CHANNELS 0x80
+#define IBMVFC_CAN_FLUSH_ON_HALT 0x0008
+#define IBMVFC_CAN_SUPPRESS_ABTS 0x0010
+#define IBMVFC_MAD_VERSION_CAP 0x0020
+#define IBMVFC_HANDLE_VF_WWPN 0x0040
+#define IBMVFC_CAN_SUPPORT_CHANNELS 0x0080
+#define IBMVFC_SUPPORT_NVMEOF 0x0100
+#define IBMVFC_SUPPORT_SCSI 0x0200
+#define IBMVFC_SUPPORT_WWPN_ALL 0x0400
+#define IBMVFC_ASYNC_SUBQ 0x0800
+#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
__be64 max_dma_len;
@@ -238,7 +268,7 @@ struct ibmvfc_npiv_login_resp {
u8 port_loc_code[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
struct ibmvfc_service_parms service_parms;
- __be64 reserved2;
+ __be64 reserved;
} __packed __aligned(8);
union ibmvfc_npiv_login_data {
@@ -246,6 +276,17 @@ union ibmvfc_npiv_login_data {
struct ibmvfc_npiv_login_resp resp;
} __packed __aligned(8);
+struct ibmvfc_fabric_login_mad {
+ struct ibmvfc_mad_common common;
+ __be64 flags;
+ __be64 capabilities;
+ __be64 nport_id;
+ __be16 status;
+ __be16 error;
+ __be32 reserved;
+ __be64 reserved2[16];
+} __packed __aligned(8);
+
struct ibmvfc_discover_targets_entry {
__be32 scsi_id;
__be32 pad;
@@ -287,6 +328,7 @@ enum ibmvfc_fc_type {
IBMVFC_FABRIC_BUSY = 0x04,
IBMVFC_PORT_BUSY = 0x05,
IBMVFC_BASIC_REJECT = 0x06,
+ IBMVFC_FC4_LS_REJECT = 0x07,
};
enum ibmvfc_gs_explain {
@@ -377,20 +419,27 @@ struct ibmvfc_query_tgt {
struct ibmvfc_implicit_logout {
struct ibmvfc_mad_common common;
__be64 old_scsi_id;
- __be64 reserved[2];
+ __be64 reserved[8];
+ __be64 target_wwpn;
} __packed __aligned(8);
struct ibmvfc_tmf {
struct ibmvfc_mad_common common;
__be64 scsi_id;
- struct scsi_lun lun;
+ union {
+ struct scsi_lun lun;
+ __be64 assoc_id;
+ };
__be32 flags;
-#define IBMVFC_TMF_ABORT_TASK 0x02
-#define IBMVFC_TMF_ABORT_TASK_SET 0x04
-#define IBMVFC_TMF_LUN_RESET 0x10
-#define IBMVFC_TMF_TGT_RESET 0x20
-#define IBMVFC_TMF_LUA_VALID 0x40
-#define IBMVFC_TMF_SUPPRESS_ABTS 0x80
+#define IBMVFC_TMF_ABORT_TASK 0x002
+#define IBMVFC_TMF_ABORT_TASK_SET 0x004
+#define IBMVFC_TMF_LUN_RESET 0x010
+#define IBMVFC_TMF_TGT_RESET 0x020
+#define IBMVFC_TMF_LUA_VALID 0x040
+#define IBMVFC_TMF_SUPPRESS_ABTS 0x080
+#define IBMVFC_TMF_NVMF_ASSOC 0x100
+#define IBMVFC_TMF_NVMF_TARGET 0x200
+#define IBMVFC_TMF_BITMASK_VALID 0x400
__be32 cancel_key;
__be32 my_cancel_key;
__be32 pad;
@@ -446,6 +495,8 @@ enum ibmvfc_cmd_flags {
IBMVFC_WRITE = 0x0008,
IBMVFC_TMF = 0x0080,
IBMVFC_CLASS_3_ERR = 0x0100,
+ IBMVFC_NVMEOF_PROTOCOL = 0x0200,
+ IBMVFC_NVMF_SLER = 0x0400,
};
enum ibmvfc_fc_task_attr {
@@ -493,7 +544,7 @@ struct ibmvfc_cmd {
__be64 tgt_scsi_id;
__be64 tag;
__be64 target_wwpn;
- __be64 reserved3;
+ __be64 assoc_id;
union {
struct {
struct ibmvfc_fcp_cmd_iu iu;
@@ -504,6 +555,15 @@ struct ibmvfc_cmd {
struct ibmvfc_fcp_cmd_iu iu;
struct ibmvfc_fcp_rsp rsp;
} v2;
+ struct {
+ __be64 reserved5[4];
+ struct ibmvfc_fcp_cmd_iu iu;
+ struct ibmvfc_fcp_rsp rsp;
+ } v3scsi;
+ struct {
+ __be64 reserved[4];
+ struct nvme_fc_cmd_iu iu;
+ } v3nvme;
};
} __packed __aligned(8);
@@ -522,6 +582,9 @@ struct ibmvfc_passthru_iu {
__be32 flags;
#define IBMVFC_FC_ELS 0x01
#define IBMVFC_FC_CT_IU 0x02
+#define IBMVFC_PT_PRLI 0x04
+#define IBMVFC_FC4_LS_OTH 0x08
+#define IBMVFC_FC4_LS_DSC_CTRL 0x10
__be32 cancel_key;
#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
@@ -559,7 +622,7 @@ struct ibmvfc_channel_setup_mad {
struct srp_direct_buf buffer;
} __packed __aligned(8);
-#define IBMVFC_MAX_CHANNELS 502
+#define IBMVFC_MAX_CHANNELS 501
struct ibmvfc_channel_setup {
__be32 flags;
@@ -574,6 +637,7 @@ struct ibmvfc_channel_setup {
struct srp_direct_buf buffer;
__be64 reserved2[5];
__be64 channel_handles[IBMVFC_MAX_CHANNELS];
+ __be64 async_sub_crq_handle;
} __packed __aligned(8);
struct ibmvfc_connection_info {
@@ -620,7 +684,8 @@ struct ibmvfc_trace_entry {
enum ibmvfc_crq_formats {
IBMVFC_CMD_FORMAT = 0x01,
- IBMVFC_ASYNC_EVENT = 0x02,
+ IBMVFC_ASYNC_EVENT = 0x02,
+ IBMVFC_NOOP = 0x03,
IBMVFC_MAD_FORMAT = 0x04,
};
@@ -639,6 +704,9 @@ enum ibmvfc_async_event {
IBMVFC_AE_RESUME = 0x0800,
IBMVFC_AE_ADAPTER_FAILED = 0x1000,
IBMVFC_AE_FPIN = 0x2000,
+ IBMVFC_NVME_DISCONNECT = 0x4000,
+ IBMVFC_NVMEOF_PROTO_AVAIL = 0x40000000,
+ IBMVFC_SCSI_PROTO_AVAIL = 0x80000000,
};
struct ibmvfc_async_desc {
@@ -683,13 +751,30 @@ struct ibmvfc_async_crq {
volatile __be64 scsi_id;
volatile __be64 wwpn;
volatile __be64 node_name;
- __be64 reserved;
+ __be64 assoc_id;
+} __packed __aligned(8);
+
+struct ibmvfc_async_sub_crq {
+ volatile u8 valid;
+ u8 flags;
+#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
+ u8 link_state;
+ u8 fpin_status;
+ __be16 event;
+ __be16 pad;
+ __be64 wwpn;
+ __be64 nport_id;
+ union {
+ __be64 node_name;
+ __be64 assoc_id;
+ } id;
} __packed __aligned(8);
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
struct ibmvfc_npiv_logout_mad npiv_logout;
+ struct ibmvfc_fabric_login_mad fabric_login;
struct ibmvfc_discover_targets discover_targets;
struct ibmvfc_port_login plogi;
struct ibmvfc_process_login prli;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (5 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:22 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe Tyrel Datwyler
` (25 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Rename ibmvfc.c to ibmvfc-core.c as first step in decoupling each
protocol from the core driver logic. Add ibmvfc-nvme.[ch] files, and
register an nvme_fc_port_template with empty callback stubs.
Add empty registration functions definitions for local and remote ports.
No functional NVMe/FC support is added yet.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/Makefile | 2 +
.../scsi/ibmvscsi/{ibmvfc.c => ibmvfc-core.c} | 19 +++-
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 97 +++++++++++++++++++
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | 32 ++++++
drivers/scsi/ibmvscsi/ibmvfc.h | 10 +-
5 files changed, 156 insertions(+), 4 deletions(-)
rename drivers/scsi/ibmvscsi/{ibmvfc.c => ibmvfc-core.c} (99%)
create mode 100644 drivers/scsi/ibmvscsi/ibmvfc-nvme.c
create mode 100644 drivers/scsi/ibmvscsi/ibmvfc-nvme.h
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 5eb1cb1a0028..9408c7f4cdee 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o
+
obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o
obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
similarity index 99%
rename from drivers/scsi/ibmvscsi/ibmvfc.c
rename to drivers/scsi/ibmvscsi/ibmvfc-core.c
index 78b4ac201a49..56f4fb200a69 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1,10 +1,11 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
/*
* ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
*/
#include <linux/module.h>
@@ -46,6 +47,9 @@ static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
static unsigned int mq_enabled = IBMVFC_MQ;
static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES;
static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS;
+static unsigned int nvme_enabled = IBMVFC_NVME;
+static unsigned int nr_nvme_hw_queues = IBMVFC_NVME_HW_QUEUES;
+static unsigned int nr_nvme_channels = IBMVFC_NVME_CHANNELS;
static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
@@ -74,6 +78,16 @@ module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO);
MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. "
"[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]");
+module_param_named(nvme, nvme_enabled, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme, "Enable NVMe over FC support. "
+ "[Default=" __stringify(IBMVFC_NVME) "]");
+module_param_named(nvme_host_queues, nr_nvme_hw_queues, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_host_queues, "Number of NVMeoF Host submission queues. "
+ "[Default=" __stringify(IBMVFC_NVME_HW_QUEUES) "]");
+module_param_named(nvme_hw_channels, nr_nvme_channels, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_hw_channels, "Number of hw NVMeoF channels to request. "
+ "[Default=" __stringify(IBMVFC_NVME_CHANNELS) "]");
+
module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
"[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
@@ -468,6 +482,7 @@ static const struct {
{ IBMVFC_FABRIC_BUSY, "fabric busy" },
{ IBMVFC_PORT_BUSY, "port busy" },
{ IBMVFC_BASIC_REJECT, "basic reject" },
+ { IBMVFC_FC4_LS_REJECT, "fc4 ls reject" },
};
static const char *unknown_fc_type = "unknown fc type";
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
new file mode 100644
index 000000000000..2ee1c64665d9
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.c -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#include <scsi/scsi_transport_fc.h>
+
+#include "ibmvfc-nvme.h"
+
+static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
+{
+}
+
+static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
+{
+}
+
+static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_req)
+{
+ return 0;
+}
+
+static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_abort)
+{
+}
+
+static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *fcp_req)
+{
+ return 0;
+}
+
+static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *abort_req)
+{
+}
+
+static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
+ .localport_delete = ibmvfc_nvme_localport_delete,
+ .remoteport_delete = ibmvfc_nvme_remoteport_delete,
+ .create_queue = NULL,
+ .delete_queue = NULL,
+ .ls_req = ibmvfc_nvme_ls_req,
+ .ls_abort = ibmvfc_nvme_ls_abort,
+ .fcp_io = ibmvfc_nvme_fcp_io,
+ .fcp_abort = ibmvfc_nvme_fcp_abort,
+ .map_queues = NULL,
+ .max_hw_queues = IBMVFC_NVME_HW_QUEUES,
+ .max_sgl_segments = 1024,
+ .max_dif_sgl_segments = 64,
+ .dma_boundary = 0xFFFFFFFF,
+ .local_priv_sz = sizeof(struct ibmvfc_host *),
+ .remote_priv_sz = sizeof(struct ibmvfc_target *),
+ .lsrqst_priv_sz = sizeof(struct ibmvfc_event *),
+ .fcprqst_priv_sz = sizeof(struct ibmvfc_event *),
+};
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
+{
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return 0;
+
+ return 0;
+}
+
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt)
+{
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return;
+}
+
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
+{
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return 0;
+
+ return 0;
+}
+
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
+{
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return;
+}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
new file mode 100644
index 000000000000..97e267871df2
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.h -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#ifndef _IBMVFC_NVME_H
+#define _IBMVFC_NVME_H
+
+#include <uapi/scsi/fc/fc_fs.h>
+#include <uapi/scsi/fc/fc_els.h>
+#include <linux/nvme-fc-driver.h>
+
+#include "ibmvfc.h"
+
+#define IBMVFC_NVME 0
+#define IBMVFC_NVME_HW_QUEUES 8
+#define IBMVFC_MAX_NVME_QUEUES 16
+#define IBMVFC_NVME_CHANNELS 8
+
+struct ibmvfc_host;
+struct ibmvfc_target;
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt);
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt);
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost);
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost);
+#endif
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index f8a2bf92da41..f137b61ce422 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1,22 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+
/*
* ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
*/
#ifndef _IBMVFC_H
#define _IBMVFC_H
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/types.h>
+#include <scsi/scsi_device.h>
#include <scsi/viosrp.h>
#include <linux/nvme.h>
#include <linux/nvme-fc.h>
-#define IBMVFC_NAME "ibmvfc"
+#include "ibmvfc-nvme.h"
+
+#define IBMVFC_NAME "ibmvfc"
#define IBMVFC_DRIVER_VERSION "1.0.11"
#define IBMVFC_DRIVER_DATE "(April 12, 2013)"
@@ -984,6 +989,7 @@ struct ibmvfc_host {
unsigned int mq_enabled:1;
unsigned int using_channels:1;
unsigned int do_enquiry:1;
+ unsigned int nvme_enabled:1;
unsigned int aborting_passthru:1;
unsigned int scan_complete:1;
int scan_timeout;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (6 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:21 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels Tyrel Datwyler
` (24 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Initialize the host's NVMe channel-group state during probe.
Set up the NVMe channel list head, desired queue count, maximum queue
count, protocol identifier, and enablement state alongside the existing
SCSI channel-group initialization in ibmvfc_probe().
This prepares the driver with a NVMe/FC channel group that can will be
used by later patches for NVMe queue allocation, discovery buffers,
target management, and IO submission.
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 56f4fb200a69..5b6570232df3 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6347,7 +6347,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
struct device *dev = &vdev->dev;
int rc = -ENOMEM;
unsigned int online_cpus = num_online_cpus();
- unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, 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);
ENTER;
shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
@@ -6367,6 +6368,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vhost = shost_priv(shost);
INIT_LIST_HEAD(&vhost->scsi_scrqs.targets);
+ INIT_LIST_HEAD(&vhost->nvme_scrqs.targets);
INIT_LIST_HEAD(&vhost->purge);
sprintf(vhost->name, IBMVFC_NAME);
vhost->host = shost;
@@ -6381,6 +6383,10 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI;
vhost->using_channels = 0;
vhost->do_enquiry = 1;
+ vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0;
+ vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels);
+ vhost->nvme_scrqs.max_queues = max_nvme_queues;
+ vhost->nvme_scrqs.protocol = IBMVFC_PROTO_NVME;
vhost->scan_timeout = 0;
strcpy(vhost->partition_name, "UNKNOWN");
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index f137b61ce422..01c49d81b135 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -970,6 +970,7 @@ struct ibmvfc_host {
struct ibmvfc_queue crq;
struct ibmvfc_queue async_crq;
struct ibmvfc_channels scsi_scrqs;
+ struct ibmvfc_channels nvme_scrqs;
struct ibmvfc_npiv_login login_info;
union ibmvfc_npiv_login_data *login_buf;
dma_addr_t login_buf_dma;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (7 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:33 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins Tyrel Datwyler
` (23 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Allocate, register, deregister, and release NVMe subordinate CRQs
alongside the existing SCSI sub-CRQs.
Update the CRQ reset and re-enable paths to tear down and recreate NVMe
sub-queues, extend sub-CRQ initialization to allocate NVMe channels when
enabled, and release NVMe channel resources during adapter teardown.
This keeps the NVMe queue lifecycle aligned with the existing SCSI queue
lifecycle so both protocols are reset consistently across probe, remove,
and connection recovery.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 5b6570232df3..d8c2b56258ee 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -945,6 +945,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
unsigned long flags;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
/* Re-enable the CRQ */
do {
@@ -964,6 +965,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
return rc;
}
@@ -983,6 +985,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
struct ibmvfc_queue *crq = &vhost->crq;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
/* Close the CRQ */
do {
@@ -1016,6 +1019,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
return rc;
}
@@ -6119,6 +6123,13 @@ static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ if (vhost->nvme_enabled) {
+ if (ibmvfc_alloc_channels(vhost, &vhost->nvme_scrqs))
+ vhost->nvme_enabled = 0;
+ else
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
+ }
+
LEAVE;
}
@@ -6147,8 +6158,10 @@ static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
return;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
-
ibmvfc_release_channels(vhost, &vhost->scsi_scrqs);
+
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
+ ibmvfc_release_channels(vhost, &vhost->nvme_scrqs);
LEAVE;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (8 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:28 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 11/33] ibmvfc: add wrapper to get vhost associated with a channel struct Tyrel Datwyler
` (22 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add support for the protocol-specific fabric login flow introduced by
the updated client/VIOS interface.
After NPIV login, a VIOS that advertises protocol-specific support
requires separate fabric login MADs for SCSI and NVMe/FC. Track whether
SCSI and NVMe/FC fabric login are needed, extend channel enquiry/setup
handling to negotiate both SCSI and NVMe queue counts, and issue the
appropriate fabric login MADs before target discovery begins.
Also update command layout selection so the driver uses the v3 command
format when the VIOS advertises NVMe/FC-capable framing.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 230 ++++++++++++++++++++++++----
drivers/scsi/ibmvscsi/ibmvfc.h | 11 +-
2 files changed, 206 insertions(+), 35 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index d8c2b56258ee..e0edd71e9fd4 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -210,7 +210,9 @@ static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
struct ibmvfc_cmd *vfc_cmd)
{
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ return &vfc_cmd->v3scsi.iu;
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
return &vfc_cmd->v2.iu;
else
return &vfc_cmd->v1.iu;
@@ -219,7 +221,9 @@ static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost,
struct ibmvfc_cmd *vfc_cmd)
{
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ return &vfc_cmd->v3scsi.rsp;
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
return &vfc_cmd->v2.rsp;
else
return &vfc_cmd->v1.rsp;
@@ -961,6 +965,8 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
spin_lock(vhost->crq.q_lock);
vhost->do_enquiry = 1;
vhost->using_channels = 0;
+ vhost->do_scsi_login = 0;
+ vhost->do_nvme_login = 0;
spin_unlock(vhost->crq.q_lock);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -1000,6 +1006,8 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
vhost->logged_in = 0;
vhost->do_enquiry = 1;
vhost->using_channels = 0;
+ vhost->do_scsi_login = 0;
+ vhost->do_nvme_login = 0;
/* Clean out the queue */
memset(crq->msgs.crq, 0, PAGE_SIZE);
@@ -1512,7 +1520,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
if (mq_enabled)
max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
- vhost->scsi_scrqs.desired_queues;
+ (vhost->scsi_scrqs.desired_queues + vhost->nvme_scrqs.desired_queues);
memset(login_info, 0, sizeof(*login_info));
@@ -1530,8 +1538,14 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->max_cmds = cpu_to_be32(max_cmds);
login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
- if (vhost->mq_enabled || vhost->using_channels)
+ if (vhost->mq_enabled || vhost->using_channels) {
login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+ if (vhost->nvme_enabled) {
+ login_info->capabilities |= cpu_to_be64(IBMVFC_YES_NVMEOF);
+ login_info->capabilities |= cpu_to_be64(IBMVFC_YES_SCSI);
+ login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_WWPN_ALL);
+ }
+ }
login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
login_info->async.len = cpu_to_be32(async_crq->size *
@@ -1956,11 +1970,13 @@ static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct s
size_t offset;
memset(vfc_cmd, 0, sizeof(*vfc_cmd));
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ offset = offsetof(struct ibmvfc_cmd, v3scsi.rsp);
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
offset = offsetof(struct ibmvfc_cmd, v2.rsp);
- vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
- } else
+ else
offset = offsetof(struct ibmvfc_cmd, v1.rsp);
+ vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset);
vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp));
vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE);
@@ -5052,14 +5068,141 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
}
+static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
+ break;
+ case IBMVFC_MAD_FAILED:
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+ level += ibmvfc_retry_host_init(vhost);
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "NVMe fabric login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+ ibmvfc_free_event(evt);
+ return;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid NVMe fabric login response: 0x%x\n", mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ }
+
+ ibmvfc_free_event(evt);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+}
+
+static void ibmvfc_fabric_login_nvme(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_fabric_login_mad *mad;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt) {
+ ibmvfc_retry_host_init(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_fabric_login_nvme_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.fabric_login;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_FABRIC_LOGIN);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Send NVMe fabric login\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+static void ibmvfc_fabric_login_scsi_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ ibmvfc_dbg(vhost, "SCSI fabric login succeeded\n");
+ break;
+ case IBMVFC_MAD_FAILED:
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+ level += ibmvfc_retry_host_init(vhost);
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "SCSI fabric login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+ ibmvfc_free_event(evt);
+ return;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid SCSI fabric login response: 0x%x\n", mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ }
+
+ ibmvfc_free_event(evt);
+
+ if (vhost->do_nvme_login) {
+ ibmvfc_fabric_login_nvme(vhost);
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+ }
+}
+
+static void ibmvfc_fabric_login_scsi(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_fabric_login_mad *mad;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt) {
+ ibmvfc_retry_host_init(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_fabric_login_scsi_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.fabric_login;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Send SCSI fabric login\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf;
- struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
- int flags, active_queues, i;
+ int flags, i;
ibmvfc_free_event(evt);
@@ -5068,22 +5211,28 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
flags = be32_to_cpu(setup->flags);
vhost->do_enquiry = 0;
- active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
- scrqs->active_queues = active_queues;
+ scsi->active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
+ nvme->active_queues = be32_to_cpu(setup->num_nvme_subq_channels);
if (flags & IBMVFC_CHANNELS_CANCELED) {
ibmvfc_dbg(vhost, "Channels Canceled\n");
vhost->using_channels = 0;
- } else {
- if (active_queues)
- vhost->using_channels = 1;
- for (i = 0; i < active_queues; i++)
- scrqs->scrqs[i].vios_cookie =
- be64_to_cpu(setup->channel_handles[i]);
-
- ibmvfc_dbg(vhost, "Using %u channels\n",
- vhost->scsi_scrqs.active_queues);
+ break;
}
+
+ if (scsi->active_queues || nvme->active_queues)
+ vhost->using_channels = 1;
+ for (i = 0; i < scsi->active_queues; i++)
+ scsi->scrqs[i].vios_cookie =
+ be64_to_cpu(setup->channel_handles[i]);
+ for (i = 0; i < nvme->active_queues; i++)
+ nvme->scrqs[i].vios_cookie =
+ be64_to_cpu(setup->channel_handles[scsi->active_queues + i]);
+
+ ibmvfc_dbg(vhost, "Using %u SCSI channels\n",
+ scsi->active_queues);
+ ibmvfc_dbg(vhost, "Using %u NVMe channels\n",
+ nvme->active_queues);
break;
case IBMVFC_MAD_FAILED:
level += ibmvfc_retry_host_init(vhost);
@@ -5098,8 +5247,12 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
return;
}
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- wake_up(&vhost->work_wait_q);
+ if (vhost->do_scsi_login) {
+ ibmvfc_fabric_login_scsi(vhost);
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+ }
}
static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
@@ -5107,9 +5260,12 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
struct ibmvfc_channel_setup_mad *mad;
struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf;
struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
- struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
- unsigned int num_channels =
- min(scrqs->desired_queues, vhost->max_vios_scsi_channels);
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
+ unsigned int scsi_channels =
+ min(scsi->desired_queues, vhost->max_vios_scsi_channels);
+ unsigned int nvme_channels =
+ min(nvme->desired_queues, vhost->max_vios_nvme_channels);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
int i;
@@ -5120,12 +5276,17 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
}
memset(setup_buf, 0, sizeof(*setup_buf));
- if (num_channels == 0)
+ if (!scsi_channels && !nvme_channels)
setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
else {
- setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
- for (i = 0; i < num_channels; i++)
- setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
+ setup_buf->num_scsi_subq_channels = cpu_to_be32(scsi_channels);
+ setup_buf->num_nvme_subq_channels = cpu_to_be32(nvme_channels);
+ for (i = 0; i < scsi_channels; i++)
+ setup_buf->channel_handles[i] =
+ cpu_to_be64(scsi->scrqs[i].cookie);
+ for (i = 0; i < nvme_channels; i++)
+ setup_buf->channel_handles[scsi_channels + i] =
+ cpu_to_be64(nvme->scrqs[i].cookie);
}
ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
@@ -5156,6 +5317,7 @@ static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n");
vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels);
+ vhost->max_vios_nvme_channels = be32_to_cpu(rsp->num_nvme_subq_channels);
ibmvfc_free_event(evt);
break;
case IBMVFC_MAD_FAILED:
@@ -5290,8 +5452,14 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
vhost->host->max_sectors = npiv_max_sectors;
- if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
- ibmvfc_channel_enquiry(vhost);
+
+ if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS)) {
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI))
+ vhost->do_scsi_login = 1;
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ vhost->do_nvme_login = 1;
+ if (vhost->do_enquiry)
+ ibmvfc_channel_enquiry(vhost);
} else {
vhost->do_enquiry = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 01c49d81b135..454968de925b 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -617,9 +617,9 @@ struct ibmvfc_channel_enquiry {
#define IBMVFC_SUPPORT_VARIABLE_SUBQ_MSG 0x02
#define IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT 0x04
__be32 num_scsi_subq_channels;
- __be32 num_nvmeof_subq_channels;
+ __be32 num_nvme_subq_channels;
__be32 num_scsi_vas_channels;
- __be32 num_nvmeof_vas_channels;
+ __be32 num_nvme_vas_channels;
} __packed __aligned(8);
struct ibmvfc_channel_setup_mad {
@@ -636,9 +636,9 @@ struct ibmvfc_channel_setup {
#define IBMVFC_CHANNELS_CANCELED 0x04
__be32 reserved;
__be32 num_scsi_subq_channels;
- __be32 num_nvmeof_subq_channels;
+ __be32 num_nvme_subq_channels;
__be32 num_scsi_vas_channels;
- __be32 num_nvmeof_vas_channels;
+ __be32 num_nvme_vas_channels;
struct srp_direct_buf buffer;
__be64 reserved2[5];
__be64 channel_handles[IBMVFC_MAX_CHANNELS];
@@ -979,6 +979,7 @@ struct ibmvfc_host {
int log_level;
struct mutex passthru_mutex;
unsigned int max_vios_scsi_channels;
+ unsigned int max_vios_nvme_channels;
int task_set;
int init_retries;
int discovery_threads;
@@ -991,6 +992,8 @@ struct ibmvfc_host {
unsigned int using_channels:1;
unsigned int do_enquiry:1;
unsigned int nvme_enabled:1;
+ unsigned int do_scsi_login:1;
+ unsigned int do_nvme_login:1;
unsigned int aborting_passthru:1;
unsigned int scan_complete:1;
int scan_timeout;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 11/33] ibmvfc: add wrapper to get vhost associated with a channel struct
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (9 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 12/33] ibmvfc: add helper for creating protocol specific discovery event Tyrel Datwyler
` (21 subsequent siblings)
32 siblings, 0 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add ibmvfc_channels_to_vhost() to recover the parent struct ibmvfc_host
from a protocol-specific struct ibmvfc_channels.
Later patches need to operate on either the SCSI or NVMe channel group
and still access host-wide state such as the primary CRQ, device, and
logging context. Centralize that mapping in a helper instead of open-
coding container lookups at each call site.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 454968de925b..67f546ff092e 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1011,6 +1011,16 @@ struct ibmvfc_host {
wait_queue_head_t work_wait_q;
};
+static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels)
+{
+ if (channels->protocol == IBMVFC_PROTO_SCSI)
+ return container_of(channels, struct ibmvfc_host, scsi_scrqs);
+ else if (channels->protocol == IBMVFC_PROTO_NVME)
+ return container_of(channels, struct ibmvfc_host, nvme_scrqs);
+
+ return NULL;
+}
+
#define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
#define tgt_dbg(t, fmt, ...) \
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 12/33] ibmvfc: add helper for creating protocol specific discovery event
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (10 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 11/33] ibmvfc: add wrapper to get vhost associated with a channel struct Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels Tyrel Datwyler
` (20 subsequent siblings)
32 siblings, 0 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Refactor discover-target event creation so it can be shared by both SCSI
and NVMe/FC discovery.
Introduce a helper that takes a protocol-specific channel group, selects
the correct discover-target opcode, and maps the corresponding discovery
buffer into the MAD.
This is a preparatory cleanup for issuing protocol-specific discovery
MADs in later patches.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 39 ++++++++++++++++++++---------
1 file changed, 27 insertions(+), 12 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index e0edd71e9fd4..84302f982a6a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5033,6 +5033,32 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
wake_up(&vhost->work_wait_q);
}
+static struct ibmvfc_event *ibmvfc_get_disc_event(struct ibmvfc_channels *channels)
+{
+ struct ibmvfc_discover_targets *mad;
+ struct ibmvfc_host *vhost = ibmvfc_channels_to_vhost(channels);
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt)
+ return NULL;
+
+ ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.discover_targets;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ if (channels->protocol == IBMVFC_PROTO_SCSI)
+ mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+ else
+ mad->common.opcode = cpu_to_be32(IBMVFC_DISC_NVMF_TARGETS);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+ mad->bufflen = cpu_to_be32(channels->disc_buf_sz);
+ mad->buffer.va = cpu_to_be64(channels->disc_buf_dma);
+ mad->buffer.len = cpu_to_be32(channels->disc_buf_sz);
+ mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
+
+ return evt;
+}
+
/**
* ibmvfc_discover_targets - Send Discover Targets MAD
* @vhost: ibmvfc host struct
@@ -5040,8 +5066,7 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
**/
static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
{
- struct ibmvfc_discover_targets *mad;
- struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+ struct ibmvfc_event *evt = ibmvfc_get_disc_event(&vhost->scsi_scrqs);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
if (!evt) {
@@ -5050,16 +5075,6 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
return;
}
- ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
- mad = &evt->iu.discover_targets;
- memset(mad, 0, sizeof(*mad));
- mad->common.version = cpu_to_be32(1);
- mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
- mad->common.length = cpu_to_be16(sizeof(*mad));
- mad->bufflen = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
- mad->buffer.va = cpu_to_be64(vhost->scsi_scrqs.disc_buf_dma);
- mad->buffer.len = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
- mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
if (!ibmvfc_send_event(evt, vhost, default_timeout))
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (11 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 12/33] ibmvfc: add helper for creating protocol specific discovery event Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:17 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 14/33] ibmvfc: allocate and free NVMe channel group discover buffer Tyrel Datwyler
` (19 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
It can be the case that NVMeoF is enabled on both the client and VIOS,
but no queues are configured making the need to do NVMe target discovery
pointless. Add a helper to short hand the capabilities check and active
NVMe queue check.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 84302f982a6a..088167ec04ff 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -207,6 +207,12 @@ static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
return (host_caps & cap_flags) ? 1 : 0;
}
+static int ibmvfc_nvme_active(struct ibmvfc_host *vhost)
+{
+ return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) &&
+ vhost->nvme_scrqs.active_queues);
+}
+
static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
struct ibmvfc_cmd *vfc_cmd)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 14/33] ibmvfc: allocate and free NVMe channel group discover buffer
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (12 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD Tyrel Datwyler
` (18 subsequent siblings)
32 siblings, 0 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Allocate a discovery buffer for the NVMe channel group and free it on
all teardown and error paths.
The existing discovery-buffer allocation only covered the SCSI channel
group. This patch is prepratory for sending NVMe/FC target discovery
MAD.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 088167ec04ff..a700e2554e69 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6375,6 +6375,7 @@ static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
mempool_destroy(vhost->tgt_pool);
kfree(vhost->trace);
ibmvfc_free_disc_buf(vhost->dev, &vhost->scsi_scrqs);
+ ibmvfc_free_disc_buf(vhost->dev, &vhost->nvme_scrqs);
dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
vhost->login_buf, vhost->login_buf_dma);
dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf),
@@ -6437,12 +6438,15 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
if (ibmvfc_alloc_disc_buf(dev, &vhost->scsi_scrqs))
goto free_login_buffer;
+ if (ibmvfc_alloc_disc_buf(dev, &vhost->nvme_scrqs))
+ goto free_scsi_disc_buffer;
+
vhost->trace = kzalloc_objs(struct ibmvfc_trace_entry,
IBMVFC_NUM_TRACE_ENTRIES);
atomic_set(&vhost->trace_index, -1);
if (!vhost->trace)
- goto free_scsi_disc_buffer;
+ goto free_nvme_disc_buffer;
vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
sizeof(struct ibmvfc_target));
@@ -6468,6 +6472,8 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
mempool_destroy(vhost->tgt_pool);
free_trace:
kfree(vhost->trace);
+free_nvme_disc_buffer:
+ ibmvfc_free_disc_buf(dev, &vhost->nvme_scrqs);
free_scsi_disc_buffer:
ibmvfc_free_disc_buf(dev, &vhost->scsi_scrqs);
free_login_buffer:
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (13 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 14/33] ibmvfc: allocate and free NVMe channel group discover buffer Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:31 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support Tyrel Datwyler
` (17 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Extend target discovery to send protocol-specific discover-target MADs
for NVMe/FC.
Use the protocol-aware discovery helper to build an NVMe discover-target
request, submit it when NVMe/FC support is active, and process the
returned target count using the NVMe channel group's discovery buffer.
This allows the driver to discover NVMe/FC targets in parallel with the
existing SCSI discovery flow while keeping protocol-specific target data
separate.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 50 +++++++++++++++++++++++++----
1 file changed, 43 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index a700e2554e69..1d2a88e9fe94 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -173,6 +173,11 @@ static const struct {
{ IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
};
+static const char *proto_type[] = {
+ "SCSI",
+ "NVMe",
+};
+
static void ibmvfc_npiv_login(struct ibmvfc_host *);
static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
@@ -5011,19 +5016,30 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
+ struct ibmvfc_channels *channels;
u32 mad_status = be16_to_cpu(rsp->common.status);
+ u32 opcode = be32_to_cpu(rsp->common.opcode);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
+ if (opcode == IBMVFC_DISC_TARGETS)
+ channels = &vhost->scsi_scrqs;
+ else
+ channels = &vhost->nvme_scrqs;
+
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
- ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
- vhost->scsi_scrqs.num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
- max_targets);
+ ibmvfc_dbg(vhost, "Discover %s Targets succeeded\n",
+ proto_type[channels->protocol]);
+ channels->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
+ 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);
break;
case IBMVFC_MAD_FAILED:
level += ibmvfc_retry_host_init(vhost);
- ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+ ibmvfc_log(vhost, level, "Discover %s Targets failed: %s (%x:%x)\n",
+ proto_type[channels->protocol],
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
break;
@@ -5076,7 +5092,7 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
int level = IBMVFC_DEFAULT_LOG_LEVEL;
if (!evt) {
- ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n");
+ ibmvfc_log(vhost, level, "Discover SCSI Targets failed: no available events\n");
ibmvfc_hard_reset_host(vhost);
return;
}
@@ -5084,9 +5100,29 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
if (!ibmvfc_send_event(evt, vhost, default_timeout))
- ibmvfc_dbg(vhost, "Sent discover targets\n");
+ ibmvfc_dbg(vhost, "Sent discover SCSI targets\n");
else
- ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ goto link_down;
+
+ if (!ibmvfc_nvme_active(vhost))
+ return;
+
+ evt = ibmvfc_get_disc_event(&vhost->nvme_scrqs);
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Discover NVMe Targets failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent discover NVMe targets\n");
+ else
+ goto link_down;
+
+ return;
+
+link_down:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
}
static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt)
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (14 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:36 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 17/33] ibmvfc: add NVMe/FC Port " Tyrel Datwyler
` (16 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add protocol-specific handling for implicit logout and move-login
operations on NVMe/FC targets.
Select the NVMe/FC-specific implicit logout opcode when operating on an
NVMe target and update the associated logging so protocol-specific
operations are visible in debug output. This extends the existing target
relogin and migration-related flows to work with NVMe targets as well as
SCSI targets.
These changes are needed so target reauthentication and target movement
continue to work once NVMe/FC targets are added to the driver's state
machine.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 33 +++++++++++++++++++----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 1d2a88e9fe94..66fcff7ac834 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4318,7 +4318,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Implicit Logout succeeded\n");
+ tgt_dbg(tgt, "%s Implicit Logout succeeded\n", proto_type[tgt->protocol]);
break;
case IBMVFC_MAD_DRIVER_FAILED:
kref_put(&tgt->kref, ibmvfc_release_tgt);
@@ -4326,7 +4326,8 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
return;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
+ tgt_err(tgt, "%s Implicit Logout failed: rc=0x%02X\n",
+ proto_type[tgt->protocol], status);
break;
}
@@ -4359,7 +4360,10 @@ static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_t
mad = &evt->iu.implicit_logout;
memset(mad, 0, sizeof(*mad));
mad->common.version = cpu_to_be32(1);
- mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+ else
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_IMPLICIT_LOGOUT);
mad->common.length = cpu_to_be16(sizeof(*mad));
mad->old_scsi_id = cpu_to_be64(tgt->scsi_id);
return evt;
@@ -4395,7 +4399,7 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Implicit Logout\n");
+ tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
}
/**
@@ -4425,7 +4429,8 @@ static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt)
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT);
- tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
+ tgt_dbg(tgt, "%s Implicit Logout %s\n", proto_type[tgt->protocol],
+ (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
kref_put(&tgt->kref, ibmvfc_release_tgt);
wake_up(&vhost->work_wait_q);
}
@@ -4466,7 +4471,7 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Implicit Logout\n");
+ tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
}
/**
@@ -4486,7 +4491,8 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
+ tgt_dbg(tgt, "%s Move Login succeeded for new scsi_id: %llX\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id);
tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
tgt->scsi_id = tgt->new_scsi_id;
@@ -4507,8 +4513,9 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
tgt_log(tgt, level,
- "Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
- tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
+ "%s Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id,
+ be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
status);
break;
}
@@ -4548,7 +4555,10 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
move = &evt->iu.move_login;
memset(move, 0, sizeof(*move));
move->common.version = cpu_to_be32(1);
- move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+ else
+ move->common.opcode = cpu_to_be32(IBMVFC_NVMF_MOVE_LOGIN);
move->common.length = cpu_to_be16(sizeof(*move));
move->old_scsi_id = cpu_to_be64(tgt->scsi_id);
@@ -4561,7 +4571,8 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id);
+ tgt_dbg(tgt, "Sent %s Move Login for new scsi_id: %llX\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id);
}
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 17/33] ibmvfc: add NVMe/FC Port Login support
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (15 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:38 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 18/33] ibmvfc: add NVMe/FC Process " Tyrel Datwyler
` (15 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Expand the target login path to issue the NVMe/FC-specific port login
MAD for NVMe targets.
Select the correct PLOGI MAD opcode based on the target protocol and
include the protocol name in success and failure logging. The rest of
the target login flow remains shared with the existing SCSI
implementation.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 66fcff7ac834..adc146214e66 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4212,7 +4212,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Port Login succeeded\n");
+ tgt_dbg(tgt, "%s Port Login succeeded\n", proto_type[tgt->protocol]);
if (tgt->ids.port_name &&
tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
vhost->reinit = 1;
@@ -4240,9 +4240,9 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ tgt_log(tgt, level, "%s Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
break;
@@ -4288,7 +4288,10 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
} else {
plogi->common.version = cpu_to_be32(1);
}
- plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ else
+ plogi->common.opcode = cpu_to_be32(IBMVFC_NVMF_PORT_LOGIN);
plogi->common.length = cpu_to_be16(sizeof(*plogi));
plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
@@ -4297,7 +4300,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent port login\n");
+ tgt_dbg(tgt, "Sent %s port login\n", proto_type[tgt->protocol]);
}
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 18/33] ibmvfc: add NVMe/FC Process Login support
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (16 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 17/33] ibmvfc: add NVMe/FC Port " Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:39 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support Tyrel Datwyler
` (14 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Extend PRLI handling code to support NVMe/FC targets.
When the target protocol is NVMe/FC, issue the NVMe process login MAD,
set the NVMe FC-4 type, and populate NVMe-specific service parameters.
On completion, decode the returned PRLI service parameters and derive
the appropriate remote-port roles for NVMe initiator, target, and
discovery ports.
Keep the existing SCSI PRLI flow unchanged while allowing the common
target state machine to complete login for NVMe/FC targets.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 55 ++++++++++++++++++++---------
drivers/scsi/ibmvscsi/ibmvfc.h | 6 ++++
2 files changed, 45 insertions(+), 16 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index adc146214e66..5950f143e9a1 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4090,11 +4090,12 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n",
- parms->type, parms->flags, parms->service_parms);
+ tgt_dbg(tgt, "%s Process Login succeeded: %X %02X %04X\n",
+ proto_type[tgt->protocol], parms->type, parms->flags,
+ parms->service_parms);
+ index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
- index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
if (prli_rsp[index].logged_in) {
if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) {
tgt->need_login = 0;
@@ -4110,6 +4111,22 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
ibmvfc_del_tgt(tgt);
+ } else if (parms->type == IBMVFC_NVME_FCP_TYPE) {
+ if (prli_rsp[index].logged_in) {
+ /* For FC-NVMe PRLI the Image Pair field is always set to zero (see 6.3.3) */
+ tgt->need_login = 0;
+ tgt->ids.roles = 0;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_TARGET)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_TARGET;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_INITIATOR)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_INITIATOR;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_DISCOVERY)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
+ tgt->add_rport = 1;
+ } else if (prli_rsp[index].retry)
+ ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ else
+ ibmvfc_del_tgt(tgt);
} else
ibmvfc_del_tgt(tgt);
break;
@@ -4130,9 +4147,10 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
+ tgt_log(tgt, level, "%s Process Login failed: %s (%x:%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error), status);
break;
}
@@ -4174,25 +4192,30 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
} else {
prli->common.version = cpu_to_be32(1);
}
- prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI) {
+ prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+ prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
+ prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
+ prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
+
+ if (cls3_error)
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
+ } else {
+ prli->common.opcode = cpu_to_be32(IBMVFC_NVMF_PROCESS_LOGIN);
+ prli->parms.type = IBMVFC_NVME_FCP_TYPE;
+ prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_NVME_INITIATOR);
+ }
prli->common.length = cpu_to_be16(sizeof(*prli));
prli->scsi_id = cpu_to_be64(tgt->scsi_id);
- prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
- prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
- prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
- prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
-
- if (cls3_error)
- prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
-
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent process login\n");
+ tgt_dbg(tgt, "%s Sent process login\n", proto_type[tgt->protocol]);
}
/**
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 67f546ff092e..66025e6ffeed 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -381,6 +381,7 @@ struct ibmvfc_move_login {
struct ibmvfc_prli_svc_parms {
u8 type;
#define IBMVFC_SCSI_FCP_TYPE 0x08
+#define IBMVFC_NVME_FCP_TYPE 0x28
u8 type_ext;
__be16 flags;
#define IBMVFC_PRLI_ORIG_PA_VALID 0x8000
@@ -396,6 +397,11 @@ struct ibmvfc_prli_svc_parms {
#define IBMVFC_PRLI_TARGET_FUNC 0x00000010
#define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED 0x00000002
#define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED 0x00000001
+#define IBMVFC_PRLI_NVME_PI_CTRL 0x00000200
+#define IBMVFC_PRLI_NVME_SLER 0x00000100
+#define IBMVFC_PRLI_NVME_INITIATOR 0x00000020
+#define IBMVFC_PRLI_NVME_TARGET 0x00000010
+#define IBMVFC_PRLI_NVME_DISCOVERY 0x00000008
} __packed __aligned(4);
struct ibmvfc_process_login {
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (17 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 18/33] ibmvfc: add NVMe/FC Process " Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:50 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 20/33] ibmvfc: allocate targets based on protocol Tyrel Datwyler
` (13 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add protocol-specific query-target support for NVMe/FC targets.
Use the NVMe query-target specific MAD when querying an NVMe target and
update the associated debug and error logging to include the target
protocol.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 5950f143e9a1..b479537e9e78 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4859,7 +4859,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Query Target succeeded\n");
+ tgt_dbg(tgt, "%s Query Target succeeded\n", proto_type[tgt->protocol]);
if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id)
ibmvfc_del_tgt(tgt);
else
@@ -4881,9 +4881,9 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ tgt_log(tgt, level, "%s Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
status);
@@ -4923,7 +4923,10 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
query_tgt = &evt->iu.query_tgt;
memset(query_tgt, 0, sizeof(*query_tgt));
query_tgt->common.version = cpu_to_be32(1);
- query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+ else
+ query_tgt->common.opcode = cpu_to_be32(IBMVFC_NVMF_QUERY_TARGET);
query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
@@ -4933,7 +4936,7 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Query Target\n");
+ tgt_dbg(tgt, "Sent %s Query Target\n", proto_type[tgt->protocol]);
}
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 20/33] ibmvfc: allocate targets based on protocol
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (18 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:43 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI Tyrel Datwyler
` (12 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Allocate discovered targets onto the channel-group list that matches
their protocol.
When a target is created, use the discovered protocol type to decide
which list it belongs on. This keeps protocol-specific discovery
results isolated and allows later state-machine and remote-port code to
walk the correct target set.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index b479537e9e78..92c286cfc69c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -4948,25 +4948,32 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
* 0 on success / other on failure
**/
static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
- struct ibmvfc_discover_targets_entry *target)
+ struct ibmvfc_discover_targets_entry *target,
+ enum ibmvfc_protocol protocol)
{
struct ibmvfc_target *stgt = NULL;
struct ibmvfc_target *wtgt = NULL;
struct ibmvfc_target *tgt;
+ struct ibmvfc_channels *channels;
unsigned long flags;
u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK;
u64 wwpn = be64_to_cpu(target->wwpn);
+ if (protocol == IBMVFC_PROTO_SCSI)
+ channels = &vhost->scsi_scrqs;
+ else
+ channels = &vhost->nvme_scrqs;
+
/* Look to see if we already have a target allocated for this SCSI ID or WWPN */
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ list_for_each_entry(tgt, &channels->targets, queue) {
if (tgt->wwpn == wwpn) {
wtgt = tgt;
break;
}
}
- list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ list_for_each_entry(tgt, &channels->targets, queue) {
if (tgt->scsi_id == scsi_id) {
stgt = tgt;
break;
@@ -5014,6 +5021,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
memset(tgt, 0, sizeof(*tgt));
+ tgt->protocol = protocol;
tgt->scsi_id = scsi_id;
tgt->wwpn = wwpn;
tgt->vhost = vhost;
@@ -5023,7 +5031,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
spin_lock_irqsave(vhost->host->host_lock, flags);
tgt->cancel_key = vhost->task_set++;
- list_add_tail(&tgt->queue, &vhost->scsi_scrqs.targets);
+ list_add_tail(&tgt->queue, &channels->targets);
unlock_out:
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -5042,7 +5050,11 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
int i, rc;
for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
- rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
+ rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i],
+ vhost->scsi_scrqs.protocol);
+ for (i = 0; !rc && i < vhost->nvme_scrqs.num_targets; i++)
+ rc = ibmvfc_alloc_target(vhost, &vhost->nvme_scrqs.disc_buf[i],
+ vhost->nvme_scrqs.protocol);
return rc;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (19 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 20/33] ibmvfc: allocate targets based on protocol Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:52 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets Tyrel Datwyler
` (11 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Extend target deletion paths to process NVMe targets as well as SCSI
targets.
Update link-down, and host reinitialization flows to walk both the SCSI
and NVMe target lists when marking targets for deletion. This ensures
that protocol-specific target state stays consistent across adapter
resets and fabric events.
Rename ibmvfc_relogin to ibmvfc_scsi_relogin as it acts on a scsi
command.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 92c286cfc69c..1185d72a8a73 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -714,6 +714,8 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
scsi_block_requests(vhost->host);
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
ibmvfc_del_tgt(tgt);
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ ibmvfc_del_tgt(tgt);
ibmvfc_set_host_state(vhost, state);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
@@ -751,6 +753,12 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
else
ibmvfc_del_tgt(tgt);
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (vhost->client_migrated)
+ tgt->need_login = 1;
+ else
+ ibmvfc_del_tgt(tgt);
+ }
scsi_block_requests(vhost->host);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
@@ -1870,7 +1878,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
* @sdev: scsi device struct
*
**/
-static void ibmvfc_relogin(struct scsi_device *sdev)
+static void ibmvfc_scsi_relogin(struct scsi_device *sdev)
{
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
@@ -1922,7 +1930,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) &&
(be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED))
- ibmvfc_relogin(cmnd->device);
+ ibmvfc_scsi_relogin(cmnd->device);
if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
cmnd->result = (DID_ERROR << 16);
@@ -3306,6 +3314,22 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
ibmvfc_reinit_host(vhost);
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ break;
+ if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
+ continue;
+ if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ continue;
+ if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
+ continue;
+ if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
+ tgt->logo_rcvd = 1;
+ if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
+ ibmvfc_del_tgt(tgt);
+ ibmvfc_reinit_host(vhost);
+ }
+ }
break;
case IBMVFC_AE_LINK_DOWN:
case IBMVFC_AE_ADAPTER_FAILED:
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (20 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:53 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration Tyrel Datwyler
` (10 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Update the host work loop and target state-machine helpers to process
NVMe targets in addition to SCSI targets.
Check both protocol-specific target lists when determining whether there
is initialization or logout work pending, and extend the query, target
init, and target delete phases to dispatch work for NVMe targets using
the same common state-machine callbacks.
This allows the existing discovery and login state machine to drive
NVMe/FC targets through query, login, and deletion without duplicating
the control flow.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 42 +++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 1185d72a8a73..ff84ca0f9f45 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5718,6 +5718,11 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 1;
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
+ tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+ return 1;
+ }
return 0;
}
@@ -5738,6 +5743,11 @@ static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 1;
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
+ tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 1;
+ }
return 0;
}
@@ -5766,9 +5776,15 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT)
return 1;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT)
+ return 1;
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+ return 0;
return 1;
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
@@ -5777,9 +5793,15 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
return 1;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
+ return 1;
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 0;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 0;
return 1;
case IBMVFC_HOST_ACTION_LOGO:
case IBMVFC_HOST_ACTION_INIT:
@@ -5967,6 +5989,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_QUERY:
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)
+ ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
break;
case IBMVFC_HOST_ACTION_QUERY_TGTS:
@@ -5976,6 +6000,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
if (!ibmvfc_dev_init_to_do(vhost))
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
@@ -5988,6 +6018,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
if (ibmvfc_dev_logo_to_do(vhost)) {
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -6075,6 +6111,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
if (!ibmvfc_dev_init_to_do(vhost))
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (21 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:54 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login Tyrel Datwyler
` (9 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Implement the initial NVMe/FC local-port and remote-port registration
functions that notify the NVMe-FC midlayr of port discovery and loss.
Register the local port with the NVMe-FC transport, register discovered
remote ports against that local port, and add matching unregister paths
that wait for the NVMe-FC core to complete asynchronous deletion before
dropping driver references. Also store driver-private host and target
pointers in the registered NVMe-FC port objects.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 80 ++++++++++++++++++++++++++++-
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | 4 +-
drivers/scsi/ibmvscsi/ibmvfc.h | 4 ++
3 files changed, 84 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 2ee1c64665d9..d679d1ca366a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -14,10 +14,18 @@
static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
{
+ struct ibmvfc_host *vhost = lport->private;
+
+ vhost->nvme_local_port = NULL;
+ complete(&vhost->nvme_delete_done);
}
static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
{
+ struct ibmvfc_target *tgt = rport->private;
+
+ tgt->nvme_remote_port = NULL;
+ complete(&tgt->nvme_delete_done);
}
static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
@@ -70,28 +78,96 @@ 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_port_info pinfo;
+ int rc;
+
if (!IS_ENABLED(CONFIG_NVME_FC))
return 0;
- return 0;
+ if (!vhost->nvme_local_port) {
+ dev_err(vhost->dev, "Attempt to register NVMe fc remoteport without valid localport\n");
+ return -EINVAL;
+ }
+
+ memset(&pinfo, 0, sizeof(struct nvme_fc_port_info));
+ pinfo.node_name = tgt->ids.node_name;
+ pinfo.port_name = tgt->ids.port_name;
+ 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);
+
+ 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;
+ }
+
+ return rc;
}
void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt)
{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct nvme_fc_remote_port *rport = tgt->nvme_remote_port;
+ int rc;
+
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
+
+ if (!tgt->nvme_remote_port)
+ return;
+
+ ibmvfc_log(vhost, 2, "unregister_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
+ rport->node_name, rport->port_name, rport->port_id);
+ init_completion(&tgt->nvme_delete_done);
+ rc = nvme_fc_unregister_remoteport(tgt->nvme_remote_port);
+
+ if (!rc) {
+ wait_for_completion(&tgt->nvme_delete_done);
+ }
}
int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
{
+ struct nvme_fc_port_info pinfo;
+ int rc;
+
if (!IS_ENABLED(CONFIG_NVME_FC))
return 0;
- return 0;
+ 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);
+ pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+ pinfo.dev_loss_tmo = 0;
+
+ rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport,
+ vhost->dev, &vhost->nvme_local_port);
+
+ if (!rc) {
+ ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+ pinfo.node_name, pinfo.port_name, pinfo.port_id);
+ vhost->nvme_local_port->private = vhost;
+ } else
+ dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc);
+
+ return rc;
}
void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
{
+ int rc;
+
if (!IS_ENABLED(CONFIG_NVME_FC))
return;
+
+ if (vhost->nvme_local_port) {
+ init_completion(&vhost->nvme_delete_done);
+ rc = nvme_fc_unregister_localport(vhost->nvme_local_port);
+ if (!rc)
+ wait_for_completion(&vhost->nvme_delete_done);
+ }
}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
index 97e267871df2..0465e8719881 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -11,8 +11,8 @@
#ifndef _IBMVFC_NVME_H
#define _IBMVFC_NVME_H
-#include <uapi/scsi/fc/fc_fs.h>
-#include <uapi/scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_els.h>
#include <linux/nvme-fc-driver.h>
#include "ibmvfc.h"
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 66025e6ffeed..d8c2e5f1fdec 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -839,6 +839,8 @@ struct ibmvfc_target {
void (*job_step) (struct ibmvfc_target *);
struct timer_list timer;
struct kref kref;
+ struct completion nvme_delete_done;
+ struct nvme_fc_remote_port *nvme_remote_port;
};
/* a unit of work for the hosting partition */
@@ -1015,6 +1017,8 @@ struct ibmvfc_host {
struct work_struct rport_add_work_q;
wait_queue_head_t init_wait_q;
wait_queue_head_t work_wait_q;
+ struct nvme_fc_local_port *nvme_local_port;
+ struct completion nvme_delete_done;
};
static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels)
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (22 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:53 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread Tyrel Datwyler
` (8 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Register the local NVMe/FC port only after fabric login has completed.
The VIOS returns the client port ID in the fabric login response, and
that port ID is required to populate the local-port information passed
to the NVMe-FC midlayer. Delay local-port registration until that data
is available and update the registration helper accordingly.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 5 +++++
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 17 ++++++++++++++---
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index ff84ca0f9f45..81d9229bf388 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5210,6 +5210,8 @@ 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);
ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
break;
case IBMVFC_MAD_FAILED:
@@ -5271,6 +5273,7 @@ static void ibmvfc_fabric_login_scsi_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_dbg(vhost, "SCSI fabric login succeeded\n");
break;
case IBMVFC_MAD_FAILED:
@@ -5653,6 +5656,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
if (list_empty(&vhost->crq.sent) &&
vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+ ibmvfc_nvme_unregister(vhost);
ibmvfc_init_host(vhost);
return;
}
@@ -5931,6 +5935,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
list_splice_init(&vhost->purge, &purge);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_complete_purge(&purge);
+ ibmvfc_nvme_unregister(vhost);
rc = ibmvfc_reset_crq(vhost);
spin_lock_irqsave(vhost->host->host_lock, flags);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index d679d1ca366a..637d7d7015cf 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -145,14 +145,17 @@ int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
pinfo.dev_loss_tmo = 0;
rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport,
- vhost->dev, &vhost->nvme_local_port);
+ get_device(vhost->dev),
+ &vhost->nvme_local_port);
if (!rc) {
ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
pinfo.node_name, pinfo.port_name, pinfo.port_id);
vhost->nvme_local_port->private = vhost;
- } else
+ } else {
dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc);
+ put_device(vhost->dev);
+ }
return rc;
}
@@ -165,9 +168,17 @@ void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
return;
if (vhost->nvme_local_port) {
+ ibmvfc_log(vhost, 2, "unregister_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+ vhost->nvme_local_port->node_name,
+ vhost->nvme_local_port->port_name,
+ vhost->nvme_local_port->port_id);
init_completion(&vhost->nvme_delete_done);
rc = nvme_fc_unregister_localport(vhost->nvme_local_port);
- if (!rc)
+ if (!rc) {
wait_for_completion(&vhost->nvme_delete_done);
+ } else
+ dev_err(vhost->dev, "Failed to unregister NVMe fc localport (%d)\n", rc);
+
+ put_device(vhost->dev);
}
}
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (23 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:50 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h Tyrel Datwyler
` (7 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add an NVMe-specific remote-port add helper and update the rport worker
thread to walk the NVMe target list, register new NVMe remote ports, and
rescan existing ones through the NVMe-FC midlayer. Also handle delete
and delete-with-logout transitions for NVMe remote ports in the same
worker context used for SCSI rports.
This keeps remote-port registration serialized in the existing worker
model while allowing NVMe targets to participate in the common target
state machine.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 94 ++++++++++++++++++++++++++++-
1 file changed, 93 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 81d9229bf388..ffb579816e84 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -5856,6 +5856,51 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
}
+/**
+ * ibmvfc_tgt_add_nvme_rport - Tell the FC transport about a new remote port
+ * @tgt: ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_add_nvme_rport(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct nvme_fc_remote_port *rport;
+ unsigned long flags;
+
+ tgt_dbg(tgt, "Adding NVMe rport\n");
+ ibmvfc_nvme_register_remoteport(tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ rport = tgt->nvme_remote_port;
+
+ if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport\n");
+ list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ timer_delete_sync(&tgt->timer);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+ tgt->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return;
+ }
+
+ if (rport) {
+ tgt_dbg(tgt, "NVMe rport add succeeded\n");
+ tgt->target_id = tgt->nvme_remote_port->port_id;
+ } else
+ tgt_dbg(tgt, "NVMe rport add failed\n");
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
/**
* ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
* @tgt: ibmvfc target struct
@@ -5920,6 +5965,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
struct ibmvfc_target *tgt;
unsigned long flags;
struct fc_rport *rport;
+ struct nvme_fc_remote_port *nvme_rport;
LIST_HEAD(purge);
int rc;
@@ -6073,6 +6119,30 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt_dbg(tgt, "Deleteing NVMe rport\n");
+ nvme_rport = tgt->nvme_remote_port;
+ list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (nvme_rport)
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ timer_delete_sync(&tgt->timer);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+ nvme_rport = tgt->nvme_remote_port;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+ tgt->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (nvme_rport)
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ return;
+ }
+ }
+
if (vhost->state == IBMVFC_INITIALIZING) {
if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
if (vhost->reinit) {
@@ -6658,6 +6728,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
rport_add_work_q);
struct ibmvfc_target *tgt;
struct fc_rport *rport;
+ struct nvme_fc_remote_port *nvme_rport;
unsigned long flags;
int did_work;
@@ -6691,7 +6762,28 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
break;
}
}
- } while(did_work);
+
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->add_rport) {
+ did_work = 1;
+ tgt->add_rport = 0;
+ kref_get(&tgt->kref);
+ nvme_rport = tgt->nvme_remote_port;
+ if (!nvme_rport) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_tgt_add_nvme_rport(tgt);
+ } else {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (IS_ENABLED(CONFIG_NVME_FC))
+ nvme_fc_rescan_remoteport(nvme_rport);
+ }
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ break;
+ }
+ }
+ } while (did_work);
if (vhost->state == IBMVFC_ACTIVE)
vhost->scan_complete = 1;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (24 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:42 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 27/33] ibmvfc: declare global function definitions Tyrel Datwyler
` (6 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Export ibmvfc_debug so the NVMe support code can use the
existing ibmvfc_dbg logging macro.
The debug control variable is currently file-local to the core driver,
which prevents protocol-specific code in ibmvfc-nvme.c from using the
shared debug infrastructure. Make the variable global within the module
and declare it in ibmvfc-nvme.h.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 3 ++-
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index ffb579816e84..6156f10871ac 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -41,7 +41,6 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
static u16 max_sectors = IBMVFC_MAX_SECTORS;
static u16 scsi_qdepth = IBMVFC_SCSI_QDEPTH;
static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
-static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
static unsigned int mq_enabled = IBMVFC_MQ;
@@ -53,6 +52,8 @@ static unsigned int nr_nvme_channels = IBMVFC_NVME_CHANNELS;
static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
+unsigned int ibmvfc_debug = IBMVFC_DEBUG;
+
static LIST_HEAD(ibmvfc_head);
static DEFINE_SPINLOCK(ibmvfc_driver_lock);
static struct scsi_transport_template *ibmvfc_transport_template;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
index 0465e8719881..3aa285788795 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -22,6 +22,8 @@
#define IBMVFC_MAX_NVME_QUEUES 16
#define IBMVFC_NVME_CHANNELS 8
+extern unsigned int ibmvfc_debug;
+
struct ibmvfc_host;
struct ibmvfc_target;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 27/33] ibmvfc: declare global function definitions
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (25 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues Tyrel Datwyler
` (5 subsequent siblings)
32 siblings, 0 replies; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Some common functions will require visibility by both SCSI and NVMe
protocols. Make common ibmvfc helper routines available to the NVMe
support code.
Remove static from the core event allocation, event initialization,
event free, target release, command error, and event send helpers, and
declare them in ibmvfc.h. This allows ibmvfc-nvme.c to reuse the
existing event and target infrastructure.
No functional change is intended.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 22 ++++++----------------
drivers/scsi/ibmvscsi/ibmvfc.h | 17 +++++++++++++++++
2 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 6156f10871ac..206cc9508dda 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -206,13 +206,6 @@ static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
return rc;
}
-static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
-{
- u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
-
- return (host_caps & cap_flags) ? 1 : 0;
-}
-
static int ibmvfc_nvme_active(struct ibmvfc_host *vhost)
{
return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) &&
@@ -354,7 +347,7 @@ static int ibmvfc_get_err_index(u16 status, u16 error)
* Return value:
* error description string
**/
-static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
+const char *ibmvfc_get_cmd_error(u16 status, u16 error)
{
int rc = ibmvfc_get_err_index(status, error);
if (rc >= 0)
@@ -1076,7 +1069,7 @@ static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
* @evt: ibmvfc_event to be freed
*
**/
-static void ibmvfc_free_event(struct ibmvfc_event *evt)
+void ibmvfc_free_event(struct ibmvfc_event *evt)
{
struct ibmvfc_event_pool *pool = &evt->queue->evt_pool;
unsigned long flags;
@@ -1410,7 +1403,7 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
* @kref: kref struct
*
**/
-static void ibmvfc_release_tgt(struct kref *kref)
+void ibmvfc_release_tgt(struct kref *kref)
{
struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
mempool_free(tgt, tgt->vhost->tgt_pool);
@@ -1588,7 +1581,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
*
* Returns a free event from the pool.
**/
-static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
{
struct ibmvfc_event *evt = NULL;
unsigned long flags;
@@ -1612,9 +1605,6 @@ static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int r
return evt;
}
-#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
-#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
-
/**
* ibmvfc_locked_done - Calls evt completion with host_lock held
* @evt: ibmvfc evt to complete
@@ -1639,7 +1629,7 @@ static void ibmvfc_locked_done(struct ibmvfc_event *evt)
* @done: Routine to call when the event is responded to
* @format: SRP or MAD format
**/
-static void ibmvfc_init_event(struct ibmvfc_event *evt,
+void ibmvfc_init_event(struct ibmvfc_event *evt,
void (*done) (struct ibmvfc_event *), u8 format)
{
evt->cmnd = NULL;
@@ -1764,7 +1754,7 @@ static void ibmvfc_timeout(struct timer_list *t)
*
* Returns the value returned from ibmvfc_send_crq(). (Zero for success)
**/
-static int ibmvfc_send_event(struct ibmvfc_event *evt,
+int ibmvfc_send_event(struct ibmvfc_event *evt,
struct ibmvfc_host *vhost, unsigned long timeout)
{
__be64 *crq_as_u64 = (__be64 *) &evt->crq;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index d8c2e5f1fdec..ece1f379c269 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1021,6 +1021,23 @@ struct ibmvfc_host {
struct completion nvme_delete_done;
};
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved);
+#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
+#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
+
+void ibmvfc_init_event(struct ibmvfc_event *evt, void (*done) (struct ibmvfc_event *), u8 format);
+void ibmvfc_free_event(struct ibmvfc_event *evt);
+void ibmvfc_release_tgt(struct kref *kref);
+int ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsigned long timeout);
+const char *ibmvfc_get_cmd_error(u16 status, u16 error);
+
+static inline int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+{
+ u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
+
+ return (host_caps & cap_flags) ? 1 : 0;
+}
+
static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels)
{
if (channels->protocol == IBMVFC_PROTO_SCSI)
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (26 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 27/33] ibmvfc: declare global function definitions Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:58 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback Tyrel Datwyler
` (4 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Implement the NVMe-FC queue create and delete callbacks and map NVMe
controller queues onto ibmvfc hardware queues.
Use qidx of NVMe controller queue to map onto a ibmvfc_queue channel.
The Admin queue is always qidx 0 and general practice among other
drivers is to map both the Admin queue and first IO queue to the same HW
queue. Add a new ibmvfc_nvme_qhandle struct that will be used as the
opaque queue handle by the NVMe-FC layer when issuing fcp IO.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 38 +++++++++++++++++++++++++++--
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | 7 ++++++
2 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 637d7d7015cf..ecf9f4c0549c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -28,6 +28,40 @@ static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
complete(&tgt->nvme_delete_done);
}
+static int ibmvfc_nvme_create_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+ u16 qsize, void **handle)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_nvme_qhandle *qhandle;
+
+ if (!vhost->nvme_scrqs.active_queues)
+ return -ENODEV;
+
+ qhandle = kzalloc_obj(struct ibmvfc_nvme_qhandle);
+ if (!qhandle)
+ return -ENOMEM;
+
+ qhandle->cpu_id = raw_smp_processor_id();
+ qhandle->qidx = qidx;
+
+ /* Admin and first IO queue are both mapped to index 0 */
+ if (qidx)
+ qhandle->index = (qidx - 1) % vhost->nvme_scrqs.active_queues;
+ else
+ qhandle->index = qidx;
+
+ qhandle->queue = &vhost->nvme_scrqs.scrqs[qhandle->index];
+
+ *handle = qhandle;
+ return 0;
+}
+
+static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+ void *handle)
+{
+ kfree(handle);
+}
+
static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport,
struct nvmefc_ls_req *ls_req)
@@ -59,8 +93,8 @@ static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
.localport_delete = ibmvfc_nvme_localport_delete,
.remoteport_delete = ibmvfc_nvme_remoteport_delete,
- .create_queue = NULL,
- .delete_queue = NULL,
+ .create_queue = ibmvfc_nvme_create_queue,
+ .delete_queue = ibmvfc_nvme_delete_queue,
.ls_req = ibmvfc_nvme_ls_req,
.ls_abort = ibmvfc_nvme_ls_abort,
.fcp_io = ibmvfc_nvme_fcp_io,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
index 3aa285788795..4c6146048a6f 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -27,6 +27,13 @@ extern unsigned int ibmvfc_debug;
struct ibmvfc_host;
struct ibmvfc_target;
+struct ibmvfc_nvme_qhandle {
+ unsigned int qidx;
+ u16 cpu_id;
+ unsigned long index;
+ struct ibmvfc_queue *queue;
+};
+
int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt);
void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt);
int ibmvfc_nvme_register(struct ibmvfc_host *vhost);
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (27 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 1:00 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback Tyrel Datwyler
` (3 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
NVMe FC Link Service commands are required to use the ibmvfc_passthru
MAD. Initialize a pssthru mad for the target port including the DMA
addresses for the FC4_LS request and response as well as the max length
of each IU as provided in the nvmefc_ls_req struct. FC4_LS commands are
sent via the primary CRQ. Further, store the assoc_id during a create
association request as this is a required field in our vfc_cmd struct
for nvme_fcp_io commands.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 1 +
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 89 +++++++++++++++++++++++++++++
drivers/scsi/ibmvscsi/ibmvfc-nvme.h | 8 +++
drivers/scsi/ibmvscsi/ibmvfc.h | 2 +
4 files changed, 100 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 206cc9508dda..45c7bcf69201 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1633,6 +1633,7 @@ void ibmvfc_init_event(struct ibmvfc_event *evt,
void (*done) (struct ibmvfc_event *), u8 format)
{
evt->cmnd = NULL;
+ evt->ls_req = NULL;
evt->sync_iu = NULL;
evt->eh_comp = NULL;
evt->crq.format = format;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index ecf9f4c0549c..cf72f0682297 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -62,10 +62,99 @@ static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned
kfree(handle);
}
+static void ibmvfc_ls_req_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_target *tgt = evt->tgt;
+ struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+ struct fcnvme_ls_rqst_w0 *ls_rqst;
+ struct fcnvme_ls_cr_assoc_acc *ls_resp;
+ u32 status = be16_to_cpu(mad->common.status);
+ int rc = 0;
+
+ ls_rqst = (struct fcnvme_ls_rqst_w0 *)evt->ls_req->rqstaddr;
+ ls_resp = (struct fcnvme_ls_cr_assoc_acc *)evt->ls_req->rspaddr;
+
+ switch (status) {
+ case IBMVFC_MAD_SUCCESS:
+ tgt_dbg(tgt, "ls_req succeeded\n");
+ if ((ls_rqst->ls_cmd == FCNVME_LS_CREATE_ASSOCIATION) &&
+ (ls_resp->hdr.w0.ls_cmd == FCNVME_LS_ACC)) {
+ tgt->assoc_id = be64_to_cpu(ls_resp->associd.association_id);
+ tgt_dbg(tgt, "assoc_id 0x%llx\n", tgt->assoc_id);
+ }
+ break;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ break;
+ case IBMVFC_MAD_FAILED:
+ default:
+ tgt_info(tgt, "ls_req failed: %s (%x:%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
+ be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error), status);
+ break;
+ }
+
+ if (status)
+ rc = -EIO;
+
+ evt->ls_req->done(evt->ls_req, rc);
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ ibmvfc_free_event(evt);
+}
+
+static void ibmvfc_init_ls_req(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_req)
+{
+ struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
+
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(2);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_PASSTHRU);
+ mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
+ mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
+ offsetof(struct ibmvfc_passthru_mad, iu));
+ mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
+ mad->iu.cmd_len = cpu_to_be32(ls_req->rqstlen);
+ mad->iu.rsp_len = cpu_to_be32(ls_req->rsplen);
+ mad->iu.cmd.va = cpu_to_be64(ls_req->rqstdma);
+ mad->iu.cmd.len = cpu_to_be32(ls_req->rqstlen);
+ mad->iu.rsp.va = cpu_to_be64(ls_req->rspdma);
+ mad->iu.rsp.len = cpu_to_be32(ls_req->rsplen);
+}
+
static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport,
struct nvmefc_ls_req *ls_req)
{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_passthru_mad *mad;
+ struct ibmvfc_event *evt;
+
+ kref_get(&tgt->kref);
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!evt) {
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return -EBUSY;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_ls_req_done, IBMVFC_MAD_FORMAT);
+ evt->tgt = tgt;
+ evt->ls_req = ls_req;
+ ls_req->private = evt;
+
+ ibmvfc_init_ls_req(evt, ls_req);
+ mad = &evt->iu.passthru;
+ mad->iu.flags = cpu_to_be32(IBMVFC_FC4_LS_DSC_CTRL);
+ mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id);
+ mad->iu.cancel_key = cpu_to_be32((u64)evt);
+ mad->iu.target_wwpn = cpu_to_be64(tgt->wwpn);
+
+ ibmvfc_dbg(vhost, "nvme_ls_req\n");
+ if (ibmvfc_send_event(evt, vhost, IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT)) {
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return -ENXIO;
+ }
+
return 0;
}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
index 4c6146048a6f..e245b6ed0875 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -14,6 +14,8 @@
#include <scsi/fc/fc_fs.h>
#include <scsi/fc/fc_els.h>
#include <linux/nvme-fc-driver.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
#include "ibmvfc.h"
@@ -22,10 +24,16 @@
#define IBMVFC_MAX_NVME_QUEUES 16
#define IBMVFC_NVME_CHANNELS 8
+#define IBMVFC_FC4_LS_TIMEOUT 15
+#define IBMVFC_FC4_LS_CANCEL_TIMEOUT 45
+#define IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT \
+ (IBMVFC_FC4_LS_TIMEOUT + IBMVFC_FC4_LS_CANCEL_TIMEOUT)
+
extern unsigned int ibmvfc_debug;
struct ibmvfc_host;
struct ibmvfc_target;
+struct ibmvfc_queue;
struct ibmvfc_nvme_qhandle {
unsigned int qidx;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index ece1f379c269..9f6705e604fd 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -824,6 +824,7 @@ struct ibmvfc_target {
u64 scsi_id;
u64 wwpn;
u64 new_scsi_id;
+ u64 assoc_id;
struct fc_rport *rport;
int target_id;
enum ibmvfc_target_action action;
@@ -851,6 +852,7 @@ struct ibmvfc_event {
struct ibmvfc_queue *queue;
struct ibmvfc_target *tgt;
struct scsi_cmnd *cmnd;
+ struct nvmefc_ls_req *ls_req;
atomic_t free;
atomic_t active;
union ibmvfc_iu *xfer_iu;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (28 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 1:08 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback Tyrel Datwyler
` (2 subsequent siblings)
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Add helpers to initialize an ibmvfc command from an nvmefc_fcp_req,
map request scatterlists into either an inline descriptor or an external
DMA pool list, and submit the request on the selected NVMe hardware
queue. On completion, translate ibmvfc status into the NVMe-FC response
format, including transferred length and CQE handling for no-DMA
responses.
Also store the NVMe request pointer in struct ibmvfc_event so the
completion path can finish the original request.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 3 +-
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 140 +++++++++++++++++++++++++++-
drivers/scsi/ibmvscsi/ibmvfc.h | 1 +
3 files changed, 142 insertions(+), 2 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 45c7bcf69201..bc975f632da2 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1539,7 +1539,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX);
login_info->max_dma_len = cpu_to_be64(max_sectors << 9);
- login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu));
+ login_info->max_payload = cpu_to_be32(sizeof(struct nvme_fc_cmd_iu));
login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp));
login_info->partition_num = cpu_to_be32(vhost->partition_number);
login_info->vfc_frame_version = cpu_to_be32(1);
@@ -1633,6 +1633,7 @@ void ibmvfc_init_event(struct ibmvfc_event *evt,
void (*done) (struct ibmvfc_event *), u8 format)
{
evt->cmnd = NULL;
+ evt->fcp_req = NULL;
evt->ls_req = NULL;
evt->sync_iu = NULL;
evt->eh_comp = NULL;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index cf72f0682297..d31e7e9ea7a9 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -8,6 +8,7 @@
* Copyright (C) IBM Corporation, 2026
*/
+#include <linux/dmapool.h>
#include <scsi/scsi_transport_fc.h>
#include "ibmvfc-nvme.h"
@@ -164,12 +165,149 @@ static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
{
}
+static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+ struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
+ struct nvme_fc_ersp_iu *ersp = (struct nvme_fc_ersp_iu *)fcp_req->rspaddr;
+ struct nvme_completion *cqe = &ersp->cqe;
+ struct nvme_command *sqe = &((struct nvme_fc_cmd_iu *)fcp_req->cmdaddr)->sqe;
+
+ ibmvfc_dbg(evt->vhost, "fc_done: (%x:%x)\n", be16_to_cpu(vfc_cmd->status),
+ be16_to_cpu(vfc_cmd->error));
+ ibmvfc_dbg(evt->vhost, "fc_done: cmdlen: %d, rsplen %d, payload_len %d\n",
+ fcp_req->cmdlen, fcp_req->rsplen, fcp_req->payload_length);
+
+ fcp_req->status = 0;
+ if (!vfc_cmd->status) {
+ fcp_req->rcv_rsplen = NVME_FC_SIZEOF_ZEROS_RSP;
+ fcp_req->transferred_length = fcp_req->payload_length;
+ } else if (be16_to_cpu(vfc_cmd->status) & IBMVFC_FC_NVME_STATUS) {
+ fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu);
+ fcp_req->transferred_length = be32_to_cpu(ersp->xfrd_len);
+ if (be16_to_cpu(vfc_cmd->error) & IBMVFC_NVMS_VALID_NODMA_CQE)
+ cqe->command_id = sqe->common.command_id;
+ } else {
+ fcp_req->rcv_rsplen = 0;
+ fcp_req->transferred_length = 0;
+ fcp_req->status = NVME_SC_INTERNAL;
+ }
+
+ fcp_req->done(fcp_req);
+ ibmvfc_free_event(evt);
+}
+
+static struct ibmvfc_cmd *ibmvfc_nvme_init_vfc_cmd(struct ibmvfc_event *evt,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_fcp_req *fcp_req)
+{
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
+
+ memset(vfc_cmd, 0, sizeof(*vfc_cmd));
+
+ vfc_cmd->resp.va = cpu_to_be64(fcp_req->rspdma);
+ vfc_cmd->resp.len = cpu_to_be32(fcp_req->rsplen);
+ vfc_cmd->frame_type = cpu_to_be32(IBMVFC_NVME_FCP_TYPE);
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_NVMEOF_PROTOCOL);
+ vfc_cmd->payload_len = cpu_to_be32(fcp_req->cmdlen);
+ vfc_cmd->resp_len = cpu_to_be32(fcp_req->rsplen);
+ vfc_cmd->cancel_key = cpu_to_be32((u64)evt);
+ vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
+ vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
+ vfc_cmd->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+ memcpy(&vfc_cmd->v3nvme.iu, fcp_req->cmdaddr, fcp_req->cmdlen);
+
+ return vfc_cmd;
+}
+
+static void ibmvfc_nvme_map_sg_list(struct nvmefc_fcp_req *fcp_req,
+ struct srp_direct_buf *md)
+{
+ int i;
+ struct scatterlist *sg;
+
+ for_each_sg(fcp_req->first_sgl, sg, fcp_req->sg_cnt, i) {
+ md[i].va = cpu_to_be64(sg_dma_address(sg));
+ md[i].len = cpu_to_be32(sg_dma_len(sg));
+ md[i].key = 0;
+ }
+}
+
+static int ibmvfc_nvme_map_sg_data(struct nvmefc_fcp_req *fcp_req,
+ struct ibmvfc_event *evt,
+ struct ibmvfc_cmd *vfc_cmd)
+{
+ struct srp_direct_buf *data = &vfc_cmd->ioba;
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ if (!fcp_req->sg_cnt) {
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
+ return 0;
+ }
+
+ if (fcp_req->io_dir == NVMEFC_FCP_WRITE)
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
+ else
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
+
+ if (fcp_req->sg_cnt == 1) {
+ ibmvfc_nvme_map_sg_list(fcp_req, data);
+ return 0;
+ }
+
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
+
+ if (!evt->ext_list) {
+ evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
+ &evt->ext_list_token);
+
+ if (!evt->ext_list)
+ return -ENOMEM;
+ }
+
+ ibmvfc_nvme_map_sg_list(fcp_req, evt->ext_list);
+
+ data->va = cpu_to_be64(evt->ext_list_token);
+ data->len = cpu_to_be32(fcp_req->sg_cnt * sizeof(struct srp_direct_buf));
+ data->key = 0;
+ return 0;
+}
+
static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport,
void *hw_queue_handle,
struct nvmefc_fcp_req *fcp_req)
{
- return 0;
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_nvme_qhandle *qhandle = hw_queue_handle;
+ struct ibmvfc_cmd *vfc_cmd;
+ struct ibmvfc_event *evt;
+ int rc;
+
+ ibmvfc_dbg(vhost, "nvme_fcp_io\n");
+ evt = ibmvfc_get_event(qhandle->queue);
+ if (!evt)
+ return -EBUSY;
+
+ evt->hwq = qhandle->index;
+ ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq);
+
+ ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT);
+ evt->fcp_req = fcp_req;
+ fcp_req->private = evt;
+
+ vfc_cmd = ibmvfc_nvme_init_vfc_cmd(evt, rport, fcp_req);
+
+ vfc_cmd->correlation = cpu_to_be64((u64)evt);
+
+ if (likely(!(rc = ibmvfc_nvme_map_sg_data(fcp_req, evt, vfc_cmd))))
+ return ibmvfc_send_event(evt, vhost, 0);
+
+ ibmvfc_free_event(evt);
+
+ return rc;
}
static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 9f6705e604fd..ca80ceffe53a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -853,6 +853,7 @@ struct ibmvfc_event {
struct ibmvfc_target *tgt;
struct scsi_cmnd *cmnd;
struct nvmefc_ls_req *ls_req;
+ struct nvmefc_fcp_req *fcp_req;
atomic_t free;
atomic_t active;
union ibmvfc_iu *xfer_iu;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (29 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 1:05 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset Tyrel Datwyler
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Implement the NVMe FC-LS abort callback by issuing an ibmvfc cancel MAD
to the VIOS for the outstanding link-service request.
Use the saved event pointer from the original FC-LS request to identify
the command to cancel, submit the cancel operation, and complete the
abort request based on the returned status.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 64 +++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index d31e7e9ea7a9..390410e44f0d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -13,6 +13,8 @@
#include "ibmvfc-nvme.h"
+static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
+
static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
{
struct ibmvfc_host *vhost = lport->private;
@@ -159,10 +161,72 @@ static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
return 0;
}
+static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *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;
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = cpu_to_be32(2);
+ tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+ 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))
+ 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);
+ tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+ init_completion(&evt->comp);
+}
+
static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport,
struct nvmefc_ls_req *ls_abort)
{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp;
+ unsigned long flags;
+ u16 status;
+
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!vhost->logged_in || !evt)
+ 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);
+
+ 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:
+ 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 related [flat|nested] 62+ messages in thread
* [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (30 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 1:05 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset Tyrel Datwyler
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
Implement the NVMe-FC FCP abort callback by issuing an NVMF cancel MAD
on the same submission queue used by the original FCP request.
Use the original request event stored in abort_req->private to recover
the associated ibmvfc queue, then allocate a new event from that queue
so the cancel is sent on the matching nvme_scrq. Factor the TMF setup
into a dedicated helper, mirroring the LS abort path, and populate the
cancel key, task tag, target WWPN, and association ID needed for the
VIOS NVMF abort request.
The abort path sends the cancel synchronously, waits for completion,
frees the temporary event, and logs non-zero MAD status values for
debugging.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-nvme.c | 64 +++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
index 390410e44f0d..54f80b6265d4 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -374,11 +374,75 @@ static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
return rc;
}
+static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt,
+ struct nvmefc_fcp_req *abort_req)
+{
+ struct ibmvfc_tmf *tmf;
+ struct ibmvfc_event *abt_evt = abort_req->private;
+ struct ibmvfc_target *tgt = abt_evt->tgt;
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = cpu_to_be32(2);
+ tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ tmf->flags = cpu_to_be32(IBMVFC_TMF_ABORT_TASK | IBMVFC_TMF_NVMF_ASSOC);
+ tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+ tmf->my_cancel_key = cpu_to_be32((u64)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(struct nvme_fc_local_port *lport,
struct nvme_fc_remote_port *rport,
void *hw_queue_handle,
struct nvmefc_fcp_req *abort_req)
{
+ struct ibmvfc_host *vhost = lport->private;
+ 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;
+
+ queue = abt_evt->queue;
+ if (!vhost->logged_in || !queue)
+ return;
+
+ evt = ibmvfc_get_event(queue);
+ if (!evt)
+ return;
+
+ spin_lock_irqsave(queue->q_lock, flags);
+ kref_get(&tgt->kref);
+ ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+ ibmvfc_init_fcp_abort(evt, abort_req);
+ evt->sync_iu = &rsp;
+
+ if (ibmvfc_send_event(evt, vhost, default_timeout))
+ goto out;
+
+ 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 related [flat|nested] 62+ messages in thread
* [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
` (31 preceding siblings ...)
2026-07-23 0:01 ` [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback Tyrel Datwyler
@ 2026-07-23 0:01 ` Tyrel Datwyler
2026-07-23 0:58 ` sashiko-bot
32 siblings, 1 reply; 62+ messages in thread
From: Tyrel Datwyler @ 2026-07-23 0:01 UTC (permalink / raw)
To: james.bottomley, martin.petersen
Cc: linux-scsi, linuxppc-dev, linux-kernel, brking, davemarq,
Tyrel Datwyler
The current purge code for flushing outstanding commands during a
transport reset only deals with SCSI commands. Rename the
ibmvfc_scsi_eh_done completion handler to ibmvfc_vfc_eh_done and wire it
to correctly complete nvme fcp and ls commands when flushing the
inflight command list during a reset.
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc-core.c | 63 ++++++++++++++++++++---------
1 file changed, 45 insertions(+), 18 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index bc975f632da2..dfa231b6cc47 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1092,19 +1092,29 @@ void ibmvfc_free_event(struct ibmvfc_event *evt)
}
/**
- * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
+ * ibmvfc_vfc_eh_done - EH done function for queued IO
* @evt: ibmvfc event struct
*
- * This function does not setup any error status, that must be done
- * before this function gets called.
+ * This function does not setup any error status for scsi commands, that must be
+ * done before this function gets called.
**/
-static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
+static void ibmvfc_vfc_eh_done(struct ibmvfc_event *evt)
{
struct scsi_cmnd *cmnd = evt->cmnd;
+ struct nvmefc_ls_req *ls_req = evt->ls_req;
+ struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
if (cmnd) {
scsi_dma_unmap(cmnd);
scsi_done(cmnd);
+ } else if (fcp_req) {
+ fcp_req->rcv_rsplen = 0;
+ fcp_req->transferred_length = 0;
+ fcp_req->status = NVME_SC_INTERNAL;
+ fcp_req->done(fcp_req);
+ } else if (ls_req) {
+ ls_req->done(ls_req, -EIO);
+ kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
}
ibmvfc_free_event(evt);
@@ -1146,8 +1156,10 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
BUG_ON(!atomic_dec_and_test(&evt->active));
if (evt->cmnd) {
evt->cmnd->result = (error_code << 16);
- evt->done = ibmvfc_scsi_eh_done;
- } else
+ evt->done = ibmvfc_vfc_eh_done;
+ } else if (evt->fcp_req || evt->ls_req)
+ evt->done = ibmvfc_vfc_eh_done;
+ else
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
timer_delete(&evt->timer);
@@ -1164,13 +1176,16 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
{
struct ibmvfc_event *evt, *pos;
- struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+ struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs;
+ struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs;
unsigned long flags;
- int hwqs = 0;
+ int shwqs, nhwqs = 0;
int i;
- if (vhost->using_channels)
- hwqs = vhost->scsi_scrqs.active_queues;
+ if (vhost->using_channels) {
+ shwqs = vhost->scsi_scrqs.active_queues;
+ nhwqs = vhost->nvme_scrqs.active_queues;
+ }
ibmvfc_dbg(vhost, "Purging all requests\n");
spin_lock_irqsave(&vhost->crq.l_lock, flags);
@@ -1179,14 +1194,24 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
list_splice_init(&vhost->crq.sent, &vhost->purge);
spin_unlock_irqrestore(&vhost->crq.l_lock, flags);
- for (i = 0; i < hwqs; i++) {
- spin_lock_irqsave(queues[i].q_lock, flags);
- spin_lock(&queues[i].l_lock);
- list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
+ for (i = 0; i < shwqs; i++) {
+ spin_lock_irqsave(scsi_q[i].q_lock, flags);
+ spin_lock(&scsi_q[i].l_lock);
+ list_for_each_entry_safe(evt, pos, &scsi_q[i].sent, queue_list)
ibmvfc_fail_request(evt, error_code);
- list_splice_init(&queues[i].sent, &vhost->purge);
- spin_unlock(&queues[i].l_lock);
- spin_unlock_irqrestore(queues[i].q_lock, flags);
+ list_splice_init(&scsi_q[i].sent, &vhost->purge);
+ spin_unlock(&scsi_q[i].l_lock);
+ spin_unlock_irqrestore(scsi_q[i].q_lock, flags);
+ }
+
+ for (i = 0; i < nhwqs; i++) {
+ spin_lock_irqsave(nvme_q[i].q_lock, flags);
+ spin_lock(&nvme_q[i].l_lock);
+ list_for_each_entry_safe(evt, pos, &nvme_q[i].sent, queue_list)
+ ibmvfc_fail_request(evt, error_code);
+ list_splice_init(&nvme_q[i].sent, &vhost->purge);
+ spin_unlock(&nvme_q[i].l_lock);
+ spin_unlock_irqrestore(nvme_q[i].q_lock, flags);
}
}
@@ -1818,7 +1843,9 @@ int ibmvfc_send_event(struct ibmvfc_event *evt,
dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
if (evt->cmnd) {
evt->cmnd->result = DID_ERROR << 16;
- evt->done = ibmvfc_scsi_eh_done;
+ evt->done = ibmvfc_vfc_eh_done;
+ } else if (evt->fcp_req || evt->ls_req) {
+ evt->done = ibmvfc_vfc_eh_done;
} else {
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
evt->done = evt->_done;
--
2.55.0
^ permalink raw reply related [flat|nested] 62+ messages in thread
* Re: [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels
2026-07-23 0:01 ` [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels Tyrel Datwyler
@ 2026-07-23 0:17 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:17 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch introduces a new static function `ibmvfc_nvme_active` but does not add any callers for it, resulting in a `-Wunused-function` compiler warning.
--
--- Patch [13]: [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels ---
commit b0d2f58cb79cae1f9014312c2de6b5f52ab4b38e
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add helper to check NVMe/FC support with active channels
This commit adds a new helper function to check if NVMe over Fibre Channel
is supported and whether it has active configured queues, aiming to bypass
unnecessary NVMe target discovery when no queues are present.
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -207,6 +207,12 @@ static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
> return (host_caps & cap_flags) ? 1 : 0;
> }
>
> +static int ibmvfc_nvme_active(struct ibmvfc_host *vhost)
[Severity: Low]
Is this new static function used anywhere in this patch?
Adding a static function without any callers introduces a -Wunused-function
compiler warning. This can break builds that have CONFIG_WERROR enabled and
negatively impact bisectability. Should the usage of this helper be included
in this commit, or perhaps dropped if it is no longer needed?
> +{
> + return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) &&
> + vhost->nvme_scrqs.active_queues);
> +}
> +
> static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
> struct ibmvfc_cmd *vfc_cmd)
> {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=13
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe
2026-07-23 0:01 ` [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe Tyrel Datwyler
@ 2026-07-23 0:21 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:21 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The `nvme_host_queues` module parameter (stored as `nr_nvme_hw_queues`) is completely ignored when computing and configuring the maximum number of NVMe hardware queues.
--
--- Patch [8]: [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe ---
commit 99d71f6b2c78b8700e766c71f498b0d820a0931d
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: initialize NVMe channel configuration during driver probe
This commit initializes the host's NVMe channel-group state during driver
probe. It sets up the NVMe channel list, desired and maximum queue counts,
and enablement state to prepare the driver for subsequent NVMe queue
allocation and IO submission.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 56f4fb200a69b..5b6570232df36 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -6347,7 +6347,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
> struct device *dev = &vdev->dev;
> int rc = -ENOMEM;
> unsigned int online_cpus = num_online_cpus();
> - unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, 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);
[Severity: Medium]
Is there a regression in configuration handling here by bypassing the
nvme_host_queues module parameter?
Unlike the SCSI equivalent which caps queues with min(max_scsi_queues,
nr_scsi_hw_queues), max_nvme_queues is computed here without incorporating
nr_nvme_hw_queues at all.
[ ... ]
> @@ -6381,6 +6383,10 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
> vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI;
> vhost->using_channels = 0;
> vhost->do_enquiry = 1;
> + vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0;
> + vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels);
> + vhost->nvme_scrqs.max_queues = max_nvme_queues;
[Severity: Medium]
Does this assignment cause a regression for deployments trying to tune NVMe
queue counts?
Since max_nvme_queues does not incorporate nr_nvme_hw_queues, it seems the
administrator's module parameter configuration is completely ignored when
setting the maximum number of NVMe hardware queues.
> + vhost->nvme_scrqs.protocol = IBMVFC_PROTO_NVME;
> vhost->scan_timeout = 0;
>
> strcpy(vhost->partition_name, "UNKNOWN");
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=8
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs
2026-07-23 0:01 ` [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs Tyrel Datwyler
@ 2026-07-23 0:22 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:22 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Uninitialized pointer dereference in `ibmvfc_send_event` error handling for SCSI Task Management Functions (TMFs).
- [Low] The static variable `ibmvfc_nvme_fc_transport` is defined but never used, triggering a `-Wunused-variable` compiler warning.
--
--- Patch [7]: [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs ---
commit 98e0f82a0d3f6f97d6d1d88906eb468378039a58
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: split NVMe support into separate source file and add transport stubs
This patch renames ibmvfc.c to ibmvfc-core.c to begin decoupling protocols
from the core driver logic. It also introduces ibmvfc-nvme.c and
ibmvfc-nvme.h with empty callback stubs and an nvme_fc_port_template. No
functional NVMe/FC support is enabled yet.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> similarity index 99%
> rename from drivers/scsi/ibmvscsi/ibmvfc.c
> rename to drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
Looking at ibmvfc_send_event() in the newly renamed ibmvfc-core.c:
} else {
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
evt->done = evt->_done;
}
evt->done(evt);
[Severity: Critical]
Could the error path here overwrite a properly initialized done callback with
an uninitialized _done callback?
If ibmvfc_send_event() encounters an error sending a SCSI Task Management
Function (TMF) command (where rc != H_CLOSED and rc != 0), this else branch
is taken because TMFs are initialized with a NULL evt->cmnd.
Since IBMVFC_CMD_FORMAT does not initialize evt->_done in ibmvfc_init_event(),
can this copy an uninitialized or stale pointer into evt->done and immediately
execute it?
TMF operations like Abort Task Set or Target Reset are typically triggered
during SCSI error recovery, so this might lead to a wild function pointer
dereference under SAN error conditions.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> new file mode 100644
> index 0000000000000..2ee1c64665d9c
> --- /dev/null
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
[ ... ]
> +static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
> + struct nvme_fc_remote_port *rport,
> + void *hw_queue_handle,
> + struct nvmefc_fcp_req *abort_req)
> +{
> +}
> +
> +static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
> + .localport_delete = ibmvfc_nvme_localport_delete,
> + .remoteport_delete = ibmvfc_nvme_remoteport_delete,
> + .create_queue = NULL,
> + .delete_queue = NULL,
> + .ls_req = ibmvfc_nvme_ls_req,
> + .ls_abort = ibmvfc_nvme_ls_abort,
> + .fcp_io = ibmvfc_nvme_fcp_io,
> + .fcp_abort = ibmvfc_nvme_fcp_abort,
> + .map_queues = NULL,
> + .max_hw_queues = IBMVFC_NVME_HW_QUEUES,
> + .max_sgl_segments = 1024,
> + .max_dif_sgl_segments = 64,
> + .dma_boundary = 0xFFFFFFFF,
> + .local_priv_sz = sizeof(struct ibmvfc_host *),
> + .remote_priv_sz = sizeof(struct ibmvfc_target *),
> + .lsrqst_priv_sz = sizeof(struct ibmvfc_event *),
> + .fcprqst_priv_sz = sizeof(struct ibmvfc_event *),
> +};
[Severity: Low]
Is this static struct unused?
It appears ibmvfc_nvme_fc_transport is defined here but never referenced
anywhere in the file, which might trigger a compiler warning for an unused
variable.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=7
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions
2026-07-23 0:01 ` [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions Tyrel Datwyler
@ 2026-07-23 0:28 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:28 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ABI breakage in legacy SCSI implicit logout MAD length
--
--- Patch [6]: [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions ---
commit ed7fd173b2f77c91dcd53e86ac17a531e4649515
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add NVMe/FC protocol interface definitions
This commit adds the protocol definitions for client-VIOS interface updates
needed to support NVMe/FC over the ibmvfc NPIV transport. It extends the
ibmvfc interface with NVMe/FC-specific capability bits, opcodes, channel
definitions, and v3 command layout support.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
> index 0e259e9d2e9bb..f8a2bf92da410 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.h
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.h
[ ... ]
> @@ -377,20 +419,27 @@ struct ibmvfc_query_tgt {
> struct ibmvfc_implicit_logout {
> struct ibmvfc_mad_common common;
> __be64 old_scsi_id;
> - __be64 reserved[2];
> + __be64 reserved[8];
> + __be64 target_wwpn;
> } __packed __aligned(8);
[Severity: High]
Does this change break ABI compatibility with older VIOS firmware for legacy
SCSI logouts?
The size of this structure increases from 48 bytes to 104 bytes. When
tearing down a target, the existing code in the driver uses sizeof() to set
the MAD length:
drivers/scsi/ibmvscsi/ibmvfc.c:__ibmvfc_tgt_get_implicit_logout_evt() {
...
mad->common.length = cpu_to_be16(sizeof(*mad));
...
}
Since this length is sent for the existing IBMVFC_IMPLICIT_LOGOUT (0x0040)
opcode, will older VIOS firmware reject the 104-byte request as malformed,
leading to target resource leaks?
Additionally, it appears target_wwpn is never populated with the target's
WWPN in this path. Would newer VIOS firmware that recognizes the larger MAD
also reject the request due to the zeroed WWPN field?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=6
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins
2026-07-23 0:01 ` [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins Tyrel Datwyler
@ 2026-07-23 0:28 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:28 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [High] The host initialization state machine permanently stalls if NPIV login completes while `vhost->do_enquiry` is false.
- [High] NVMe fabric login is entirely skipped if the VIOS advertises NVMe support but not SCSI support, or if SCSI fabric login is disabled.
- [High] Concurrent transmission of SCSI and NVMe discovery MADs causes state machine transitions to be lost, resulting in dropped targets.
- [High] If NVMe is disabled by the guest but supported by the VIOS, `ibmvfc_channel_setup()` unconditionally dereferences a NULL `nvme->scrqs` pointer.
- [Medium] Unintended switch fallthrough in `ibmvfc_fabric_login_scsi_done` default case sends NVMe MAD on a dead link.
Pre-existing issues:
- [High] Missing validation of hypervisor-provided `active_queues` in `ibmvfc_channel_setup_done` leads to out-of-bounds heap writes.
- [High] Integer overflow of `max_cmds` when calculating queue depths for NPIV login.
--
--- Patch [10]: [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins ---
commit bffd16f5906104e27c687d661ecee756b7747318
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add logic for protocol specific fabric logins
Add support for the protocol-specific fabric login flow introduced by
the updated client/VIOS interface. Track whether SCSI and NVMe/FC fabric
login are needed, extend channel enquiry/setup handling to negotiate both
SCSI and NVMe queue counts, and issue the appropriate fabric login MADs
before target discovery begins.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index d8c2b56258eeb..e0edd71e9fd4b 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -1512,7 +1520,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
> max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
> if (mq_enabled)
> max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
> - vhost->scsi_scrqs.desired_queues;
> + (vhost->scsi_scrqs.desired_queues + vhost->nvme_scrqs.desired_queues);
[Severity: High]
This isn't a bug introduced by this patch, but does calculating the queue
depth this way risk an integer overflow?
In ibmvfc_set_login_info(), max_cmds is declared as a 16-bit unsigned integer
(u16). If large values are configured for module parameters like scsi_qdepth,
this calculation could silently exceed 65,535 and wrap around.
Could this overflow submit a corrupted max_cmds limit to the VIOS?
[ ... ]
> +static void ibmvfc_fabric_login_scsi_done(struct ibmvfc_event *evt)
> +{
> + struct ibmvfc_host *vhost = evt->vhost;
> + struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
> + u32 mad_status = be16_to_cpu(rsp->common.status);
> + int level = IBMVFC_DEFAULT_LOG_LEVEL;
> +
> + switch (mad_status) {
> + case IBMVFC_MAD_SUCCESS:
> + ibmvfc_dbg(vhost, "SCSI fabric login succeeded\n");
> + break;
> + case IBMVFC_MAD_FAILED:
[ ... ]
> + case IBMVFC_MAD_DRIVER_FAILED:
> + ibmvfc_free_event(evt);
> + return;
> + default:
> + dev_err(vhost->dev, "Invalid SCSI fabric login response: 0x%x\n", mad_status);
> + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
> + break;
> + }
> +
> + ibmvfc_free_event(evt);
> +
> + if (vhost->do_nvme_login) {
> + ibmvfc_fabric_login_nvme(vhost);
[Severity: Medium]
Does the break statement in the default case above cause an unintended switch
fallthrough?
If SCSI fabric login fails with an unrecognized status, the default case calls
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD). However, unlike the other error
cases which return, it breaks out of the switch.
Will this cause execution to fall through and improperly invoke
ibmvfc_fabric_login_nvme() on a dead link?
> + } else {
> + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
> + wake_up(&vhost->work_wait_q);
> + }
> +}
[ ... ]
> @@ -5068,22 +5211,28 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
> ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
> flags = be32_to_cpu(setup->flags);
> vhost->do_enquiry = 0;
> - active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
> - scrqs->active_queues = active_queues;
> + scsi->active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
> + nvme->active_queues = be32_to_cpu(setup->num_nvme_subq_channels);
[Severity: High]
This is a pre-existing issue, but are these hypervisor-provided counts
validated against the array bounds?
The counts are taken directly from the untrusted 32-bit values returned by
the VIOS.
If the VIOS returns values larger than the dynamically allocated array sizes
(max_queues), will the subsequent loops that initialize vios_cookie corrupt
the guest kernel heap by writing out-of-bounds?
>
> if (flags & IBMVFC_CHANNELS_CANCELED) {
> ibmvfc_dbg(vhost, "Channels Canceled\n");
> vhost->using_channels = 0;
> - } else {
> - if (active_queues)
> - vhost->using_channels = 1;
> - for (i = 0; i < active_queues; i++)
> - scrqs->scrqs[i].vios_cookie =
> - be64_to_cpu(setup->channel_handles[i]);
> -
> - ibmvfc_dbg(vhost, "Using %u channels\n",
> - vhost->scsi_scrqs.active_queues);
> + break;
> }
> +
> + if (scsi->active_queues || nvme->active_queues)
> + vhost->using_channels = 1;
> + for (i = 0; i < scsi->active_queues; i++)
> + scsi->scrqs[i].vios_cookie =
> + be64_to_cpu(setup->channel_handles[i]);
> + for (i = 0; i < nvme->active_queues; i++)
> + nvme->scrqs[i].vios_cookie =
> + be64_to_cpu(setup->channel_handles[scsi->active_queues + i]);
[ ... ]
> @@ -5098,8 +5247,12 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
> return;
> }
>
> - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
> - wake_up(&vhost->work_wait_q);
> + if (vhost->do_scsi_login) {
> + ibmvfc_fabric_login_scsi(vhost);
> + } else {
> + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
> + wake_up(&vhost->work_wait_q);
> + }
> }
[Severity: High]
Does this bypass NVMe fabric login if SCSI fabric login is disabled?
If vhost->do_scsi_login is false, the code immediately transitions the host
state to IBMVFC_HOST_ACTION_QUERY.
Since NVMe login is sequentially chained from the SCSI login completion
callback (ibmvfc_fabric_login_scsi_done), if the VIOS advertises NVMe support
but not SCSI support, will NVMe initialization fail because it is entirely
skipped?
>
> static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
> @@ -5120,12 +5276,17 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
> }
>
> memset(setup_buf, 0, sizeof(*setup_buf));
> - if (num_channels == 0)
> + if (!scsi_channels && !nvme_channels)
> setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
> else {
> - setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
> - for (i = 0; i < num_channels; i++)
> - setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
> + setup_buf->num_scsi_subq_channels = cpu_to_be32(scsi_channels);
> + setup_buf->num_nvme_subq_channels = cpu_to_be32(nvme_channels);
> + for (i = 0; i < scsi_channels; i++)
> + setup_buf->channel_handles[i] =
> + cpu_to_be64(scsi->scrqs[i].cookie);
> + for (i = 0; i < nvme_channels; i++)
> + setup_buf->channel_handles[scsi_channels + i] =
> + cpu_to_be64(nvme->scrqs[i].cookie);
[Severity: High]
Can nvme->scrqs be a NULL pointer here?
If the guest disables NVMe (e.g., via module parameter),
ibmvfc_alloc_channels() for NVMe is skipped, leaving nvme->scrqs as NULL.
When ibmvfc_channel_enquiry_done() executes, if the VIOS reports
num_nvme_subq_channels > 0, nvme_channels calculates to > 0.
Will the loop above then unconditionally dereference the NULL nvme->scrqs
pointer?
> }
>
> ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
[ ... ]
> @@ -5290,8 +5452,14 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
> vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
> vhost->host->max_sectors = npiv_max_sectors;
>
> - if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
> - ibmvfc_channel_enquiry(vhost);
> +
> + if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS)) {
> + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI))
> + vhost->do_scsi_login = 1;
> + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
> + vhost->do_nvme_login = 1;
> + if (vhost->do_enquiry)
> + ibmvfc_channel_enquiry(vhost);
> } else {
> vhost->do_enquiry = 0;
> ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
[Severity: High]
Does removing the else block here cause the host initialization state machine
to stall if vhost->do_enquiry is false?
If sub-CRQ channel allocation fails, do_enquiry is set to 0. When NPIV login
subsequently completes, this block skips ibmvfc_channel_enquiry().
Since there is no fallback to transition the state to IBMVFC_HOST_ACTION_QUERY
when do_enquiry is false, does this cause the state machine to permanently
stall?
[Severity: High]
One additional question about target discovery behavior in
ibmvfc_discover_targets():
When both SCSI and NVMe target discovery are active, both discovery MADs
are sent concurrently:
if (!ibmvfc_send_event(evt, vhost, default_timeout))
ibmvfc_dbg(vhost, "Sent discover SCSI targets\n");
...
if (!ibmvfc_send_event(evt, vhost, default_timeout))
ibmvfc_dbg(vhost, "Sent discover NVMe targets\n");
However, when each completes, they both call ibmvfc_discover_targets_done(),
which attempts to advance the shared state machine:
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
Will the second completion be ignored because the host action is no longer
IBMVFC_HOST_ACTION_INIT_WAIT, causing one protocol's discovered targets to be
silently dropped and never allocated?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=10
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path
2026-07-23 0:01 ` [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path Tyrel Datwyler
@ 2026-07-23 0:30 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:30 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The error path added for a NULL event attempts an invalid state transition to IBMVFC_TGT_ACTION_NONE, leaving the target stuck in IBMVFC_TGT_ACTION_LOGOUT_RPORT and potentially causing an infinite host reset loop.
--
--- Patch [3]: [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path ---
commit c8380c3ff6514fe4df58a3ad3f0363eddb03454e
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: check for NULL evt in implicit LOGO and target delete path
Adds a check for a NULL event in the implicit LOGO and target delete path to
prevent a null pointer dereference in ibmvfc_send_event. If the event is
NULL, it escalates to resetting the host.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 72317d7840a53..59102dd1c7878 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
> @@ -4406,6 +4406,14 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
> evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
> ibmvfc_tgt_implicit_logout_and_del_done);
>
> + if (!evt) {
> + vhost->discovery_threads--;
> + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
[Severity: High]
Will this state transition succeed? Looking at ibmvfc_set_tgt_action(),
it appears that transitioning from IBMVFC_TGT_ACTION_LOGOUT_RPORT to
IBMVFC_TGT_ACTION_NONE is silently rejected by the state machine:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_set_tgt_action() {
...
case IBMVFC_TGT_ACTION_LOGOUT_RPORT:
if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT ||
action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt->action = action;
rc = 0;
}
break;
...
}
Since this silently fails and leaves the target in
IBMVFC_TGT_ACTION_LOGOUT_RPORT, does this cause ibmvfc_dev_logo_to_do()
to continually return 1?
If so, the discovery loop could get stuck infinitely retrying the logout
and triggering continuous host resets. Should this error path transition
to IBMVFC_TGT_ACTION_DEL_RPORT instead?
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> + __ibmvfc_reset_host(vhost);
> + return;
> + }
> +
> ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
> if (ibmvfc_send_event(evt, vhost, default_timeout)) {
> vhost->discovery_threads--;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD
2026-07-23 0:01 ` [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD Tyrel Datwyler
@ 2026-07-23 0:31 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:31 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Concurrent SCSI and NVMe target discovery requests race, causing DMA data races and ignored discovery responses.
- [High] NVMe targets are ignored by the host state machine and lifecycle loops, leading to memory leaks and uninitialized targets.
--
--- Patch [15]: [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD ---
commit 64be8514770ed6154d989f0192b5c3cb746d46d2
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: send NVMe target discovery MAD
This commit extends target discovery to send protocol-specific discover-target
MADs for NVMe/FC. It uses a protocol-aware discovery helper to build and submit
an NVMe discover-target request, processing the returned target count into the
NVMe channel group's discovery buffer.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index a700e2554e692..1d2a88e9fe941 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -5011,19 +5016,30 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
> {
> struct ibmvfc_host *vhost = evt->vhost;
> struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
> + struct ibmvfc_channels *channels;
> u32 mad_status = be16_to_cpu(rsp->common.status);
> + u32 opcode = be32_to_cpu(rsp->common.opcode);
> int level = IBMVFC_DEFAULT_LOG_LEVEL;
>
> + if (opcode == IBMVFC_DISC_TARGETS)
> + channels = &vhost->scsi_scrqs;
> + else
> + channels = &vhost->nvme_scrqs;
> +
> switch (mad_status) {
> case IBMVFC_MAD_SUCCESS:
> - ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
> - vhost->scsi_scrqs.num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
> - max_targets);
> + ibmvfc_dbg(vhost, "Discover %s Targets succeeded\n",
> + proto_type[channels->protocol]);
> + channels->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
> + 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);
[Severity: High]
If SCSI and NVMe target discovery requests are sent concurrently, won't the
first completion trigger ibmvfc_discover_targets_done() and immediately set
the host action to IBMVFC_HOST_ACTION_ALLOC_TGTS?
This state transition wakes the worker thread, which then calls
ibmvfc_alloc_targets().
Because ibmvfc_alloc_targets() unconditionally loops over both the nvme_scrqs
and scsi_scrqs discovery buffers to parse targets, could the worker thread
read from a buffer while it is still being actively modified by the VIOS via
DMA for the slower, in-flight protocol?
Additionally, when the slower protocol finally completes, its transition to
ALLOC_TGTS is ignored because the host is no longer in INIT_WAIT. Does this
cause those discovered targets to be silently dropped?
[Severity: High]
When NVMe targets are successfully discovered here, they are eventually
allocated and appended to vhost->nvme_scrqs.targets by ibmvfc_alloc_target().
However, looking at the host state machine loops in ibmvfc_do_work()
(specifically for IBMVFC_HOST_ACTION_TGT_INIT) and the teardown logic in
ibmvfc_link_down():
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_do_work()
case IBMVFC_HOST_ACTION_TGT_INIT:
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
tgt->job_step(tgt);
break;
}
}
These loops appear to only iterate over vhost->scsi_scrqs.targets.
Will this cause the newly populated NVMe targets to be completely ignored
for initialization?
Does this also mean these NVMe target structures will leak during driver
teardown, since ibmvfc_link_down() also only cleans up the scsi_scrqs list?
> break;
> case IBMVFC_MAD_FAILED:
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=15
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels
2026-07-23 0:01 ` [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels Tyrel Datwyler
@ 2026-07-23 0:33 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:33 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] NVMe sub-queue registration failure unintentionally disables SCSI multiqueue support.
- [Medium] Incomplete integration of NVMe sub-CRQs and targets in host lifecycle and work scheduling routines.
--
--- Patch [9]: [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels ---
commit bf86ac3ca0d20bb114b63c6bb20bb9e08b7ed641
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: alloc/dealloc sub-queues for nvme channels
This commit updates the ibmvfc driver to allocate, register, deregister, and
release NVMe subordinate CRQs alongside the existing SCSI sub-CRQs. It
extends the CRQ reset and re-enable paths to handle NVMe channels during
adapter setup and teardown.
> This keeps the NVMe queue lifecycle aligned with the existing SCSI queue
> lifecycle so both protocols are reset consistently across probe, remove,
> and connection recovery.
[Severity: Medium]
Is this alignment complete? Looking at the rest of the host lifecycle,
several routines like ibmvfc_purge_requests() only iterate over the SCSI
queues:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_purge_requests() {
...
struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
...
}
Similarly, ibmvfc_link_down() and ibmvfc_dev_init_to_do() appear to abandon
NVMe targets by only checking scsi_scrqs:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_link_down() {
...
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
ibmvfc_del_tgt(tgt);
...
}
Will this cause pending I/O or targets on nvme_scrqs to be missed during
connection recovery or adapter teardown events?
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 5b6570232df36..d8c2b56258eeb 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -6119,6 +6123,13 @@ static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
>
> ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
>
> + if (vhost->nvme_enabled) {
> + if (ibmvfc_alloc_channels(vhost, &vhost->nvme_scrqs))
> + vhost->nvme_enabled = 0;
> + else
> + ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
[Severity: High]
If NVMe channel registration fails here, will it unintentionally disable SCSI
multiqueue support?
Looking at ibmvfc_reg_sub_crqs(), if channel registration fails, it clears
the global do_enquiry flag:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_reg_sub_crqs() {
...
for (i = 0; i < channels->max_queues; i++) {
if (ibmvfc_register_channel(vhost, channels, i)) {
for (j = i; j > 0; j--)
ibmvfc_deregister_channel(vhost, channels, j - 1);
vhost->do_enquiry = 0;
return;
}
}
...
}
Since ibmvfc_reg_sub_crqs() is called for the SCSI channels right before
the NVMe channels, setting do_enquiry to 0 upon an NVMe failure would
prevent the driver from sending IBMVFC_CHANNEL_ENQUIRY and
IBMVFC_CHANNEL_SETUP during NPIV login.
Does this cause the SCSI protocol to silently fall back to single-queue mode
if a user attempts to enable NVMe on a hypervisor that lacks support or
resources for additional sub-queues?
> + }
> +
> LEAVE;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=9
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups
2026-07-23 0:01 ` [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups Tyrel Datwyler
@ 2026-07-23 0:34 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:34 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Pre-existing memory leak and mempool_destroy BUG() trigger during module teardown for orphaned targets
--
--- Patch [5]: [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups ---
commit daeeaeede0866c302e3a906a87d7eef6faf608ae
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: move target list from host to protocol specific channel groups
This commit prepares the driver for protocol-specific target management by
moving the target list and target count out of struct ibmvfc_host and into
struct ibmvfc_channels. The existing target iteration, allocation, and
discovery paths are updated to use the SCSI channel group's target list.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 412e946abcb77..78b4ac201a494 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -5662,7 +5662,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
> break;
> case IBMVFC_HOST_ACTION_TGT_DEL:
> case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
> - list_for_each_entry(tgt, &vhost->targets, queue) {
> + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
> if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
> tgt->job_step(tgt);
> break;
> @@ -5674,7 +5674,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
> return;
> }
>
> - list_for_each_entry(tgt, &vhost->targets, queue) {
> + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
[Severity: High]
This is a pre-existing issue, but does this code leave targets orphaned on the
new vhost->scsi_scrqs.targets list during module teardown?
If a target is in IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT when
ibmvfc_remove() begins, ibmvfc_link_down() fails to change its state.
The worker thread in ibmvfc_do_work() transitions the target to
IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT without unlinking it:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_do_work() {
...
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
...
}
It then calls fc_remote_port_delete(), which schedules asynchronous teardown.
The worker thread sets vhost->action = IBMVFC_HOST_ACTION_NONE and
wakes the teardown wait queue. ibmvfc_remove() resumes and permanently kills
the worker thread before the target completes its asynchronous teardown:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_remove() {
...
kthread_stop(vhost->work_thread);
...
}
When the asynchronous terminate_rport_io() callback finally fires, it changes
the target state to LOGOUT_RPORT, but the dead worker thread cannot process it.
Does this leave the orphaned target on vhost->scsi_scrqs.targets indefinitely,
preventing its memory from being freed and triggering a
BUG_ON(pool->curr_nr != pool->min_nr) during mempool_destroy() in
ibmvfc_free_mem()?
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_free_mem() {
...
mempool_destroy(vhost->tgt_pool);
...
}
> if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
> tgt_dbg(tgt, "Deleting rport\n");
> rport = tgt->rport;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown
2026-07-23 0:01 ` [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown Tyrel Datwyler
@ 2026-07-23 0:35 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:35 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] The patch incorrectly assumes all events with `!evt->cmnd` are MAD events, assigning an uninitialized `evt->_done` pointer to `evt->done` for TMF events, causing a kernel panic or deadlock.
Pre-existing issues:
- [High] The error path in `ibmvfc_probe()` lacks a `cancel_work_sync()` or `flush_work()` for `vhost->rport_add_work_q`, leading to a workqueue leak and a use-after-free.
- [High] Target objects allocated from `vhost->tgt_pool` are improperly freed using `kfree()` instead of `mempool_free()`, permanently depleting the mempool's reserves.
- [High] In `ibmvfc_cancel_all_mq()`, the return value of `ibmvfc_send_event()` is ignored. If sending fails with `H_CLOSED`, the event is freed internally but the caller adds it to a wait list, causing a use-after-free and an indefinite hang.
--
--- Patch [2]: [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown ---
commit 17edf5c871930177f3e5dcc29059e768abdcd3dd
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: flush rport_add_work_q during driver teardown
INIT_WORK is called on rport_add_work_q during driver probe. When the
adapter is removed, ibmvfc_remove() stops the work thread and proceeds
to tear down the FC host, but any work item queued via schedule_work()
that raced the teardown could still be running or pending in the
system workqueue.
Add a flush_work() call after kthread_stop() and before fc_remove_host()
to drain any outstanding rport add work before the FC host and its rports
are torn down.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 0a5f73df8d7d2..72317d7840a53 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
> @@ -6443,6 +6443,7 @@ static void ibmvfc_remove(struct vio_dev *vdev)
>
> ibmvfc_wait_while_resetting(vhost);
> kthread_stop(vhost->work_thread);
> + flush_work(&vhost->rport_add_work_q);
> fc_remove_host(vhost->host);
> scsi_remove_host(vhost->host);
[Severity: High]
This isn't a bug introduced by this patch, but does ibmvfc_probe() also need
a similar flush_work() or cancel_work_sync() in its error path?
If ibmvfc_probe() fails after starting the kthread and initializing the CRQ
(for example, if scsi_add_host() fails), it stops the kthread and frees the
host memory:
kill_kthread:
kthread_stop(vhost->work_thread);
free_host_mem:
ibmvfc_free_mem(vhost);
free_scsi_host:
scsi_host_put(shost);
If the CRQ initialization has already received an interrupt, transitioned the
host state, and scheduled rport_add_work_q on the system workqueue, wouldn't
ibmvfc_rport_add_thread() execute after the vhost structure has been freed,
causing a use-after-free?
[ ... ]
> @@ -1367,8 +1367,8 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
> static void ibmvfc_release_tgt(struct kref *kref)
> {
> struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
> kfree(tgt);
> }
[Severity: High]
This isn't a bug introduced by this patch, but does ibmvfc_release_tgt()
permanently deplete the tgt_pool mempool reserves?
Target structures are allocated from a mempool in ibmvfc_alloc_target():
tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
However, ibmvfc_release_tgt() frees them directly using kfree(). While the
memory is returned to the slab allocator, bypassing mempool_free() means the
mempool's internal reserve count is never replenished.
Could this permanently deplete the mempool's preallocated reserves and lead
to allocation failures or device discovery hangs under memory pressure?
[ ... ]
> @@ -1591,8 +1591,8 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
> {
> evt->cmnd = NULL;
> evt->sync_iu = NULL;
> evt->eh_comp = NULL;
> evt->crq.format = format;
> if (format == IBMVFC_CMD_FORMAT)
> evt->done = done;
> else {
> evt->_done = done;
[ ... ]
> @@ -1774,9 +1774,9 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
> if (evt->cmnd) {
> evt->cmnd->result = DID_ERROR << 16;
> evt->done = ibmvfc_scsi_eh_done;
> } else {
> evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
> evt->done = evt->_done;
> }
>
> evt->done(evt);
[Severity: High]
Does this else branch in ibmvfc_send_event() correctly handle Task
Management Function (TMF) events?
TMF events use the IBMVFC_CMD_FORMAT format and have evt->cmnd = NULL.
When ibmvfc_init_event() initializes a CMD format event, it explicitly leaves
evt->_done uninitialized.
If ibmvfc_send_event() encounters an error sending a TMF event, it evaluates
!evt->cmnd, enters the else branch, and assigns evt->done = evt->_done.
Could this execute an uninitialized or stale callback?
[ ... ]
> @@ -2630,8 +2630,8 @@ static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
> }
> evt->sync_iu = &queues[i].cancel_rsp;
> ibmvfc_send_event(evt, vhost, default_timeout);
> list_add_tail(&evt->cancel, &cancelq);
> }
>
> spin_unlock(queues[i].q_lock);
> }
> spin_unlock_irqrestore(vhost->host->host_lock, flags);
>
> if (list_empty(&cancelq)) {
> if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
> sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
> return 0;
> }
>
> sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
>
> list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
> wait_for_completion(&evt->comp);
[Severity: High]
This isn't a bug introduced by this patch, but does ibmvfc_cancel_all_mq()
hang indefinitely if ibmvfc_send_event() fails with H_CLOSED?
If ibmvfc_send_event() returns H_CLOSED, it returns SCSI_MLQUEUE_HOST_BUSY
and frees the event via ibmvfc_free_event().
However, ibmvfc_cancel_all_mq() ignores the return value of
ibmvfc_send_event(). It unconditionally proceeds to call
list_add_tail(&evt->cancel, &cancelq) on the now-freed event and
subsequently loops over the cancelq list calling
wait_for_completion(&evt->comp).
Could this cause a use-after-free and an indefinite hang since the completion
object resides in freed memory and will never be signaled?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support
2026-07-23 0:01 ` [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support Tyrel Datwyler
@ 2026-07-23 0:36 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:36 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The Move Login completion handler incorrectly schedules a SCSI-specific Process Login (PRLI) for NVMe targets, breaking the target initialization state machine.
Pre-existing issues:
- [High] Event completion handlers modify shared host and target state without holding `host_lock`, causing data races with the worker thread.
--
--- Patch [16]: [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support ---
commit 3f8ab5b8122810e98caf8a4efbdac3e6f3574d85
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add NVMe/FC Implicit Logout and Move Login support
This commit adds protocol-specific opcodes and debug logging for implicit
logout and move login operations on NVMe/FC targets. It extends the existing
target login and migration flows to distinguish between SCSI and NVMe target
protocols.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 1d2a88e9fe941..66fcff7ac834b 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -4318,7 +4318,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
>
> switch (status) {
> case IBMVFC_MAD_SUCCESS:
> - tgt_dbg(tgt, "Implicit Logout succeeded\n");
> + tgt_dbg(tgt, "%s Implicit Logout succeeded\n", proto_type[tgt->protocol]);
[Severity: High]
This is a pre-existing issue, but it appears that event completion handlers
like ibmvfc_tgt_implicit_logout_done() and ibmvfc_tgt_move_login_done()
modify shared host and target state without holding host_lock.
Can this cause data races with the worker thread?
The handlers are invoked locklessly during CRQ draining:
ibmvfc_drain_sub_crq() {
...
spin_unlock_irqrestore(scrq->q_lock, flags);
list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
...
evt->done(evt);
}
}
The evt->done() call executes ibmvfc_tgt_implicit_logout_done() which does:
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
If vhost->discovery_threads is decremented locklessly while the worker thread
(ibmvfc_do_work) increments it under host_lock, could we lose decrements?
If we do, the count could artificially inflate and eventually hit the
disc_threads limit, permanently aborting future target discovery and login
operations.
[ ... ]
> @@ -4486,7 +4491,8 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
> ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
> switch (status) {
> case IBMVFC_MAD_SUCCESS:
> - tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
> + tgt_dbg(tgt, "%s Move Login succeeded for new scsi_id: %llX\n",
> + proto_type[tgt->protocol], tgt->new_scsi_id);
[Severity: High]
Does this code inadvertently schedule a SCSI-specific process login for NVMe
targets?
Just below this debug statement in ibmvfc_tgt_move_login_done(), a
successful move login unconditionally initializes the target with
ibmvfc_tgt_send_prli:
case IBMVFC_MAD_SUCCESS:
...
ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
break;
However, ibmvfc_tgt_send_prli() hardcodes the SCSI FCP type and opcode
without any fallback for NVMe targets:
prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
...
prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
Since this patch adds support for distinguishing NVMe/FC targets during move
login, will an NVMe target fail to complete the login sequence here when it
receives an invalid SCSI-specific Process Login MAD?
> tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
> tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
> tgt->scsi_id = tgt->new_scsi_id;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=16
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 17/33] ibmvfc: add NVMe/FC Port Login support
2026-07-23 0:01 ` [PATCH v2 17/33] ibmvfc: add NVMe/FC Port " Tyrel Datwyler
@ 2026-07-23 0:38 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:38 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The newly added NVMe Port Login logic is unreachable because NVMe targets are never allocated.
- [High] A successful NVMe Port Login (PLOGI) will incorrectly proceed to issue a SCSI Process Login (PRLI).
--
--- Patch [17]: [PATCH v2 17/33] ibmvfc: add NVMe/FC Port Login support ---
commit dfa3e967fac7e65d31ae01b75c2fc44ab7b459fd
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add NVMe/FC Port Login support
Expand the target login path to issue the NVMe/FC-specific port login
MAD for NVMe targets.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 66fcff7ac834b..adc146214e668 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -4212,7 +4212,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
> ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
> switch (status) {
> case IBMVFC_MAD_SUCCESS:
> - tgt_dbg(tgt, "Port Login succeeded\n");
> + tgt_dbg(tgt, "%s Port Login succeeded\n", proto_type[tgt->protocol]);
[Severity: High]
If an NVMe target successfully completes a Port Login (PLOGI) here, will it
incorrectly proceed to issue a SCSI Process Login (PRLI)?
After a successful PLOGI, the state machine transitions to send a PRLI:
ibmvfc_tgt_plogi_done() {
...
ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
break;
...
}
However, ibmvfc_tgt_send_prli() seems to unconditionally issue a SCSI FCP
PRLI without checking if tgt->protocol is NVMe:
ibmvfc_tgt_send_prli() {
...
prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
prli->common.length = cpu_to_be16(sizeof(*prli));
prli->scsi_id = cpu_to_be64(tgt->scsi_id);
prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
...
}
Does this cause a protocol error and target initialization failure for NVMe
targets by sending them a SCSI FCP PRLI?
[ ... ]
> @@ -4288,7 +4288,10 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
> } else {
> plogi->common.version = cpu_to_be32(1);
> }
> - plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
> + if (tgt->protocol == IBMVFC_PROTO_SCSI)
> + plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
> + else
> + plogi->common.opcode = cpu_to_be32(IBMVFC_NVMF_PORT_LOGIN);
[Severity: High]
Will this NVMe branch ever execute? Looking at ibmvfc_alloc_targets(), it
appears to only allocate SCSI targets, completely ignoring the NVMe discovery
buffer:
ibmvfc_alloc_targets() {
...
for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
...
}
Since NVMe targets are never instantiated, does this leave the entire NVMe
target discovery and login flow as unreachable dead code?
> plogi->common.length = cpu_to_be16(sizeof(*plogi));
> plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=17
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 18/33] ibmvfc: add NVMe/FC Process Login support
2026-07-23 0:01 ` [PATCH v2 18/33] ibmvfc: add NVMe/FC Process " Tyrel Datwyler
@ 2026-07-23 0:39 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:39 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing protocol validation in PRLI response handling allows protocol mismatch between target and response.
--
--- Patch [18]: [PATCH v2 18/33] ibmvfc: add NVMe/FC Process Login support ---
commit 6682b3a365a88d88e3d8b2117f28f9fba600bb88
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add NVMe/FC Process Login support
This commit extends the PRLI handling code in the ibmvfc driver to
support NVMe/FC targets. It issues the NVMe process login MAD when the
target protocol is NVMe/FC, sets the NVMe FC-4 type, and populates the
service parameters, subsequently decoding them to assign the proper
remote-port roles.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -4110,6 +4111,22 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
> ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
> else
> ibmvfc_del_tgt(tgt);
> + } else if (parms->type == IBMVFC_NVME_FCP_TYPE) {
> + if (prli_rsp[index].logged_in) {
> + /* For FC-NVMe PRLI the Image Pair field is always set to zero (see 6.3.3) */
> + tgt->need_login = 0;
> + tgt->ids.roles = 0;
> + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_TARGET)
> + tgt->ids.roles |= FC_PORT_ROLE_NVME_TARGET;
> + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_INITIATOR)
> + tgt->ids.roles |= FC_PORT_ROLE_NVME_INITIATOR;
> + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_DISCOVERY)
> + tgt->ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
> + tgt->add_rport = 1;
[Severity: Medium]
Does this code allow a protocol mismatch if the target returns an unexpected
FC-4 type in the PRLI response?
In ibmvfc_tgt_prli_done(), the response is evaluated purely based on
parms->type without checking if it matches the expected tgt->protocol.
If a target responds to a SCSI PRLI (where tgt->protocol is
IBMVFC_PROTO_SCSI) with the NVMe type (IBMVFC_NVME_FCP_TYPE), would the
driver branch into this NVMe handling block?
This appears to allow registering an FC remote port with the wrong transport
roles if a buggy or malicious target returns a mismatched response type.
> + } else if (prli_rsp[index].retry)
> + ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
> + else
> + ibmvfc_del_tgt(tgt);
> } else
> ibmvfc_del_tgt(tgt);
> break;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=18
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h
2026-07-23 0:01 ` [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h Tyrel Datwyler
@ 2026-07-23 0:42 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:42 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The exported variable `ibmvfc_debug` is completely unused by the NVMe support code.
--
--- Patch [26]: [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h ---
commit 35cd49c4c7f0c39e5adc622d14fb39bef0080a91
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h
This commit makes the ibmvfc_debug control variable global and declares it
in ibmvfc-nvme.h. The stated intent is to allow NVMe protocol-specific code
in ibmvfc-nvme.c to use the shared ibmvfc_dbg logging macro.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
> index 0465e87198817..3aa2857887950 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
> @@ -22,6 +22,8 @@
> #define IBMVFC_MAX_NVME_QUEUES 16
> #define IBMVFC_NVME_CHANNELS 8
>
> +extern unsigned int ibmvfc_debug;
[Severity: Low]
Is this variable actually utilized by the NVMe support code?
The commit message indicates this export is so ibmvfc-nvme.c can use the
ibmvfc_dbg macro. However, looking at drivers/scsi/ibmvscsi/ibmvfc-nvme.c,
it appears to use ibmvfc_log instead and contains no references to
ibmvfc_dbg, DBG_CMD, or ibmvfc_debug.
Are there follow-up patches that add these debug calls, or is this export
unnecessary?
> +
> struct ibmvfc_host;
> struct ibmvfc_target;
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=26
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure
2026-07-23 0:01 ` [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure Tyrel Datwyler
@ 2026-07-23 0:42 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:42 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] The patch introduces an uninitialized function pointer dereference when a Task Management Function (TMF) fails to send. It assigns `evt->done = evt->_done;` in the failure path of `ibmvfc_send_event()` when `evt->cmnd` is NULL, assuming all such events are Management Datagrams (MADs). However, TMF events like `ibmvfc_reset_device` and `ibmvfc_abort_task_set` lack a SCSI command but use `IBMVFC_CMD_FORMAT`. For `IBMVFC_CMD_FORMAT`, `ibmvfc_init_event()` leaves `evt->_done` completely uninitialized. Overwriting `evt->done` with the uninitialized `evt->_done` and executing it causes a kernel panic or use-after-free (by executing a stale callback from a previous allocation).
Pre-existing issues:
- [High] In the failure path of `ibmvfc_send_event()`, when an event without a SCSI command (`evt->cmnd == NULL`) fails to send, the code writes an error status to `evt->xfer_iu->mad_common.status`. However, TMF events use `struct ibmvfc_cmd` rather than `struct ibmvfc_mad_common`. Because they share the `ibmvfc_iu` union, writing to `mad_common.status` (offset 12) corrupts the `payload_len` field of the `ibmvfc_cmd` structure and leaves the actual `cmd.status` field (offset 24) as 0 (success). This causes callers like `ibmvfc_reset_device` to silently ignore transport failures.
- [High] `ibmvfc_cancel_all_mq()` ignores the return value of `ibmvfc_send_event()`. If the transmit fails because the queue is closed (`H_CLOSED`), `ibmvfc_send_event()` immediately frees the event and returns `SCSI_MLQUEUE_HOST_BUSY` without ever triggering completion. Because the return value is ignored, the caller blindly adds the already-freed event to the `cancelq` list and blocks on `wait_for_completion(&evt->comp)`. This causes a permanent thread hang on a completion that will never occur, and creates a severe use-after-free vulnerability by linking a freed object into an active list.
--
--- Patch [1]: [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure ---
commit 5ca8f4e826ff5d72e9b72e057f69819222a9934b
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: don't call locked done variant for MADs on send failure
This commit modifies the error path for Management Datagram (MAD) sends
when h_send_crq fails with an error other than H_CLOSED. It updates the
fallback logic to use the saved unlocked evt->_done completion handler.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 3dd2adda195ee..0a5f73df8d7d2 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -1774,8 +1774,10 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
> if (evt->cmnd) {
> evt->cmnd->result = DID_ERROR << 16;
> evt->done = ibmvfc_scsi_eh_done;
> - } else
> + } else {
> evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
[Severity: High]
This is a pre-existing issue, but does writing to mad_common.status here
corrupt the command structure for TMF events?
Since TMF events like ibmvfc_reset_device() don't have a scsi_cmnd, they
fall into this else block. However, they use struct ibmvfc_cmd rather than
struct ibmvfc_mad_common.
Because these share the ibmvfc_iu union:
drivers/scsi/ibmvscsi/ibmvfc.h:union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
...
struct ibmvfc_cmd cmd;
}
Writing to mad_common.status (offset 12) inadvertently modifies the
payload_len field of the cmd structure, leaving cmd.status (offset 24)
as 0. Callers then check cmd.status and might incorrectly assume the device
reset was successfully received.
> + evt->done = evt->_done;
[Severity: High]
Could this assignment result in executing an uninitialized function pointer?
When a Task Management Function (TMF) fails to send, such as in
ibmvfc_reset_device(), the event has evt->cmnd set to NULL but uses
IBMVFC_CMD_FORMAT.
During initialization:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_init_event() {
...
if (format == IBMVFC_CMD_FORMAT)
evt->done = done;
else {
evt->_done = done;
...
}
Because evt->_done is left uninitialized for IBMVFC_CMD_FORMAT, overwriting
evt->done with evt->_done in this error path could cause a kernel panic
when evt->done(evt) is subsequently called.
> + }
>
> evt->done(evt);
> } else {
[Severity: High]
This is a pre-existing issue, but is the return value of ibmvfc_send_event()
handled safely inside ibmvfc_cancel_all_mq()?
If h_send_crq() returns H_CLOSED, ibmvfc_send_event() immediately frees the
event and returns SCSI_MLQUEUE_HOST_BUSY:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_send_event() {
...
if (rc == H_CLOSED) {
...
ibmvfc_free_event(evt);
return SCSI_MLQUEUE_HOST_BUSY;
}
...
}
However, ibmvfc_cancel_all_mq() ignores the return value:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_cancel_all_mq() {
...
ibmvfc_send_event(evt, vhost, default_timeout);
list_add_tail(&evt->cancel, &cancelq);
...
wait_for_completion(&evt->comp);
...
}
Could this lead to linking a freed event into the active cancel list and
permanently waiting on its completion?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 20/33] ibmvfc: allocate targets based on protocol
2026-07-23 0:01 ` [PATCH v2 20/33] ibmvfc: allocate targets based on protocol Tyrel Datwyler
@ 2026-07-23 0:43 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:43 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] NVMe targets placed on `nvme_scrqs.targets` are completely ignored by the driver's state machine and teardown paths, resulting in memory leaks and non-functional targets.
Pre-existing issues:
- [High] Unexpected target state causes `ibmvfc_alloc_target` to fall through and allocate a duplicate target with the same WWPN.
--
--- Patch [20]: [PATCH v2 20/33] ibmvfc: allocate targets based on protocol ---
commit c353df4f5747ab4e038043d3b4bccacccf0aa149
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: allocate targets based on protocol
This commit modifies the target allocation logic to place discovered
targets onto either the SCSI or NVMe channel-group lists based on their
protocol. It keeps protocol-specific discovery results isolated for later
traversal.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index b479537e9e78c..92c286cfc69c6 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -5014,6 +5021,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
>
[Severity: High]
This is a pre-existing issue, but if a target has moved and its state is not
IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT, the code prints an "Unexpected target
state" error but fails to abort:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_alloc_target() {
if (wtgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
...
goto unlock_out;
} else {
tgt_err(wtgt, "Unexpected target state: %d, %p\n",
wtgt->action, wtgt->rport);
}
} else if (stgt) {
...
}
spin_unlock_irqrestore(vhost->host->host_lock, flags);
Because it lacks a "goto unlock_out;" or "return;" in the else branch, does
this execution fall through to the mempool_alloc() below and erroneously
allocate a duplicate target with the same WWPN?
> tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
> memset(tgt, 0, sizeof(*tgt));
> + tgt->protocol = protocol;
> tgt->scsi_id = scsi_id;
> tgt->wwpn = wwpn;
[ ... ]
> @@ -5042,7 +5050,11 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
> int i, rc;
>
> for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
> - rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
> + rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i],
> + vhost->scsi_scrqs.protocol);
> + for (i = 0; !rc && i < vhost->nvme_scrqs.num_targets; i++)
> + rc = ibmvfc_alloc_target(vhost, &vhost->nvme_scrqs.disc_buf[i],
> + vhost->nvme_scrqs.protocol);
[Severity: High]
When the hypervisor returns NVMe targets during discovery, they are allocated
and added to vhost->nvme_scrqs.targets here. Are these targets ever cleaned up?
Since the rest of the driver's state machine and teardown paths (like
ibmvfc_remove) currently only iterate over vhost->scsi_scrqs.targets, it
appears these NVMe targets might be completely ignored.
Could this cause a leak of the mempool objects and leave active timers
running that might trigger use-after-free bugs after module removal?
>
> return rc;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=20
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support
2026-07-23 0:01 ` [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support Tyrel Datwyler
@ 2026-07-23 0:50 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:50 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The target's protocol field is never initialized to IBMVFC_PROTO_NVME, causing the driver to send SCSI Query Target MADs even for NVMe targets.
--
--- Patch [19]: [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support ---
commit 8e5ca6c48a7f422d595d70e30b62d3daf80b7c82
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: add NVMe/FC Query Target support
This commit adds protocol-specific query-target support for NVMe/FC targets.
It uses the NVMe query-target specific MAD when querying an NVMe target and
updates the associated logging.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 5950f143e9a12..b479537e9e78c 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -4923,7 +4923,10 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
> query_tgt = &evt->iu.query_tgt;
> memset(query_tgt, 0, sizeof(*query_tgt));
> query_tgt->common.version = cpu_to_be32(1);
> - query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
> + if (tgt->protocol == IBMVFC_PROTO_SCSI)
> + query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
> + else
> + query_tgt->common.opcode = cpu_to_be32(IBMVFC_NVMF_QUERY_TARGET);
[Severity: High]
Is this NVMe branch reachable?
Looking at ibmvfc_alloc_target(), the target is allocated and zero-initialized
using memset(), but the protocol field is never assigned. Because
IBMVFC_PROTO_SCSI evaluates to 0, the target implicitly and permanently
becomes a SCSI target:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_alloc_target() {
...
tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
memset(tgt, 0, sizeof(*tgt));
tgt->scsi_id = scsi_id;
tgt->wwpn = wwpn;
tgt->vhost = vhost;
tgt->need_login = 1;
...
}
Does the protocol field need to be explicitly initialized to IBMVFC_PROTO_NVME
somewhere so that NVMe targets don't incorrectly send SCSI Query Target MADs?
> query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
> query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=19
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread
2026-07-23 0:01 ` [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread Tyrel Datwyler
@ 2026-07-23 0:50 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:50 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Uninitialized variable `rport` used in NVMe target cleanup loop, leading to random state machine stalls.
- [High] Data race on `tgt->nvme_remote_port` causes NVMe remote port memory leak and dangling pointer.
- [High] Use-After-Free of NVMe remote port during concurrent unregistration.
--
--- Patch [25]: [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread ---
commit 5ac14eea9ebcccbdd022d855ccc9eba7a87b95be
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: process NVMe/FC rports in work thread
This commit adds NVMe-specific remote-port handling to the rport worker thread.
It registers new NVMe remote ports, rescans existing ones, and manages remote
port deletion transitions within the same worker context used for SCSI targets.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 81d9229bf388a..ffb579816e847 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -5856,6 +5856,51 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
> fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
> }
>
> +/**
> + * ibmvfc_tgt_add_nvme_rport - Tell the FC transport about a new remote port
> + * @tgt: ibmvfc target struct
> + *
> + **/
> +static void ibmvfc_tgt_add_nvme_rport(struct ibmvfc_target *tgt)
> +{
> + struct ibmvfc_host *vhost = tgt->vhost;
> + struct nvme_fc_remote_port *rport;
> + unsigned long flags;
> +
> + tgt_dbg(tgt, "Adding NVMe rport\n");
> + ibmvfc_nvme_register_remoteport(tgt);
> + spin_lock_irqsave(vhost->host->host_lock, flags);
> + rport = tgt->nvme_remote_port;
> +
> + if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
> + tgt_dbg(tgt, "Deleting NVMe rport\n");
> + list_del(&tgt->queue);
> + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + ibmvfc_nvme_unregister_remoteport(tgt);
> + timer_delete_sync(&tgt->timer);
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> + return;
> + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
> + tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
> + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
> + tgt->init_retries = 0;
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + ibmvfc_nvme_unregister_remoteport(tgt);
> + return;
> + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
[Severity: High]
Does this sequence create a data race and memory leak of the NVMe remote port?
If ibmvfc_nvme_register_remoteport() updates tgt->nvme_remote_port without
holding vhost->host->host_lock:
CPU1 in ibmvfc_tgt_add_nvme_rport():
ibmvfc_nvme_register_remoteport(tgt);
CPU2 in ibmvfc_do_work():
spin_lock_irqsave(...)
nvme_rport = tgt->nvme_remote_port;
list_del(&tgt->queue);
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
spin_unlock_irqrestore(...)
// skips unregister because nvme_rport was read as NULL
Then CPU1 resumes in ibmvfc_tgt_add_nvme_rport():
spin_lock_irqsave(...)
if (... tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
spin_unlock_irqrestore(...)
return;
}
Could this permanently leak the port structure and leave a dangling pointer in
the NVMe transport by returning without unregistering the newly allocated port?
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + return;
> + }
> +
> + if (rport) {
> + tgt_dbg(tgt, "NVMe rport add succeeded\n");
> + tgt->target_id = tgt->nvme_remote_port->port_id;
> + } else
> + tgt_dbg(tgt, "NVMe rport add failed\n");
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> +}
[ ... ]
> @@ -6073,6 +6119,30 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
> }
> }
>
> + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
> + if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
> + tgt_dbg(tgt, "Deleteing NVMe rport\n");
> + nvme_rport = tgt->nvme_remote_port;
> + list_del(&tgt->queue);
> + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + if (nvme_rport)
> + ibmvfc_nvme_unregister_remoteport(tgt);
> + timer_delete_sync(&tgt->timer);
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> + return;
> + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
[Severity: High]
Is rport uninitialized or containing stale data here?
This loop iterates over nvme_scrqs.targets, but rport is the local variable
used for the SCSI targets loop earlier in the function. If the preceding SCSI
loop exits early or finds no targets, rport could evaluate to false based on
uninitialized stack memory.
Should this use nvme_rport instead of rport? Also, if the registration failed
and the port pointer is NULL, does gating this state transition on port
existence cause the target to stall in the state machine indefinitely?
> + tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
> + nvme_rport = tgt->nvme_remote_port;
> + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
> + tgt->init_retries = 0;
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + if (nvme_rport)
> + ibmvfc_nvme_unregister_remoteport(tgt);
> + return;
> + }
> + }
> +
[ ... ]
> @@ -6691,7 +6762,28 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
> break;
> }
> }
> - } while(did_work);
> +
> + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
> + if (tgt->add_rport) {
> + did_work = 1;
> + tgt->add_rport = 0;
> + kref_get(&tgt->kref);
> + nvme_rport = tgt->nvme_remote_port;
> + if (!nvme_rport) {
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + ibmvfc_tgt_add_nvme_rport(tgt);
> + } else {
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + if (IS_ENABLED(CONFIG_NVME_FC))
> + nvme_fc_rescan_remoteport(nvme_rport);
[Severity: High]
Can nvme_rport be freed while nvme_fc_rescan_remoteport() is running?
Once host_lock is dropped, a concurrent thread executing ibmvfc_do_work() could
transition the target to IBMVFC_TGT_ACTION_DEL_RPORT and call
ibmvfc_nvme_unregister_remoteport(), which frees the remote port.
The legacy SCSI code in this thread pins the port using get_device(&rport->dev)
before dropping the lock. Should a similar reference be taken for the NVMe
remote port before calling rescan?
> + }
> +
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> + spin_lock_irqsave(vhost->host->host_lock, flags);
> + break;
> + }
> + }
> + } while (did_work);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=25
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI
2026-07-23 0:01 ` [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI Tyrel Datwyler
@ 2026-07-23 0:52 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:52 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] NVMe targets marked for deletion are never processed by the host's worker thread, leading to a stalled state machine and memory leaks.
--
--- Patch [21]: [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI ---
commit a374ffc0e25726714a6472bd16a2cbadb3641666
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: delete NVMe/FC targets as well as SCSI
This commit extends target deletion paths to process NVMe targets as well
as SCSI targets. Link-down and host reinitialization flows are updated to
walk both the SCSI and NVMe target lists when marking targets for deletion
to ensure protocol-specific target state stays consistent.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 92c286cfc69c6..1185d72a8a734 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -714,6 +714,8 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
> scsi_block_requests(vhost->host);
> list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
> ibmvfc_del_tgt(tgt);
> + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
> + ibmvfc_del_tgt(tgt);
[Severity: High]
When ibmvfc_del_tgt() is called here for NVMe targets, it sets the target
action and wakes the host's background worker thread (via work_wait_q). Will
these NVMe targets actually get processed by the worker thread?
Looking at the worker thread execution in ibmvfc_do_work():
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_do_work() {
...
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
tgt->job_step(tgt);
break;
}
}
...
}
And similarly in the helper function:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_dev_logo_to_do() {
...
list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
...
}
Both functions strictly iterate over vhost->scsi_scrqs.targets and completely
ignore vhost->nvme_scrqs.targets. Does this leave the NVMe targets stranded
in a pending logout state, resulting in a stalled state machine and memory
leak for the target structures?
> ibmvfc_set_host_state(vhost, state);
> ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
> vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=21
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets
2026-07-23 0:01 ` [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets Tyrel Datwyler
@ 2026-07-23 0:53 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:53 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The state machine loops were only partially updated for NVMe targets, missing the terminal deletion loop in `ibmvfc_do_work` and the rport addition loop in `ibmvfc_rport_add_thread`.
--
--- Patch [22]: [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets ---
commit caac2c6b6503f6fc5585805d7ed766e74dd3cfa7
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: update state machine to process NVMe/FC targets
Update the host work loop and target state-machine helpers to process
NVMe targets in addition to SCSI targets.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index 1185d72a8a734..ff84ca0f9f45a 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> @@ -5988,6 +6018,12 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
> break;
> }
> }
> + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
> + if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
> + tgt->job_step(tgt);
> + break;
> + }
> + }
>
> if (ibmvfc_dev_logo_to_do(vhost)) {
> spin_unlock_irqrestore(vhost->host->host_lock, flags);
[Severity: High]
Does this code introduce a regression by missing the equivalent terminal
deletion loop for NVMe targets?
While this block handles IBMVFC_TGT_ACTION_LOGOUT_RPORT for nvme_scrqs.targets,
shortly after this in ibmvfc_do_work() there is a loop for SCSI terminal
deletion (handling IBMVFC_TGT_ACTION_DEL_RPORT and
IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) that doesn't appear to be updated to
include vhost->nvme_scrqs.targets.
If that loop is omitted, won't NVMe targets marked for deletion remain in the
list indefinitely, failing to cancel their timers and leaking the target memory?
Additionally, looking at ibmvfc_rport_add_thread(), it only iterates over
vhost->scsi_scrqs.targets. Should NVMe targets that have tgt->add_rport = 1
also be processed there? If they are ignored by the worker thread, it appears
they will never be passed to ibmvfc_nvme_register_remoteport(), preventing the
NVMe targets from registering with the transport.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=22
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login
2026-07-23 0:01 ` [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login Tyrel Datwyler
@ 2026-07-23 0:53 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:53 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Blocking operations (GFP_KERNEL allocations and wait_for_completion) are executed from atomic context, causing a system panic.
- [High] An unconditional `put_device()` on the `ibmvfc_nvme_unregister()` failure path without clearing the local port pointer leads to double-puts and use-after-free.
- [High] The NVMe local port and device reference are not cleaned up during driver removal, leading to a resource leak and potential use-after-free.
- [High] Re-initializing the host without an explicit adapter reset leaks the active `nvme_local_port` and overwrites its pointer with `NULL`, breaking NVMe functionality.
--
--- Patch [24]: [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login ---
commit 4eb644adc42ffc19ca6cdbdf0f3e3185c26ee9b7
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: register local nvme fc port after fabric login
This patch delays registering the local nvme fc port until after the fabric
login completes, allowing the client port ID to be populated from the fabric
login response.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> @@ -5210,6 +5210,8 @@ 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);
[Severity: Critical]
Can this sequence safely execute under the host lock?
The completion handler ibmvfc_fabric_login_nvme_done() is invoked via
the ibmvfc_locked_done() wrapper, which acquires the vhost->host->host_lock
spinlock and disables interrupts.
However, ibmvfc_nvme_register() calls into nvme_fc_register_localport()
which performs memory allocations using GFP_KERNEL. Could this lead to a
scheduling while atomic regression?
[Severity: High]
What happens to the local port during a fabric link bounce?
If the link bounces, ibmvfc_link_down() deletes targets but doesn't appear
to unregister the local port. When the adapter re-initializes and completes
fabric login, this will unconditionally call ibmvfc_nvme_register() again.
If nvme_fc_register_localport() detects the existing port and returns
-EEXIST, its error path sets the portptr to NULL.
Could this leak the original local port and prevent nvme from functioning
until the driver is reloaded?
> ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
> break;
[ ... ]
> @@ -5653,6 +5656,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
> case IBMVFC_MAD_SUCCESS:
> if (list_empty(&vhost->crq.sent) &&
> vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
> + ibmvfc_nvme_unregister(vhost);
[Severity: Critical]
Is it safe to wait for completion here?
Similar to the registration path, ibmvfc_npiv_logout_done() executes under
the host_lock spinlock with interrupts disabled.
ibmvfc_nvme_unregister() calls wait_for_completion() which sleeps. Can
blocking here while holding the spinlock cause a scheduling while atomic
regression?
> ibmvfc_init_host(vhost);
> return;
> }
[ ... ]
> @@ -5931,6 +5935,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
> list_splice_init(&vhost->purge, &purge);
> spin_unlock_irqrestore(vhost->host->host_lock, flags);
> ibmvfc_complete_purge(&purge);
> + ibmvfc_nvme_unregister(vhost);
[Severity: High]
Should the driver removal path also unregister the nvme local port?
During device unbind or module removal, ibmvfc_remove() tears down the
adapter but doesn't seem to call ibmvfc_nvme_unregister().
If the port remains registered with the nvme fc transport layer, its
private pointer still points to the vhost structure. After vhost is freed,
could subsequent transport accesses lead to a use-after-free, while also
leaking the device reference acquired during registration?
> rc = ibmvfc_reset_crq(vhost);
>
> spin_lock_irqsave(vhost->host->host_lock, flags);
[ ... ]
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> @@ -165,9 +168,17 @@ void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
> return;
>
> if (vhost->nvme_local_port) {
> + ibmvfc_log(vhost, 2, "unregister_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
> + vhost->nvme_local_port->node_name,
> + vhost->nvme_local_port->port_name,
> + vhost->nvme_local_port->port_id);
> init_completion(&vhost->nvme_delete_done);
> rc = nvme_fc_unregister_localport(vhost->nvme_local_port);
> - if (!rc)
> + if (!rc) {
> wait_for_completion(&vhost->nvme_delete_done);
> + } else
> + dev_err(vhost->dev, "Failed to unregister NVMe fc localport (%d)\n", rc);
> +
> + put_device(vhost->dev);
[Severity: High]
If nvme_fc_unregister_localport() fails, vhost->nvme_local_port is not
set to NULL, but put_device() is still called unconditionally.
If ibmvfc_nvme_unregister() is called again later, such as during a
subsequent adapter reset loop, will it attempt to unregister the same port
and drop the device reference a second time? Could this sequence result
in a use-after-free on the device structure?
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=24
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration
2026-07-23 0:01 ` [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration Tyrel Datwyler
@ 2026-07-23 0:54 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:54 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Overwriting the NVMe-FC transport's pre-allocated `private` pointer in port registration functions causes API misuse and out-of-bounds memory corruption.
- [Medium] The NVMe-FC transport stubs (`ibmvfc_nvme_ls_req` and `ibmvfc_nvme_fcp_io`) return 0 (success) but drop requests without invoking the mandatory asynchronous `done` callbacks, causing uninterruptible hangs.
--
--- Patch [23]: [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration ---
commit a018152524c65e5db110e234e12e5b6d56bbe966
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement NVMe/FC stubs for local/remote port registration
Implement the initial NVMe/FC local-port and remote-port registration
functions that notify the NVMe-FC midlayr of port discovery and loss.
Register the local port with the NVMe-FC transport, register discovered
remote ports against that local port, and add matching unregister paths
that wait for the NVMe-FC core to complete asynchronous deletion before
dropping driver references. Also store driver-private host and target
pointers in the registered NVMe-FC port objects.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index 2ee1c64665d9c..d679d1ca366a8 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> @@ -14,10 +14,18 @@
>
> static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
> {
> + struct ibmvfc_host *vhost = lport->private;
> +
> + vhost->nvme_local_port = NULL;
> + complete(&vhost->nvme_delete_done);
> }
>
> static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
> {
> + struct ibmvfc_target *tgt = rport->private;
> +
> + tgt->nvme_remote_port = NULL;
> + complete(&tgt->nvme_delete_done);
> }
[Severity: Medium]
If a concurrent teardown unregisters the port before the private pointer
is overwritten during registration, could rport->private still point to the
8-byte inline buffer allocated by the NVMe-FC core?
If so, won't casting this 8-byte buffer to a much larger struct ibmvfc_target
and writing to tgt->nvme_remote_port cause an out-of-bounds write and memory
corruption?
>
> static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
[Severity: Medium]
Do the NVMe-FC transport stubs (ibmvfc_nvme_ls_req and ibmvfc_nvme_fcp_io)
return 0 without invoking the mandatory asynchronous done callbacks?
If these functions return 0 (success) but drop requests without calling
ls_req->done or fcp_req->done, won't this leave the NVMe-FC core waiting
forever, potentially causing an uninterruptible hang?
[ ... ]
> @@ -70,28 +78,96 @@ 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_port_info pinfo;
> + int rc;
> +
> if (!IS_ENABLED(CONFIG_NVME_FC))
> return 0;
>
> - return 0;
> + if (!vhost->nvme_local_port) {
> + dev_err(vhost->dev, "Attempt to register NVMe fc remoteport without valid localport\n");
> + return -EINVAL;
> + }
> +
> + memset(&pinfo, 0, sizeof(struct nvme_fc_port_info));
> + pinfo.node_name = tgt->ids.node_name;
> + pinfo.port_name = tgt->ids.port_name;
> + 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);
> +
> + 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;
[Severity: Medium]
Does directly overwriting the nvme_remote_port->private pointer orphan the
inline buffer allocated by the NVMe-FC core?
Since the NVMe-FC core allocates an 8-byte inline buffer for private data
based on remote_priv_sz, shouldn't the data be copied into the allocated
buffer rather than overwriting the pointer entirely?
> + }
> +
> + return rc;
> }
[ ... ]
> int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
> {
> + struct nvme_fc_port_info pinfo;
> + int rc;
> +
> if (!IS_ENABLED(CONFIG_NVME_FC))
> return 0;
>
> - return 0;
> + 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);
> + pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
> + pinfo.dev_loss_tmo = 0;
> +
> + rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport,
> + vhost->dev, &vhost->nvme_local_port);
> +
> + if (!rc) {
> + ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
> + pinfo.node_name, pinfo.port_name, pinfo.port_id);
> + vhost->nvme_local_port->private = vhost;
[Severity: Medium]
Similar to the remote port registration, does overwriting the pre-allocated
nvme_local_port->private pointer here orphan the core's inline buffer
allocation?
> + } else
> + dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc);
> +
> + return rc;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=23
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues
2026-07-23 0:01 ` [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues Tyrel Datwyler
@ 2026-07-23 0:58 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:58 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Missing `nvme_fc_unregister_localport` during adapter teardown causes Use-After-Free.
- [High] TOCTOU race in `ibmvfc_nvme_create_queue` leading to divide-by-zero and NULL pointer dereference.
- [High] Stub implementations of `ls_req` and `fcp_io` return 0 without invoking completion callbacks, causing uninterruptible kernel hangs.
--
--- Patch [28]: [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues ---
commit 186f72e380294760659d725fac77194c4e72d82e
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement LLDD callbacks for mapping nvme-fc queues
Implement the NVMe-FC queue create and delete callbacks and map NVMe
controller queues onto ibmvfc hardware queues. Use qidx of NVMe controller
queue to map onto a ibmvfc_queue channel. Add a new ibmvfc_nvme_qhandle
struct that will be used as the opaque queue handle by the NVMe-FC layer
when issuing fcp IO.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index 637d7d7015cf5..ecf9f4c0549c2 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
[ ... ]
> +static int ibmvfc_nvme_create_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
> + u16 qsize, void **handle)
> +{
> + struct ibmvfc_host *vhost = lport->private;
> + struct ibmvfc_nvme_qhandle *qhandle;
> +
> + if (!vhost->nvme_scrqs.active_queues)
> + return -ENODEV;
> +
> + qhandle = kzalloc_obj(struct ibmvfc_nvme_qhandle);
> + if (!qhandle)
> + return -ENOMEM;
> +
> + qhandle->cpu_id = raw_smp_processor_id();
> + qhandle->qidx = qidx;
> +
> + /* Admin and first IO queue are both mapped to index 0 */
> + if (qidx)
> + qhandle->index = (qidx - 1) % vhost->nvme_scrqs.active_queues;
[Severity: High]
Could this result in a divide-by-zero or NULL pointer dereference?
The vhost->nvme_scrqs.active_queues value is read locklessly at the start of
the function. Since kzalloc_obj() can sleep, if a concurrent teardown or reset
(such as ibmvfc_release_channels()) executes during this window, it could set
active_queues to 0 and scrqs to NULL.
When the thread resumes, it would divide by zero here.
> + else
> + qhandle->index = qidx;
> +
> + qhandle->queue = &vhost->nvme_scrqs.scrqs[qhandle->index];
[Severity: High]
Will this lead to a use-after-free during adapter teardown?
When unloading the driver or hot-removing the adapter, ibmvfc_remove() frees
the memory backing vhost->nvme_scrqs.scrqs via ibmvfc_release_sub_crqs().
However, it appears nvme_fc_unregister_localport() is never called during this
teardown path. Without notifying the NVMe-FC transport layer, it retains active
references to the local port and its queues. Any subsequent background transport
operations could dereference the freed queues.
[ ... ]
> @@ -59,8 +93,8 @@ static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
> static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
> .localport_delete = ibmvfc_nvme_localport_delete,
> .remoteport_delete = ibmvfc_nvme_remoteport_delete,
> - .create_queue = NULL,
> - .delete_queue = NULL,
> + .create_queue = ibmvfc_nvme_create_queue,
> + .delete_queue = ibmvfc_nvme_delete_queue,
> .ls_req = ibmvfc_nvme_ls_req,
> .ls_abort = ibmvfc_nvme_ls_abort,
> .fcp_io = ibmvfc_nvme_fcp_io,
[Severity: High]
Does returning 0 from the stub implementations of ibmvfc_nvme_ls_req() and
ibmvfc_nvme_fcp_io() cause uninterruptible kernel hangs?
The NVMe-FC transport layer calls the LLDD's ls_req and fcp_io functions.
Returning 0 signals that the request was successfully dispatched and the
LLDD will asynchronously invoke the req->done() callback.
For example, __nvme_fc_send_ls_req() issues a synchronous wait_for_completion()
with no timeout. Because the stub functions simply return 0 and never call
done(), the caller would block forever, leaving block layer I/Os permanently
outstanding.
If these are meant to be stubs, should they return an error code like
-EOPNOTSUPP or -EIO instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=28
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset
2026-07-23 0:01 ` [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset Tyrel Datwyler
@ 2026-07-23 0:58 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 0:58 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Critical] The local variable `shwqs` is used uninitialized in `ibmvfc_purge_requests` when `vhost->using_channels` is false, leading to undefined behavior, out-of-bounds array access, and potential kernel crashes.
Pre-existing issues:
- [High] Dereferencing an uninitialized or stale `tgt` pointer during NVMe FCP aborts because `evt->tgt` is never initialized for FCP requests.
- [High] NVMe FC IO requests return a SCSI-specific busy code (`SCSI_MLQUEUE_HOST_BUSY`) when the CRQ is closed, causing immediate IO failures instead of retries.
--
--- Patch [33]: [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset ---
commit ffc022564293825c73fe8e34bd53f9d7f153f0e8
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset
The current purge code for flushing outstanding commands during a
transport reset only deals with SCSI commands. Rename the
ibmvfc_scsi_eh_done completion handler to ibmvfc_vfc_eh_done and wire it
to correctly complete nvme fcp and ls commands when flushing the
inflight command list during a reset.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
> index bc975f632da21..dfa231b6cc478 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
[ ... ]
> -static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
> +static void ibmvfc_vfc_eh_done(struct ibmvfc_event *evt)
> {
> struct scsi_cmnd *cmnd = evt->cmnd;
> + struct nvmefc_ls_req *ls_req = evt->ls_req;
> + struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
[Severity: High]
This is a pre-existing issue, but does evt->tgt remain uninitialized for
FCP requests?
When an NVMe FCP command is submitted via ibmvfc_nvme_fcp_io(), evt->tgt
is not fully initialized.
If an abort occurs, ibmvfc_init_fcp_abort() reads the uninitialized
or stale pointer from abt_evt->tgt:
ibmvfc_init_fcp_abort()
struct ibmvfc_event *abt_evt = abort_req->private;
struct ibmvfc_target *tgt = abt_evt->tgt;
...
tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
Could this lead to a null pointer dereference or use-after-free crash
when reading tgt->wwpn during standard NVMe timeout and abort handling?
> if (cmnd) {
> scsi_dma_unmap(cmnd);
> scsi_done(cmnd);
> + } else if (fcp_req) {
> + fcp_req->rcv_rsplen = 0;
[ ... ]
> @@ -1164,13 +1176,16 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
> static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
> {
> struct ibmvfc_event *evt, *pos;
> - struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
> + struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs;
> + struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs;
> unsigned long flags;
> - int hwqs = 0;
> + int shwqs, nhwqs = 0;
> int i;
>
> - if (vhost->using_channels)
> - hwqs = vhost->scsi_scrqs.active_queues;
> + if (vhost->using_channels) {
> + shwqs = vhost->scsi_scrqs.active_queues;
> + nhwqs = vhost->nvme_scrqs.active_queues;
> + }
[ ... ]
> - for (i = 0; i < hwqs; i++) {
> - spin_lock_irqsave(queues[i].q_lock, flags);
> - spin_lock(&queues[i].l_lock);
> - list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
> + for (i = 0; i < shwqs; i++) {
[Severity: Critical]
Does this leave shwqs uninitialized when vhost->using_channels is false?
The declaration 'int shwqs, nhwqs = 0;' only initializes nhwqs to zero.
If a transport reset occurs on a host without channel support, the loop
condition checking against shwqs might use uninitialized memory. Can this
cause an out-of-bounds access on the scsi_q array and result in a kernel
panic?
> + spin_lock_irqsave(scsi_q[i].q_lock, flags);
> + spin_lock(&scsi_q[i].l_lock);
> + list_for_each_entry_safe(evt, pos, &scsi_q[i].sent, queue_list)
> ibmvfc_fail_request(evt, error_code);
> - list_splice_init(&queues[i].sent, &vhost->purge);
> - spin_unlock(&queues[i].l_lock);
> - spin_unlock_irqrestore(queues[i].q_lock, flags);
> + list_splice_init(&scsi_q[i].sent, &vhost->purge);
> + spin_unlock(&scsi_q[i].l_lock);
> + spin_unlock_irqrestore(scsi_q[i].q_lock, flags);
> + }
[ ... ]
> @@ -1818,7 +1843,9 @@ int ibmvfc_send_event(struct ibmvfc_event *evt,
[Severity: High]
This is a pre-existing issue, but earlier in ibmvfc_send_event() the code
unconditionally returns SCSI_MLQUEUE_HOST_BUSY when the send_crq error
code is H_CLOSED:
ibmvfc_send_event()
if (rc == H_CLOSED) {
...
return SCSI_MLQUEUE_HOST_BUSY;
}
Will this cause the NVMe FC transport layer to interpret the non-EBUSY
return value as a hard failure?
The NVMe transport might immediately fail the IO instead of gracefully
retrying it during transient transport disruptions like VIOS restarts or
Live Partition Mobility, potentially leading to filesystem corruption.
> dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
> if (evt->cmnd) {
> evt->cmnd->result = DID_ERROR << 16;
> - evt->done = ibmvfc_scsi_eh_done;
> + evt->done = ibmvfc_vfc_eh_done;
> + } else if (evt->fcp_req || evt->ls_req) {
> + evt->done = ibmvfc_vfc_eh_done;
> } else {
> evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
> evt->done = evt->_done;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=33
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback
2026-07-23 0:01 ` [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback Tyrel Datwyler
@ 2026-07-23 1:00 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 1:00 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] timer_delete is used instead of timer_delete_sync during event purging, leading to a Use-After-Free in the timer callback during driver unload.
--
--- Patch [29]: [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback ---
commit 307222be453ed384bc1c2ec92213263c1df79a6a
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement nvme-fc LS submission transport callback
NVMe FC Link Service commands are required to use the ibmvfc_passthru MAD.
This patch initializes a passthru mad for the target port and sends FC4_LS
commands via the primary CRQ.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index ecf9f4c0549c2..cf72f0682297b 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
[ ... ]
> @@ -62,10 +62,99 @@ static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned
[ ... ]
> static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
> struct nvme_fc_remote_port *rport,
> struct nvmefc_ls_req *ls_req)
> {
> + struct ibmvfc_host *vhost = lport->private;
> + struct ibmvfc_target *tgt = rport->private;
> + struct ibmvfc_passthru_mad *mad;
> + struct ibmvfc_event *evt;
[ ... ]
> + ibmvfc_dbg(vhost, "nvme_ls_req\n");
> + if (ibmvfc_send_event(evt, vhost, IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT)) {
[Severity: High]
This isn't a bug introduced by this patch, but calling ibmvfc_send_event() sets
up a timer for the event.
During driver removal, the driver calls ibmvfc_purge_requests(), which fails
pending commands and cancels their timers using the non-synchronous
timer_delete():
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_fail_request() {
...
timer_delete(&evt->timer);
...
}
If a timer is already executing concurrently on another CPU, timer_delete()
returns immediately without waiting for it to finish. The driver unload sequence
then proceeds to call ibmvfc_free_mem(), which completely frees the event pool.
Meanwhile, the still-executing ibmvfc_timeout callback dereferences the freed
evt structure:
drivers/scsi/ibmvscsi/ibmvfc-core.c:ibmvfc_timeout() {
struct ibmvfc_event *evt = container_of(timer, struct ibmvfc_event, timer);
struct ibmvfc_host *vhost = evt->vhost;
...
}
Can this lead to a use-after-free during driver unload if there are pending
requests that time out concurrently? Should the teardown path use
timer_delete_sync() to wait for executing timers to finish before freeing the
event pool?
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> + return -ENXIO;
> + }
> +
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=29
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback
2026-07-23 0:01 ` [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback Tyrel Datwyler
@ 2026-07-23 1:05 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 1:05 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Concurrent completion of an NVMe FC-LS request causes a Use-After-Free of the event structure and incorrect cancellation of an unrelated command.
- [High] Inverted capability check sets the IBMVFC_TMF_SUPPRESS_ABTS flag only when the adapter lacks the capability.
- [High] The CRQ event structure is leaked from its fixed-size pool if the host is not logged in when an abort is issued.
- [Low] The local variable `status` is used uninitialized if `ibmvfc_send_event()` fails, leading to uninitialized stack memory being logged.
--
--- Patch [31]: [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback ---
commit 508e0371279894532111408d7a22f80e0afd86a5
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement nvme-fc LS abort handling callback
Implement the NVMe FC-LS abort callback by issuing an ibmvfc cancel MAD
to the VIOS for the outstanding link-service request.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index d31e7e9ea7a9e..390410e44f0d8 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
[ ... ]
> @@ -159,10 +161,72 @@ static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
> return 0;
> }
>
> +static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *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;
[Severity: High]
Can abt_evt be freed concurrently if the original LS request receives a
hardware completion?
If a natural completion races with the abort timeout, ibmvfc_ls_req_done()
could free the event structure back to the active pool via
ibmvfc_free_event(). Does this blind dereference risk a use-after-free or
corrupting a newly allocated command?
> + struct ibmvfc_host *vhost = evt->vhost;
> +
> + tmf = &evt->iu.tmf;
> + memset(tmf, 0, sizeof(*tmf));
> + tmf->common.version = cpu_to_be32(2);
> + tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
> + 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))
> + tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS);
[Severity: High]
Is this capability check logically inverted?
It appears to set the IBMVFC_TMF_SUPPRESS_ABTS flag only when the adapter
lacks support for it, which might cause the firmware to reject the abort
TMF MAD.
> + 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);
> +
> + init_completion(&evt->comp);
> +}
> +
> +static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
> + struct nvme_fc_remote_port *rport,
> + struct nvmefc_ls_req *ls_abort)
> +{
> + struct ibmvfc_host *vhost = lport->private;
> + struct ibmvfc_target *tgt = rport->private;
> + struct ibmvfc_event *evt;
> + union ibmvfc_iu rsp;
> + unsigned long flags;
> + u16 status;
> +
> + evt = ibmvfc_get_event(&vhost->crq);
> + if (!vhost->logged_in || !evt)
> + return;
[Severity: High]
Does this code leak the allocated event structure if the host is not
logged in?
If evt is successfully allocated from the fixed-size pool but
vhost->logged_in is false, returning here without calling
ibmvfc_free_event() would permanently lose the event. Repeated
occurrences could exhaust the pool.
> +
> + 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);
> +
> + 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:
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> + ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
[Severity: Low]
Is the local variable status used uninitialized here?
If ibmvfc_send_event() fails, the code jumps to out, skipping the
assignment to status. The uninitialized stack value would then be
printed to the debug log.
> + kref_put(&tgt->kref, ibmvfc_release_tgt);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=31
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback
2026-07-23 0:01 ` [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback Tyrel Datwyler
@ 2026-07-23 1:05 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 1:05 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Lockless access to the original event in the FCP abort handler leads to Use-After-Free and spurious aborts of innocent I/O requests.
- [Critical] Dereferencing the uninitialized `abt_evt->tgt` pointer in `ibmvfc_init_fcp_abort` will cause a guaranteed kernel panic.
- [High] The `fcp_abort` callback blocks synchronously waiting for the abort MAD to complete on the wire, stalling the block layer's timeout workqueue.
- [High] FCP abort hangs forever if the adapter resets while the abort MAD is in-flight because NVMe queues are not purged.
--
--- Patch [32]: [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback ---
commit b7d2ac0c1d18f363d56507d99dd9acbcd4cd3058
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement nvme-fc FCP abort callback
Implement the NVMe-FC FCP abort callback by issuing an NVMF cancel MAD
on the same submission queue used by the original FCP request.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index 390410e44f0d8..54f80b6265d45 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> @@ -374,11 +374,75 @@ static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
> return rc;
> }
>
> +static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt,
> + struct nvmefc_fcp_req *abort_req)
> +{
> + struct ibmvfc_tmf *tmf;
> + struct ibmvfc_event *abt_evt = abort_req->private;
> + struct ibmvfc_target *tgt = abt_evt->tgt;
> +
> + tmf = &evt->iu.tmf;
> + memset(tmf, 0, sizeof(*tmf));
> + tmf->common.version = cpu_to_be32(2);
> + tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
> + tmf->common.length = cpu_to_be16(sizeof(*tmf));
> + tmf->flags = cpu_to_be32(IBMVFC_TMF_ABORT_TASK | IBMVFC_TMF_NVMF_ASSOC);
> + tmf->cancel_key = cpu_to_be32((u64)abt_evt);
> + tmf->my_cancel_key = cpu_to_be32((u64)evt);
> + tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
[Severity: Critical]
Does this dereference an uninitialized pointer?
When an FCP IO is submitted via ibmvfc_nvme_fcp_io(), the evt->tgt field is
never populated.
When this IO times out, the code above extracts abt_evt->tgt and immediately
dereferences it to assign tmf->target_wwpn.
Since the correct target pointer is passed into fcp_abort as rport->private,
should that be used here instead to avoid a panic?
> + 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(struct nvme_fc_local_port *lport,
> + struct nvme_fc_remote_port *rport,
> + void *hw_queue_handle,
> + struct nvmefc_fcp_req *abort_req)
> +{
> + struct ibmvfc_host *vhost = lport->private;
> + struct ibmvfc_target *tgt = rport->private;
> + struct ibmvfc_event *evt, *abt_evt = abort_req->private;
[Severity: Critical]
Can accessing abort_req->private locklessly race with a hardware completion?
If an IO completes in hardware at the same time as a block layer timeout,
ibmvfc_nvme_done() can unconditionally free the event:
ibmvfc_nvme_done()
ibmvfc_free_event(evt);
If this event is immediately reallocated for a new IO request on another CPU,
would using this reused event as the task_tag and cancel_key cause the VIOS
to abort the newly allocated, unrelated IO?
> + struct ibmvfc_queue *queue;
> + union ibmvfc_iu rsp;
> + unsigned long flags;
> + u16 status = 0;
> +
> + if (!abt_evt)
> + return;
[ ... ]
> + spin_unlock_irqrestore(queue->q_lock, flags);
> +
> + wait_for_completion(&evt->comp);
[Severity: High]
Does waiting synchronously here stall the blk-mq timeout worker thread?
The fcp_abort callback executes in the timeout workqueue. By waiting
synchronously for the MAD to complete on the wire, it seems this could stall
the timeout worker for up to default_timeout seconds, preventing timeout
handling for all other requests.
[Severity: High]
Could this wait_for_completion() block indefinitely during an adapter reset?
If the abort MAD times out, ibmvfc_timeout() resets the host and calls
ibmvfc_purge_requests() to fail all pending IOs.
However, the purge logic only loops over SCSI queues:
ibmvfc_purge_requests() {
struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
...
for (i = 0; i < hwqs; i++) {
list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
...
}
Since the NVMe queues (vhost->nvme_scrqs.scrqs) are completely ignored,
the NVMe MADs are left hanging. Will this cause this completion to never
be signaled?
> + 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);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=32
^ permalink raw reply [flat|nested] 62+ messages in thread
* Re: [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback
2026-07-23 0:01 ` [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback Tyrel Datwyler
@ 2026-07-23 1:08 ` sashiko-bot
0 siblings, 0 replies; 62+ messages in thread
From: sashiko-bot @ 2026-07-23 1:08 UTC (permalink / raw)
To: Tyrel Datwyler; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] NVMe FCP requests are incorrectly processed as MAD requests in error and purge paths, causing uninitialized pointer dereferences and silent data corruption.
- [Critical] Heap buffer overflow in scatterlist mapping due to mismatch between driver-advertised maximum segments and the DMA pool block size.
- [High] The driver passes uninitialized or stale NVMe Completion Queue Entries (CQE) to the NVMe-FC transport when the adapter skips the ERSP DMA.
- [High] `ibmvfc_nvme_fcp_io` propagates SCSI midlayer error codes directly to the NVMe-FC core, causing NVMe I/O requests to permanently fail instead of being retried.
- [Medium] Truncating a 64-bit pointer to a 32-bit `cancel_key` risks collisions across multiple command queues.
--
--- Patch [30]: [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback ---
commit 6afa68277a7b61e829a727b116180c19195e096c
Author: Tyrel Datwyler <tyreld@linux.ibm.com>
ibmvfc: implement nvme-fc IO command submission callback
This commit implements the NVMe-FC IO command submission callback for the
ibmvfc driver. It adds helpers to initialize commands, map scatterlists,
and submit the requests to the hardware queue.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> index cf72f0682297b..d31e7e9ea7a9e 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
[ ... ]
> +static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
> +{
> + struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
> + struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
> + struct nvme_fc_ersp_iu *ersp = (struct nvme_fc_ersp_iu *)fcp_req->rspaddr;
> + struct nvme_completion *cqe = &ersp->cqe;
> + struct nvme_command *sqe = &((struct nvme_fc_cmd_iu *)fcp_req->cmdaddr)->sqe;
> +
> + ibmvfc_dbg(evt->vhost, "fc_done: (%x:%x)\n", be16_to_cpu(vfc_cmd->status),
> + be16_to_cpu(vfc_cmd->error));
> + ibmvfc_dbg(evt->vhost, "fc_done: cmdlen: %d, rsplen %d, payload_len %d\n",
> + fcp_req->cmdlen, fcp_req->rsplen, fcp_req->payload_length);
> +
> + fcp_req->status = 0;
> + if (!vfc_cmd->status) {
> + fcp_req->rcv_rsplen = NVME_FC_SIZEOF_ZEROS_RSP;
> + fcp_req->transferred_length = fcp_req->payload_length;
> + } else if (be16_to_cpu(vfc_cmd->status) & IBMVFC_FC_NVME_STATUS) {
> + fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu);
> + fcp_req->transferred_length = be32_to_cpu(ersp->xfrd_len);
> + if (be16_to_cpu(vfc_cmd->error) & IBMVFC_NVMS_VALID_NODMA_CQE)
> + cqe->command_id = sqe->common.command_id;
[Severity: High]
Does this pass uninitialized or stale NVMe Completion Queue Entries (CQE) to
the NVMe-FC transport?
If the adapter skips the ERSP DMA (indicated by IBMVFC_NVMS_VALID_NODMA_CQE),
this forces the NVMe core to process the response buffer by setting
fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu). Since the host memory at
fcp_req->rspaddr is recycled and not zeroed by the NVMe core, could this cause
the NVMe core to process stale cqe->status and cqe->result data without
verifying that IBMVFC_NVMS_VALID_ERSP was set?
> + } else {
> + fcp_req->rcv_rsplen = 0;
> + fcp_req->transferred_length = 0;
> + fcp_req->status = NVME_SC_INTERNAL;
> + }
> +
> + fcp_req->done(fcp_req);
> + ibmvfc_free_event(evt);
> +}
> +
> +static struct ibmvfc_cmd *ibmvfc_nvme_init_vfc_cmd(struct ibmvfc_event *evt,
> + struct nvme_fc_remote_port *rport,
> + struct nvmefc_fcp_req *fcp_req)
> +{
> + struct ibmvfc_target *tgt = rport->private;
> + struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
> +
> + memset(vfc_cmd, 0, sizeof(*vfc_cmd));
> +
> + vfc_cmd->resp.va = cpu_to_be64(fcp_req->rspdma);
> + vfc_cmd->resp.len = cpu_to_be32(fcp_req->rsplen);
> + vfc_cmd->frame_type = cpu_to_be32(IBMVFC_NVME_FCP_TYPE);
> + vfc_cmd->flags |= cpu_to_be16(IBMVFC_NVMEOF_PROTOCOL);
> + vfc_cmd->payload_len = cpu_to_be32(fcp_req->cmdlen);
> + vfc_cmd->resp_len = cpu_to_be32(fcp_req->rsplen);
> + vfc_cmd->cancel_key = cpu_to_be32((u64)evt);
[Severity: Medium]
Does truncating the 64-bit evt pointer to a 32-bit cancel_key risk collisions?
Since events are allocated from per-queue DMA pools, is it possible for two
different event queues to be mapped at virtual memory addresses separated by
exactly 4GB? This would make their lower 32 bits identical, risking duplicate
cancel keys across multiple command queues and potentially aborting the wrong
command.
> + vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
> + vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
> + vfc_cmd->assoc_id = cpu_to_be64(tgt->assoc_id);
> +
> + memcpy(&vfc_cmd->v3nvme.iu, fcp_req->cmdaddr, fcp_req->cmdlen);
> +
> + return vfc_cmd;
> +}
[ ... ]
> +static int ibmvfc_nvme_map_sg_data(struct nvmefc_fcp_req *fcp_req,
> + struct ibmvfc_event *evt,
> + struct ibmvfc_cmd *vfc_cmd)
> +{
> + struct srp_direct_buf *data = &vfc_cmd->ioba;
> + struct ibmvfc_host *vhost = evt->vhost;
> +
> + if (!fcp_req->sg_cnt) {
> + vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
> + return 0;
> + }
> +
> + if (fcp_req->io_dir == NVMEFC_FCP_WRITE)
> + vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
> + else
> + vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
> +
> + if (fcp_req->sg_cnt == 1) {
> + ibmvfc_nvme_map_sg_list(fcp_req, data);
> + return 0;
> + }
> +
> + vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
> +
> + if (!evt->ext_list) {
> + evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
> + &evt->ext_list_token);
[Severity: Critical]
Can this overflow the DMA pool buffer?
The vhost->sg_pool appears to hold a maximum of 128 items, but if the
transport advertises support for up to 1024 scatterlist segments
(.max_sgl_segments = 1024), ibmvfc_nvme_map_sg_list() will iterate up to 1024
times. If fcp_req->sg_cnt exceeds 128, won't this write past the end of the
allocated buffer and corrupt adjacent memory?
> +
> + if (!evt->ext_list)
> + return -ENOMEM;
> + }
> +
> + ibmvfc_nvme_map_sg_list(fcp_req, evt->ext_list);
> +
> + data->va = cpu_to_be64(evt->ext_list_token);
> + data->len = cpu_to_be32(fcp_req->sg_cnt * sizeof(struct srp_direct_buf));
> + data->key = 0;
> + return 0;
> +}
> +
> static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
> struct nvme_fc_remote_port *rport,
> void *hw_queue_handle,
> struct nvmefc_fcp_req *fcp_req)
> {
> - return 0;
> + struct ibmvfc_host *vhost = lport->private;
> + struct ibmvfc_nvme_qhandle *qhandle = hw_queue_handle;
> + struct ibmvfc_cmd *vfc_cmd;
> + struct ibmvfc_event *evt;
> + int rc;
> +
> + ibmvfc_dbg(vhost, "nvme_fcp_io\n");
> + evt = ibmvfc_get_event(qhandle->queue);
> + if (!evt)
> + return -EBUSY;
> +
> + evt->hwq = qhandle->index;
> + ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq);
> +
> + ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT);
> + evt->fcp_req = fcp_req;
> + fcp_req->private = evt;
> +
> + vfc_cmd = ibmvfc_nvme_init_vfc_cmd(evt, rport, fcp_req);
> +
> + vfc_cmd->correlation = cpu_to_be64((u64)evt);
> +
> + if (likely(!(rc = ibmvfc_nvme_map_sg_data(fcp_req, evt, vfc_cmd))))
> + return ibmvfc_send_event(evt, vhost, 0);
[Severity: Critical]
Could this lead to NVMe FCP requests being incorrectly processed as MAD
requests in error paths?
If ibmvfc_send_event() fails (e.g., due to a transport error) or if
ibmvfc_fail_request() is called during a queue purge, does the driver assume
that any event without a SCSI cmnd is a MAD request? Since NVMe FCP requests
lack a cmnd, it appears they might fall into legacy branches where an
uninitialized evt->_done is executed, or where mad_common.status (which
overlaps with NVMe's payload_len) is written to instead of vfc_cmd->status,
resulting in a crash or the request being completed as a false success.
[Severity: High]
Will this propagate SCSI midlayer error codes directly to the NVMe-FC core?
If ibmvfc_send_event() detects a transient queue closure, it returns
SCSI_MLQUEUE_HOST_BUSY. Since the NVMe-FC core expects standard Linux error
codes (like -EBUSY) from the fcp_io callback, will returning this positive
error code cause it to be translated to BLK_STS_IOERR, leading to spurious
hard failures instead of retrying the I/O?
> +
> + ibmvfc_free_event(evt);
> +
> + return rc;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260723000149.969416-1-tyreld@linux.ibm.com?part=30
^ permalink raw reply [flat|nested] 62+ messages in thread
end of thread, other threads:[~2026-07-23 1:08 UTC | newest]
Thread overview: 62+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 0:01 [PATCH v2 00/33] ibmvfc: NVMe/FC support over IBM Virtual FC Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 01/33] ibmvfc: don't call locked done variant for MADs on send failure Tyrel Datwyler
2026-07-23 0:42 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 02/33] ibmvfc: flush rport_add_work_q during driver teardown Tyrel Datwyler
2026-07-23 0:35 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 03/33] ibmvfc: check for NULL evt in implicit LOGO and target delete path Tyrel Datwyler
2026-07-23 0:30 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 04/33] ibmvfc: free ibmvfc_target allocations with mempool_free Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 05/33] ibmvfc: move target list from host to protocol specific channel groups Tyrel Datwyler
2026-07-23 0:34 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 06/33] ibmvfc: add NVMe/FC protocol interface definitions Tyrel Datwyler
2026-07-23 0:28 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 07/33] ibmvfc: split NVMe support into separate source file and add transport stubs Tyrel Datwyler
2026-07-23 0:22 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 08/33] ibmvfc: initialize NVMe channel configuration during driver probe Tyrel Datwyler
2026-07-23 0:21 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 09/33] ibmvfc: alloc/dealloc sub-queues for nvme channels Tyrel Datwyler
2026-07-23 0:33 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 10/33] ibmvfc: add logic for protocol specific fabric logins Tyrel Datwyler
2026-07-23 0:28 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 11/33] ibmvfc: add wrapper to get vhost associated with a channel struct Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 12/33] ibmvfc: add helper for creating protocol specific discovery event Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 13/33] ibmvfc: add helper to check NVMe/FC support with active channels Tyrel Datwyler
2026-07-23 0:17 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 14/33] ibmvfc: allocate and free NVMe channel group discover buffer Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 15/33] ibmvfc: send NVMe target discovery MAD Tyrel Datwyler
2026-07-23 0:31 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 16/33] ibmvfc: add NVMe/FC Implicit Logout and Move Login support Tyrel Datwyler
2026-07-23 0:36 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 17/33] ibmvfc: add NVMe/FC Port " Tyrel Datwyler
2026-07-23 0:38 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 18/33] ibmvfc: add NVMe/FC Process " Tyrel Datwyler
2026-07-23 0:39 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 19/33] ibmvfc: add NVMe/FC Query Target support Tyrel Datwyler
2026-07-23 0:50 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 20/33] ibmvfc: allocate targets based on protocol Tyrel Datwyler
2026-07-23 0:43 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 21/33] ibmvfc: delete NVMe/FC targets as well as SCSI Tyrel Datwyler
2026-07-23 0:52 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 22/33] ibmvfc: update state machine to process NVMe/FC targets Tyrel Datwyler
2026-07-23 0:53 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 23/33] ibmvfc: implement NVMe/FC stubs for local/remote port registration Tyrel Datwyler
2026-07-23 0:54 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 24/33] ibmvfc: register local nvme fc port after fabric login Tyrel Datwyler
2026-07-23 0:53 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 25/33] ibmvfc: process NVMe/FC rports in work thread Tyrel Datwyler
2026-07-23 0:50 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 26/33] ibmvfc: extend ibmvfc_debug visibility to ibmvfc-nvme.h Tyrel Datwyler
2026-07-23 0:42 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 27/33] ibmvfc: declare global function definitions Tyrel Datwyler
2026-07-23 0:01 ` [PATCH v2 28/33] ibmvfc: implement LLDD callbacks for mapping nvme-fc queues Tyrel Datwyler
2026-07-23 0:58 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 29/33] ibmvfc: implement nvme-fc LS submission transport callback Tyrel Datwyler
2026-07-23 1:00 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 30/33] ibmvfc: implement nvme-fc IO command submission callback Tyrel Datwyler
2026-07-23 1:08 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 31/33] ibmvfc: implement nvme-fc LS abort handling callback Tyrel Datwyler
2026-07-23 1:05 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 32/33] ibmvfc: implement nvme-fc FCP abort callback Tyrel Datwyler
2026-07-23 1:05 ` sashiko-bot
2026-07-23 0:01 ` [PATCH v2 33/33] ibmvfc: fail nvme-fc fcp-io and ls requests during transport reset Tyrel Datwyler
2026-07-23 0:58 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.