* [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages
@ 2026-07-02 17:59 Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 1/8] ibmvfc: add basic FPIN support Dave Marquardt via B4 Relay
` (7 more replies)
0 siblings, 8 replies; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
This patch series adds FPIN (fabric performance impact notification)
support to the ibmvfc (IBM Virtual Fibre Channel) driver. This comes
in three flavors:
- basic, to recognize existing FPIN messages from the virtual I/O
server (VIOS) (patch 1)
- full, supporting additional FPIN information and using its own
asynchronous sub-queue and interrupt (patches 4-7)
- extended, supporting FC-LS-5 (patch 8)
Full and extended FPIN support requires a new asynchronous sub-queue
with its own interrupt. The asynchronous sub-queue support requires
ibmvfc to also support
- a new VFC_NOOP command, which the driver recognizes and
ignores (patch 2)
- fabric login, to login separately to the fabric through messages
exchanged with VIOS rather than doing fabric login through the
existing NPIV login (patch 3)
All three modes convert an incoming FPIN message from VIOS to an FC
extended link service message, in some cases using default values for
information not provided by the VIOS FPIN message but expected in the
FC ELS message. This FC ELS message is passed to fc_host_rcv_fpin for
updating statistics and sending the information upstream by netlink
multicast, where it may be read by listeners including the DM
multipath daemon "multipathd."
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
Highlights of changes in v2:
- Refactored mostly common FPIN conversion routines and async event
processing into single routines with wrappers for differences.
- Moved FPIN processing to a work queue to avoid conflicts with
fc_host_fpin_rcv and memory allocation
- Set descriptor sizes correctly
- Use target WWPN for basic FPIN descriptor
- Split patch 4 into 3 patches, for definition, allocation, and use of
the asynchronous sub-queue for events
- Link to v1: https://patch.msgid.link/20260408-ibmvfc-fpin-support-v1-0-52b06c464e03@linux.ibm.com
---
Changes in v3:
- Fixed latent bug, exposed by VFC_NOOP, related to dataless CRQs and events
- Fixed FPIN TLV descriptor length calculations
- Use safe list walker to walk targets in ibmvfc_process_async_work
- Added write memory barriers after clearing CRQ valid field
- Use per-vhost work queue for FPIN work
- Link to v2: https://patch.msgid.link/20260608-ibmvfc-fpin-support-v2-0-d41f540fba5c@linux.ibm.com
---
Dave Marquardt (8):
ibmvfc: add basic FPIN support
ibmvfc: Add NOOP command support
ibmvfc: make ibmvfc login to fabric
ibmvfc: define asynchronous sub-queue
ibmvfc: allocate asynchronous sub-queue
ibmvfc: extend async event handlers to handle async sub queue events
ibmvfc: register and use asynchronous sub-queue for events
ibmvfc: handle extended FPIN events
drivers/scsi/Kconfig | 10 +
drivers/scsi/ibmvscsi/Makefile | 1 +
drivers/scsi/ibmvscsi/ibmvfc.c | 704 ++++++++++++++++++++++++++++++++---
drivers/scsi/ibmvscsi/ibmvfc.h | 115 +++++-
drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 239 ++++++++++++
5 files changed, 1000 insertions(+), 69 deletions(-)
---
base-commit: 0600eec09ad6cc5ba3ca78aceb6fa8dcbad010bb
change-id: 20260407-ibmvfc-fpin-support-b9b575cd2da1
Best regards,
--
Dave Marquardt <davemarq@linux.ibm.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 1/8] ibmvfc: add basic FPIN support
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:16 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 2/8] ibmvfc: Add NOOP command support Dave Marquardt via B4 Relay
` (6 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Add support for basic FPIN messages to the ibmvfc driver. This includes
- adding FPIN handling support to the async event handler
- offloading processing of FPIN messages to a work queue
- converting the VIOS FPIN message to a struct fc_els_fpin as used by
the Linux kernel
- passing the converted struct fc_els_fpin to fc_host_fpin_rcv for
processing
The FPIN message conversion routines include a common routine that
will also be used in patches 6 and 8, which add full and extended FPIN
support.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/Kconfig | 10 ++
drivers/scsi/ibmvscsi/Makefile | 1 +
drivers/scsi/ibmvscsi/ibmvfc.c | 255 ++++++++++++++++++++++++++++++++++-
drivers/scsi/ibmvscsi/ibmvfc.h | 16 +++
drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 131 ++++++++++++++++++
5 files changed, 406 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index c3042393af23..d5fc7eb2ebb1 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -758,6 +758,16 @@ config SCSI_IBMVFC
To compile this driver as a module, choose M here: the
module will be called ibmvfc.
+config SCSI_IBMVFC_KUNIT_TEST
+ tristate "KUnit tests for the IBM POWER Virtual FC Client" if !KUNIT_ALL_TESTS
+ depends on SCSI_IBMVFC && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Compile IBM POWER Virtual FC client KUnit tests. These tests
+ specifically test FPIN functionality. To compile this driver
+ as a module, choose M here: the module will be called
+ ibmvfc_kunit.
+
config SCSI_IBMVFC_TRACE
bool "enable driver internal trace"
depends on SCSI_IBMVFC
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 5eb1cb1a0028..75dc7aee15a0 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o
obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o
+obj-$(CONFIG_SCSI_IBMVFC_KUNIT_TEST) += ibmvfc_kunit.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 3dd2adda195e..b63f41a6cfff 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -30,6 +30,9 @@
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_bsg_fc.h>
+#include <kunit/visibility.h>
+#include <scsi/fc/fc_els.h>
+#include <linux/overflow.h>
#include "ibmvfc.h"
static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
@@ -3137,6 +3140,7 @@ static const struct ibmvfc_async_desc ae_desc [] = {
{ "Halt", IBMVFC_AE_HALT, IBMVFC_DEFAULT_LOG_LEVEL },
{ "Resume", IBMVFC_AE_RESUME, IBMVFC_DEFAULT_LOG_LEVEL },
{ "Adapter Failed", IBMVFC_AE_ADAPTER_FAILED, IBMVFC_DEFAULT_LOG_LEVEL },
+ { "FPIN", IBMVFC_AE_FPIN, IBMVFC_DEFAULT_LOG_LEVEL },
};
static const struct ibmvfc_async_desc unknown_ae = {
@@ -3185,16 +3189,228 @@ static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
return "";
}
+#define IBMVFC_FPIN_CONGN_DESC_SZ (sizeof(struct fc_els_fpin) + sizeof(struct fc_fn_congn_desc))
+#define IBMVFC_FPIN_LI_DESC_SZ (sizeof(struct fc_els_fpin) + \
+ struct_size_t(struct fc_fn_li_desc, pname_list, 1))
+#define IBMVFC_FPIN_PEER_CONGN_DESC_SZ (sizeof(struct fc_els_fpin) + \
+ struct_size_t(struct fc_fn_peer_congn_desc, pname_list, 1))
+
+/**
+ * ibmvfc_fpin_size_helper(): compute fpin structure size based on fpin status
+ * @fpin_status: status value
+ *
+ * Return:
+ * 0: invalid fpin_status
+ * other: valid size
+ */
+static size_t ibmvfc_fpin_size_helper(u8 fpin_status)
+{
+ size_t size = 0;
+
+ switch (fpin_status) {
+ case IBMVFC_AE_FPIN_LINK_CONGESTED:
+ case IBMVFC_AE_FPIN_CONGESTION_CLEARED:
+ size = IBMVFC_FPIN_CONGN_DESC_SZ;
+ break;
+ case IBMVFC_AE_FPIN_PORT_CONGESTED:
+ case IBMVFC_AE_FPIN_PORT_CLEARED:
+ size = IBMVFC_FPIN_PEER_CONGN_DESC_SZ;
+ break;
+ case IBMVFC_AE_FPIN_PORT_DEGRADED:
+ size = IBMVFC_FPIN_LI_DESC_SZ;
+ break;
+ default:
+ break;
+ }
+
+ return size;
+}
+
+/**
+ * ibmvfc_common_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct
+ * containing a descriptor.
+ *
+ * Allocate a struct fc_els_fpin containing a descriptor and populate
+ * based on data from *ibmvfc_fpin.
+ *
+ * Return:
+ * NULL - unable to allocate structure
+ * non-NULL - pointer to populated struct fc_els_fpin
+ */
+static struct fc_els_fpin *
+ibmvfc_common_fpin_to_desc(u8 fpin_status, __be64 wwpn, __be16 type, __be16 modifier,
+ __be32 threshold, __be32 event_count)
+{
+ struct fc_fn_peer_congn_desc *pdesc;
+ struct fc_fn_congn_desc *cdesc;
+ struct fc_fn_li_desc *ldesc;
+ struct fc_els_fpin *fpin;
+ size_t size;
+
+ size = ibmvfc_fpin_size_helper(fpin_status);
+ if (!size)
+ return NULL;
+
+ fpin = kzalloc(size, GFP_KERNEL);
+ if (!fpin)
+ return NULL;
+
+ fpin->fpin_cmd = ELS_FPIN;
+
+ switch (fpin_status) {
+ case IBMVFC_AE_FPIN_CONGESTION_CLEARED:
+ case IBMVFC_AE_FPIN_LINK_CONGESTED:
+ fpin->desc_len = cpu_to_be32(sizeof(struct fc_fn_congn_desc));
+ cdesc = (struct fc_fn_congn_desc *)fpin->fpin_desc;
+ cdesc->desc_tag = cpu_to_be32(ELS_DTAG_CONGESTION);
+ cdesc->desc_len = cpu_to_be32(FC_TLV_DESC_LENGTH_FROM_SZ(*cdesc));
+ cdesc->event_type = type;
+ cdesc->event_modifier = modifier;
+ cdesc->event_period = cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_PERIOD);
+ cdesc->severity = FPIN_CONGN_SEVERITY_WARNING;
+ break;
+ case IBMVFC_AE_FPIN_PORT_CONGESTED:
+ case IBMVFC_AE_FPIN_PORT_CLEARED:
+ fpin->desc_len =
+ cpu_to_be32(struct_size_t(struct fc_fn_peer_congn_desc, pname_list, 1));
+ pdesc = (struct fc_fn_peer_congn_desc *)fpin->fpin_desc;
+ pdesc->desc_tag = cpu_to_be32(ELS_DTAG_PEER_CONGEST);
+ pdesc->desc_len = cpu_to_be32(struct_size_t(struct fc_fn_peer_congn_desc,
+ pname_list, 1) - FC_TLV_DESC_HDR_SZ);
+ pdesc->event_type = type;
+ pdesc->event_modifier = modifier;
+ pdesc->event_period = cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_PERIOD);
+ pdesc->attached_wwpn = wwpn;
+ pdesc->pname_count = cpu_to_be32(1);
+ pdesc->pname_list[0] = wwpn;
+ break;
+ case IBMVFC_AE_FPIN_PORT_DEGRADED:
+ fpin->desc_len = cpu_to_be32(struct_size_t(struct fc_fn_li_desc, pname_list, 1));
+ ldesc = (struct fc_fn_li_desc *)fpin->fpin_desc;
+ ldesc->desc_tag = cpu_to_be32(ELS_DTAG_LNK_INTEGRITY);
+ ldesc->desc_len = cpu_to_be32(struct_size_t(struct fc_fn_li_desc,
+ pname_list, 1) - FC_TLV_DESC_HDR_SZ);
+ ldesc->event_type = type;
+ ldesc->event_modifier = modifier;
+ ldesc->event_threshold = threshold;
+ ldesc->event_count = event_count;
+ ldesc->attached_wwpn = wwpn;
+ ldesc->pname_count = cpu_to_be32(1);
+ ldesc->pname_list[0] = wwpn;
+ break;
+ default:
+ /* This should be caught above. */
+ kfree(fpin);
+ fpin = NULL;
+ break;
+ }
+
+ return fpin;
+}
+
+/**
+ * ibmvfc_basic_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct
+ * containing a descriptor.
+ * @ibmvfc_fpin: Pointer to async crq
+ *
+ * Allocate a struct fc_els_fpin containing a descriptor and populate
+ * based on data from *ibmvfc_fpin.
+ *
+ * Return:
+ * NULL - unable to allocate structure
+ * non-NULL - pointer to populated struct fc_els_fpin
+ */
+static struct fc_els_fpin *
+ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, u64 wwpn)
+{
+ __be16 type;
+
+ switch (crq->fpin_status) {
+ case IBMVFC_AE_FPIN_LINK_CONGESTED:
+ case IBMVFC_AE_FPIN_PORT_CONGESTED:
+ type = cpu_to_be16(FPIN_CONGN_DEVICE_SPEC);
+ break;
+ case IBMVFC_AE_FPIN_PORT_CLEARED:
+ case IBMVFC_AE_FPIN_CONGESTION_CLEARED:
+ type = cpu_to_be16(FPIN_CONGN_CLEAR);
+ break;
+ case IBMVFC_AE_FPIN_PORT_DEGRADED:
+ type = cpu_to_be16(FPIN_LI_UNKNOWN);
+ break;
+ default:
+ return (NULL);
+ }
+
+ return ibmvfc_common_fpin_to_desc(crq->fpin_status, cpu_to_be64(wwpn),
+ type, cpu_to_be16(0),
+ cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD),
+ cpu_to_be32(1));
+}
+
+/**
+ * ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue
+ * @work: pointer to work_struct
+ */
+static void ibmvfc_process_async_work(struct work_struct *work)
+{
+ struct ibmvfc_target *tgt, *next;
+ struct ibmvfc_async_work *aw;
+ struct ibmvfc_async_crq *crq;
+ struct ibmvfc_host *vhost;
+ struct fc_els_fpin *fpin;
+ unsigned long flags;
+
+ aw = container_of(work, struct ibmvfc_async_work, async_work_s);
+ crq = &aw->crq;
+ vhost = aw->vhost;
+
+ if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ goto end;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ list_for_each_entry_safe(tgt, next, &vhost->targets, queue) {
+ 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->rport)
+ continue;
+ break;
+ }
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ if (!tgt || !tgt->rport) {
+ dev_err_ratelimited(vhost->dev, "Invalid target for FPIN\n");
+ goto end;
+ }
+
+ fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
+ if (fpin) {
+ fc_host_fpin_rcv(tgt->vhost->host,
+ sizeof(*fpin) + be32_to_cpu(fpin->desc_len),
+ (char *)fpin, 0);
+ kfree(fpin);
+ } else
+ dev_err_ratelimited(vhost->dev,
+ "FPIN event %u received, unable to process\n",
+ crq->fpin_status);
+ end:
+ kfree(aw);
+}
+
/**
* ibmvfc_handle_async - Handle an async event from the adapter
* @crq: crq to process
* @vhost: ibmvfc host struct
*
**/
-static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
- struct ibmvfc_host *vhost)
+VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
+ struct ibmvfc_host *vhost)
{
const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be64_to_cpu(crq->event));
+ struct ibmvfc_async_work *aw;
struct ibmvfc_target *tgt;
ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx,"
@@ -3269,11 +3485,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_HALT:
ibmvfc_link_down(vhost, IBMVFC_HALTED);
break;
+ case IBMVFC_AE_FPIN:
+ aw = kzalloc(sizeof(struct ibmvfc_async_work), GFP_ATOMIC);
+ if (aw) {
+ INIT_WORK(&aw->async_work_s, ibmvfc_process_async_work);
+ aw->vhost = vhost;
+ aw->crq = *crq;
+ queue_work(vhost->fpin_workq, &aw->async_work_s);
+ } else
+ dev_err_ratelimited(vhost->dev,
+ "can't offload async CRQ to work queue\n");
+ break;
default:
dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
break;
}
}
+EXPORT_SYMBOL_IF_KUNIT(ibmvfc_handle_async);
/**
* ibmvfc_handle_crq - Handles and frees received events in the CRQ
@@ -3803,8 +4031,6 @@ static void ibmvfc_tasklet(void *data)
/* Pull all the valid messages off the async CRQ */
while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
ibmvfc_handle_async(async, vhost);
- async->valid = 0;
- wmb();
}
/* Pull all the valid messages off the CRQ */
@@ -3818,8 +4044,6 @@ static void ibmvfc_tasklet(void *data)
if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_async(async, vhost);
- async->valid = 0;
- wmb();
} else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_crq(crq, vhost, &evt_doneq);
@@ -6364,9 +6588,15 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
mutex_init(&vhost->passthru_mutex);
- if ((rc = ibmvfc_alloc_mem(vhost)))
+ vhost->fpin_workq = alloc_workqueue("%s-fpin-workq-%u", 0, 0,
+ IBMVFC_NAME, shost->host_no);
+ if (vhost->fpin_workq == NULL)
goto free_scsi_host;
+ rc = ibmvfc_alloc_mem(vhost);
+ if (rc)
+ goto free_workq;
+
vhost->work_thread = kthread_run(ibmvfc_work, vhost, "%s_%d", IBMVFC_NAME,
shost->host_no);
@@ -6412,6 +6642,9 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
kthread_stop(vhost->work_thread);
free_host_mem:
ibmvfc_free_mem(vhost);
+free_workq:
+ destroy_workqueue(vhost->fpin_workq);
+ vhost->fpin_workq = NULL;
free_scsi_host:
scsi_host_put(shost);
out:
@@ -6453,6 +6686,8 @@ static void ibmvfc_remove(struct vio_dev *vdev)
ibmvfc_release_crq_queue(vhost);
ibmvfc_free_mem(vhost);
+ destroy_workqueue(vhost->fpin_workq);
+ vhost->fpin_workq = NULL;
spin_lock(&ibmvfc_driver_lock);
list_del(&vhost->queue);
spin_unlock(&ibmvfc_driver_lock);
@@ -6603,5 +6838,11 @@ static void __exit ibmvfc_module_exit(void)
fc_release_transport(ibmvfc_transport_template);
}
+VISIBLE_IF_KUNIT struct list_head *ibmvfc_get_headp(void)
+{
+ return &ibmvfc_head;
+}
+EXPORT_SYMBOL_IF_KUNIT(ibmvfc_get_headp);
+
module_init(ibmvfc_module_init);
module_exit(ibmvfc_module_exit);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index c73ed2314ad0..f69e0605a78d 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -671,8 +671,12 @@ enum ibmvfc_ae_fpin_status {
IBMVFC_AE_FPIN_PORT_CONGESTED = 0x2,
IBMVFC_AE_FPIN_PORT_CLEARED = 0x3,
IBMVFC_AE_FPIN_PORT_DEGRADED = 0x4,
+ IBMVFC_AE_FPIN_CONGESTION_CLEARED = 0x5,
};
+#define IBMVFC_FPIN_DEFAULT_EVENT_PERIOD (5*60*MSEC_PER_SEC) /* 5 minutes */
+#define IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD (5*60*MSEC_PER_SEC/2) /* 2.5 minutes */
+
struct ibmvfc_async_crq {
volatile u8 valid;
u8 link_state;
@@ -686,6 +690,12 @@ struct ibmvfc_async_crq {
__be64 reserved;
} __packed __aligned(8);
+struct ibmvfc_async_work {
+ struct ibmvfc_host *vhost;
+ struct ibmvfc_async_crq crq;
+ struct work_struct async_work_s;
+};
+
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
@@ -914,6 +924,7 @@ 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 workqueue_struct *fpin_workq;
};
#define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
@@ -953,4 +964,9 @@ struct ibmvfc_host {
#define ibmvfc_remove_trace_file(kobj, attr) do { } while (0)
#endif
+#ifdef VISIBLE_IF_KUNIT
+VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, struct ibmvfc_host *vhost);
+VISIBLE_IF_KUNIT struct list_head *ibmvfc_get_headp(void);
+#endif
+
#endif
diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
new file mode 100644
index 000000000000..e41e2a49e549
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <kunit/test.h>
+#include <kunit/visibility.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport_fc.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include "ibmvfc.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+/**
+ * ibmvfc_async_fpin_event_test - unit test for IBMVFC_AE_FPIN parts of
+ * ibmvfc_handle_async
+ * @test: pointer to kunit structure
+ *
+ * Tests
+ * - error returns from ibmvfc_handle_async
+ * - statistics updates
+ *
+ * Return: void
+ */
+static void ibmvfc_async_fpin_test(struct kunit *test)
+{
+ u64 post[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1];
+ u64 pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1];
+ enum ibmvfc_ae_fpin_status fs;
+ struct fc_host_attrs *fc_host;
+ struct ibmvfc_async_crq crq[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1];
+ struct ibmvfc_target *tgt;
+ struct ibmvfc_host *vhost;
+ struct list_head *queue;
+ struct list_head *headp;
+
+ headp = ibmvfc_get_headp();
+ KUNIT_ASSERT_FALSE_MSG(test, list_empty(headp), "No ibmvfc devices available\n");
+ queue = headp->next;
+ vhost = container_of(queue, struct ibmvfc_host, queue);
+
+ KUNIT_ASSERT_GE_MSG(test, vhost->num_targets, 1, "No targets");
+ tgt = list_first_entry(&vhost->targets, struct ibmvfc_target, queue);
+ KUNIT_EXPECT_NOT_NULL(test, tgt->rport);
+
+ fc_host = shost_to_fc_host(vhost->host);
+
+ pre[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
+ pre[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
+ pre[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ pre[IBMVFC_AE_FPIN_PORT_DEGRADED] = READ_ONCE(tgt->rport->fpin_stats.li_failure_unknown);
+ pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED] = READ_ONCE(fc_host->fpin_stats.cn_clear);
+
+ for (fs = IBMVFC_AE_FPIN_LINK_CONGESTED; fs <= IBMVFC_AE_FPIN_CONGESTION_CLEARED; fs++) {
+ crq[fs].valid = 0x80;
+ crq[fs].link_state = IBMVFC_AE_LS_LINK_UP;
+ crq[fs].fpin_status = fs;
+ crq[fs].event = cpu_to_be64(IBMVFC_AE_FPIN);
+ crq[fs].scsi_id = cpu_to_be64(tgt->scsi_id);
+ crq[fs].wwpn = cpu_to_be64(tgt->wwpn);
+ crq[fs].node_name = cpu_to_be64(tgt->ids.node_name);
+ ibmvfc_handle_async(&crq[fs], vhost);
+ }
+
+ msleep(500U);
+
+ post[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
+ post[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
+ post[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ post[IBMVFC_AE_FPIN_PORT_DEGRADED] = READ_ONCE(tgt->rport->fpin_stats.li_failure_unknown);
+ post[IBMVFC_AE_FPIN_CONGESTION_CLEARED] = READ_ONCE(fc_host->fpin_stats.cn_clear);
+
+ KUNIT_EXPECT_GE(test, post[IBMVFC_AE_FPIN_LINK_CONGESTED],
+ pre[IBMVFC_AE_FPIN_LINK_CONGESTED]+1);
+ KUNIT_EXPECT_GE(test, post[IBMVFC_AE_FPIN_PORT_CONGESTED],
+ pre[IBMVFC_AE_FPIN_PORT_CONGESTED]+1);
+ KUNIT_EXPECT_GE(test, post[IBMVFC_AE_FPIN_PORT_CLEARED],
+ pre[IBMVFC_AE_FPIN_PORT_CLEARED]+1);
+ KUNIT_EXPECT_GE(test, post[IBMVFC_AE_FPIN_PORT_DEGRADED],
+ pre[IBMVFC_AE_FPIN_PORT_DEGRADED]+1);
+ KUNIT_EXPECT_GE(test, post[IBMVFC_AE_FPIN_CONGESTION_CLEARED],
+ pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED]+1);
+
+ pre[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
+ pre[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
+ pre[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ pre[IBMVFC_AE_FPIN_PORT_DEGRADED] = READ_ONCE(tgt->rport->fpin_stats.li_failure_unknown);
+ pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED] = READ_ONCE(fc_host->fpin_stats.cn_clear);
+
+ /* bad path */
+ crq[0].valid = 0x80;
+ crq[0].link_state = IBMVFC_AE_LS_LINK_UP;
+ crq[0].fpin_status = 0; /* bad value */
+ crq[0].event = cpu_to_be64(IBMVFC_AE_FPIN);
+ crq[0].scsi_id = cpu_to_be64(tgt->scsi_id);
+ crq[0].wwpn = cpu_to_be64(tgt->wwpn);
+ crq[0].node_name = cpu_to_be64(tgt->ids.node_name);
+ ibmvfc_handle_async(&crq[0], vhost);
+
+ msleep(500U);
+
+ post[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
+ post[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
+ post[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ post[IBMVFC_AE_FPIN_PORT_DEGRADED] = READ_ONCE(tgt->rport->fpin_stats.li_failure_unknown);
+ post[IBMVFC_AE_FPIN_CONGESTION_CLEARED] = READ_ONCE(fc_host->fpin_stats.cn_clear);
+
+ KUNIT_EXPECT_EQ(test, pre[IBMVFC_AE_FPIN_LINK_CONGESTED],
+ post[IBMVFC_AE_FPIN_LINK_CONGESTED]);
+ KUNIT_EXPECT_EQ(test, pre[IBMVFC_AE_FPIN_PORT_CONGESTED],
+ post[IBMVFC_AE_FPIN_PORT_CONGESTED]);
+ KUNIT_EXPECT_EQ(test, pre[IBMVFC_AE_FPIN_PORT_CLEARED],
+ post[IBMVFC_AE_FPIN_PORT_CLEARED]);
+ KUNIT_EXPECT_EQ(test, pre[IBMVFC_AE_FPIN_PORT_DEGRADED],
+ post[IBMVFC_AE_FPIN_PORT_DEGRADED]);
+ KUNIT_EXPECT_EQ(test, pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED],
+ post[IBMVFC_AE_FPIN_CONGESTION_CLEARED]);
+}
+
+static struct kunit_case ibmvfc_fpin_test_cases[] = {
+ KUNIT_CASE_SLOW(ibmvfc_async_fpin_test),
+ {},
+};
+
+static struct kunit_suite ibmvfc_fpin_test_suite = {
+ .name = "ibmvfc-fpin-test",
+ .test_cases = ibmvfc_fpin_test_cases,
+};
+kunit_test_init_section_suite(ibmvfc_fpin_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dave Marquardt <davemarq@linux.ibm.com>");
+MODULE_DESCRIPTION("Test module for IBM Virtual Fibre Channel Driver");
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 2/8] ibmvfc: Add NOOP command support
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 1/8] ibmvfc: add basic FPIN support Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:11 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric Dave Marquardt via B4 Relay
` (5 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
This patch adds support for receiving and recognizing VFC_NOOP
messages from VIOS. This is done by
- defining the VFC_NOOP CRQ format
- recognizing the VFC_NOOP CRQ format in the CRQ handler. If the VIOS
has not provided the "support VFC_NOOP" bit in its capabilities on
NPIV login, note that the VFC_NOOP is unexpected
- setting the "can use VFC_NOOP" bit in the capabilities sent during
NPIV login
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 16 +++++++++++++++-
drivers/scsi/ibmvscsi/ibmvfc.h | 23 +++++++++++++----------
2 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index b63f41a6cfff..110d77b9aac3 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1512,7 +1512,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->flags |= cpu_to_be16(IBMVFC_CLIENT_MIGRATED);
login_info->max_cmds = cpu_to_be32(max_cmds);
- login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
+ login_info->capabilities =
+ cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN |
+ IBMVFC_CAN_USE_NOOP_CMD);
if (vhost->mq_enabled || vhost->using_channels)
login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
@@ -3569,6 +3571,14 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
if (crq->format == IBMVFC_ASYNC_EVENT)
return;
+ if (crq->format == IBMVFC_VFC_NOOP) {
+ if (vhost->state == IBMVFC_ACTIVE &&
+ !ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NOOP_CMD))
+ dev_err_ratelimited(vhost->dev,
+ "Received unexpected NOOP command from partner\n");
+ return;
+ }
+
/* The only kind of payload CRQs we should get are responses to
* things we send. Make sure this response is to something we
* actually sent
@@ -4099,6 +4109,10 @@ static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost
return;
}
+ /* Some CRQs, e.g. a VFC_NOOP command CRQ, do not have an ioba, so evt is NULL. */
+ if (!evt)
+ return;
+
/* The only kind of payload CRQs we should get are responses to
* things we send. Make sure this response is to something we
* actually sent
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index f69e0605a78d..526632cb7237 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -175,11 +175,12 @@ 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_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
@@ -221,11 +222,12 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_NATIVE_FC 0x01
__be32 reserved;
__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_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
__be64 max_dma_len;
@@ -621,6 +623,7 @@ struct ibmvfc_trace_entry {
enum ibmvfc_crq_formats {
IBMVFC_CMD_FORMAT = 0x01,
IBMVFC_ASYNC_EVENT = 0x02,
+ IBMVFC_VFC_NOOP = 0x03,
IBMVFC_MAD_FORMAT = 0x04,
};
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 1/8] ibmvfc: add basic FPIN support Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 2/8] ibmvfc: Add NOOP command support Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:17 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue Dave Marquardt via B4 Relay
` (4 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Add support for fabric login in order to support the asynchronous
event queue with its own interrupt as required by NPIV specification
to support the asynchronous sub-queue and interrupt and full and
extended FPIN messages.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 96 ++++++++++++++++++++++++++++++++++++++++--
drivers/scsi/ibmvscsi/ibmvfc.h | 17 ++++++++
2 files changed, 109 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 110d77b9aac3..6dd1e726d9b9 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -5261,6 +5261,88 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
}
+static void ibmvfc_fabric_login_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_fabric_login *rsp = &evt->xfer_iu->fabric_login;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ struct ibmvfc_host *vhost = evt->vhost;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ ENTER;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
+ ibmvfc_free_event(evt);
+ 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, "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);
+ LEAVE;
+ return;
+
+ case IBMVFC_MAD_CRQ_ERROR:
+ ibmvfc_retry_host_init(vhost);
+ fallthrough;
+
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ LEAVE;
+ return;
+
+ default:
+ dev_err(vhost->dev, "Invalid fabric Login response: 0x%x\n", mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_free_event(evt);
+ LEAVE;
+ return;
+ }
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+
+ LEAVE;
+}
+
+static void ibmvfc_fabric_login(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_fabric_login *mad;
+ struct ibmvfc_event *evt;
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ if (vhost->scsi_scrqs.protocol != IBMVFC_PROTO_SCSI) {
+ ibmvfc_log(vhost, level, "Fabric Login failed: unknown protocol\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ evt = ibmvfc_get_reserved_event(&vhost->crq);
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Fabric Login failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_fabric_login_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.fabric_login;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN);
+ mad->common.version = cpu_to_be32(1);
+ 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_link_down(vhost, IBMVFC_LINK_DOWN);
+}
+
static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
@@ -5307,8 +5389,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 (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI)) {
+ ibmvfc_fabric_login(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)
@@ -5499,9 +5585,11 @@ 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) {
+ if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry)
ibmvfc_channel_enquiry(vhost);
- } else {
+ else if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI))
+ ibmvfc_fabric_login(vhost);
+ else {
vhost->do_enquiry = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
wake_up(&vhost->work_wait_q);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 526632cb7237..adfd67e85af8 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -138,6 +138,7 @@ enum ibmvfc_mad_types {
IBMVFC_CHANNEL_ENQUIRY = 0x1000,
IBMVFC_CHANNEL_SETUP = 0x2000,
IBMVFC_CONNECTION_INFO = 0x4000,
+ IBMVFC_FABRIC_LOGIN = 0x8000,
};
struct ibmvfc_mad_common {
@@ -180,6 +181,7 @@ struct ibmvfc_npiv_login {
#define IBMVFC_CAN_HANDLE_FPIN 0x004
#define IBMVFC_CAN_USE_MAD_VERSION 0x008
#define IBMVFC_CAN_SEND_VF_WWPN 0x010
+#define IBMVFC_YES_SCSI 0x040
#define IBMVFC_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
@@ -227,6 +229,7 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_MAD_VERSION_CAP 0x0020
#define IBMVFC_HANDLE_VF_WWPN 0x0040
#define IBMVFC_CAN_SUPPORT_CHANNELS 0x0080
+#define IBMVFC_SUPPORT_SCSI 0x0200
#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
@@ -590,6 +593,19 @@ struct ibmvfc_connection_info {
__be64 reserved[16];
} __packed __aligned(8);
+struct ibmvfc_fabric_login {
+ struct ibmvfc_mad_common common;
+ __be64 flags;
+#define IBMVFC_STRIP_MERGE 0x1
+#define IBMVFC_LINK_COMMANDS 0x2
+ __be64 capabilities;
+ __be64 nport_id;
+ __be16 status;
+ __be16 error;
+ __be32 pad;
+ __be64 reserved[16];
+} __packed __aligned(8);
+
struct ibmvfc_trace_start_entry {
u32 xfer_len;
} __packed;
@@ -715,6 +731,7 @@ union ibmvfc_iu {
struct ibmvfc_channel_enquiry channel_enquiry;
struct ibmvfc_channel_setup_mad channel_setup;
struct ibmvfc_connection_info connection_info;
+ struct ibmvfc_fabric_login fabric_login;
} __packed __aligned(8);
enum ibmvfc_target_action {
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
` (2 preceding siblings ...)
2026-07-02 17:59 ` [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:12 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 5/8] ibmvfc: allocate " Dave Marquardt via B4 Relay
` (3 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Defines the asynchronous sub-queue structure, modifies the existing
channel setup structure, adds the asynchronous sub-queue to the
channels structure, and adds flags needed to tell VIOS to use the
sub-queue.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.h | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index adfd67e85af8..f38dfae9924c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -182,6 +182,7 @@ struct ibmvfc_npiv_login {
#define IBMVFC_CAN_USE_MAD_VERSION 0x008
#define IBMVFC_CAN_SEND_VF_WWPN 0x010
#define IBMVFC_YES_SCSI 0x040
+#define IBMVFC_USE_ASYNC_SUBQ 0x100
#define IBMVFC_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
@@ -230,6 +231,7 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_HANDLE_VF_WWPN 0x0040
#define IBMVFC_CAN_SUPPORT_CHANNELS 0x0080
#define IBMVFC_SUPPORT_SCSI 0x0200
+#define IBMVFC_SUPPORT_ASYNC_SUBQ 0x0800
#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
@@ -564,7 +566,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;
@@ -579,6 +581,7 @@ struct ibmvfc_channel_setup {
struct srp_direct_buf buffer;
__be64 reserved2[5];
__be64 channel_handles[IBMVFC_MAX_CHANNELS];
+ __be64 async_subq_handle;
} __packed __aligned(8);
struct ibmvfc_connection_info {
@@ -715,6 +718,25 @@ struct ibmvfc_async_work {
struct work_struct async_work_s;
};
+struct ibmvfc_async_subq {
+ volatile u8 valid;
+#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
+#define IBMVFC_FC_EEH 0x04
+#define IBMVFC_FC_FW_UPDATE 0x08
+#define IBMVFC_FC_FW_DUMP 0x10
+ u8 flags;
+ u8 link_state;
+ u8 fpin_status;
+ __be16 event;
+ __be16 pad;
+ volatile __be64 wwpn;
+ volatile __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;
@@ -854,6 +876,7 @@ struct ibmvfc_queue {
struct ibmvfc_channels {
struct ibmvfc_queue *scrqs;
+ struct ibmvfc_queue *async_scrq;
enum ibmvfc_protocol protocol;
unsigned int active_queues;
unsigned int desired_queues;
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 5/8] ibmvfc: allocate asynchronous sub-queue
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
` (3 preceding siblings ...)
2026-07-02 17:59 ` [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:20 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events Dave Marquardt via B4 Relay
` (2 subsequent siblings)
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Allocate and set up the asynchronous sub-queue for asynchronous
events, as required for full and extended FPIN support.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 6dd1e726d9b9..804887fb5635 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -5371,6 +5371,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
for (i = 0; i < active_queues; i++)
scrqs->scrqs[i].vios_cookie =
be64_to_cpu(setup->channel_handles[i]);
+ scrqs->async_scrq->vios_cookie =
+ be64_to_cpu(setup->async_subq_handle);
ibmvfc_dbg(vhost, "Using %u channels\n",
vhost->scsi_scrqs.active_queues);
@@ -5421,6 +5423,7 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
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->async_subq_handle = cpu_to_be64(scrqs->async_scrq->cookie);
}
ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
@@ -6388,6 +6391,17 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,
if (!channels->scrqs)
return -ENOMEM;
+ channels->async_scrq = kzalloc_obj(*channels->async_scrq, GFP_KERNEL);
+ if (!channels->async_scrq) {
+ rc = -ENOMEM;
+ goto free_scrqs;
+ }
+
+ rc = ibmvfc_alloc_queue(vhost, channels->async_scrq,
+ IBMVFC_SUB_CRQ_FMT);
+ if (rc)
+ goto free_async;
+
for (i = 0; i < channels->max_queues; i++) {
scrq = &channels->scrqs[i];
rc = ibmvfc_alloc_queue(vhost, scrq, IBMVFC_SUB_CRQ_FMT);
@@ -6396,13 +6410,21 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,
scrq = &channels->scrqs[j - 1];
ibmvfc_free_queue(vhost, scrq);
}
- kfree(channels->scrqs);
- channels->scrqs = NULL;
+ ibmvfc_free_queue(vhost, channels->async_scrq);
channels->active_queues = 0;
- return rc;
+ goto free_async;
}
}
+ return rc;
+
+free_async:
+ kfree(channels->async_scrq);
+ channels->async_scrq = NULL;
+free_scrqs:
+ kfree(channels->scrqs);
+ channels->scrqs = NULL;
+
return rc;
}
@@ -6437,6 +6459,10 @@ static void ibmvfc_release_channels(struct ibmvfc_host *vhost,
kfree(channels->scrqs);
channels->scrqs = NULL;
+
+ ibmvfc_free_queue(vhost, channels->async_scrq);
+ channels->async_scrq = NULL;
+
channels->active_queues = 0;
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
` (4 preceding siblings ...)
2026-07-02 17:59 ` [PATCH v3 5/8] ibmvfc: allocate " Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:14 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 8/8] ibmvfc: handle extended FPIN events Dave Marquardt via B4 Relay
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Make ibmvfc_handle_async and ibmvfc_process_async_work handle both
async CRQs and the new asynchronous sub-queue CRQs.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 156 +++++++++++++++++++++++++++--------
drivers/scsi/ibmvscsi/ibmvfc.h | 17 ++--
drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 10 +--
3 files changed, 136 insertions(+), 47 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 804887fb5635..4678d76c84fe 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -3349,6 +3349,27 @@ ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, u64 wwpn)
cpu_to_be32(1));
}
+/**
+ * ibmvfc_full_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct
+ * containing a descriptor.
+ * @ibmvfc_fpin: Pointer to async subq FPIN data
+ *
+ * Allocate a struct fc_els_fpin containing a descriptor and populate
+ * based on data from *ibmvfc_fpin.
+ *
+ * Return:
+ * NULL - unable to allocate structure
+ * non-NULL - pointer to populated struct fc_els_fpin
+ */
+static struct fc_els_fpin *
+ibmvfc_full_fpin_to_desc(struct ibmvfc_async_subq *ibmvfc_fpin)
+{
+ return ibmvfc_common_fpin_to_desc(ibmvfc_fpin->fpin_status, ibmvfc_fpin->wwpn,
+ cpu_to_be16(0), cpu_to_be16(0),
+ cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD),
+ cpu_to_be32(1));
+}
+
/**
* ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue
* @work: pointer to work_struct
@@ -3356,26 +3377,51 @@ ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq, u64 wwpn)
static void ibmvfc_process_async_work(struct work_struct *work)
{
struct ibmvfc_target *tgt, *next;
+ struct ibmvfc_async_subq *subq = NULL;
struct ibmvfc_async_work *aw;
- struct ibmvfc_async_crq *crq;
+ struct ibmvfc_async_crq *crq = NULL;
struct ibmvfc_host *vhost;
- struct fc_els_fpin *fpin;
+ struct fc_els_fpin *fpin = NULL;
unsigned long flags;
+ __be64 node_name;
+ __be64 scsi_id;
+ bool is_subq;
+ __be64 wwpn;
aw = container_of(work, struct ibmvfc_async_work, async_work_s);
- crq = &aw->crq;
vhost = aw->vhost;
+ is_subq = aw->is_subq;
+ if (is_subq)
+ subq = &aw->crq.subq;
+ else
+ crq = &aw->crq.async_crq;
- if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ if (!crq && !subq) {
+ dev_err_ratelimited(vhost->dev,
+ "FPIN event received, unable to process\n");
+ goto end;
+ }
+
+ if (crq) {
+ wwpn = crq->wwpn;
+ node_name = crq->node_name;
+ scsi_id = crq->scsi_id;
+ } else {
+ wwpn = subq->wwpn;
+ node_name = subq->id.node_name;
+ scsi_id = 0;
+ }
+
+ if (!scsi_id && !wwpn && !node_name)
goto end;
spin_lock_irqsave(vhost->host->host_lock, flags);
list_for_each_entry_safe(tgt, next, &vhost->targets, queue) {
- if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
+ if (scsi_id && cpu_to_be64(tgt->scsi_id) != scsi_id)
continue;
- if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ if (wwpn && cpu_to_be64(tgt->ids.port_name) != wwpn)
continue;
- if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
+ if (node_name && cpu_to_be64(tgt->ids.node_name) != node_name)
continue;
if (!tgt->rport)
continue;
@@ -3388,41 +3434,71 @@ static void ibmvfc_process_async_work(struct work_struct *work)
goto end;
}
- fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
+ if (crq)
+ fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
+ else
+ fpin = ibmvfc_full_fpin_to_desc(subq);
+
if (fpin) {
fc_host_fpin_rcv(tgt->vhost->host,
sizeof(*fpin) + be32_to_cpu(fpin->desc_len),
(char *)fpin, 0);
kfree(fpin);
} else
- dev_err_ratelimited(vhost->dev,
- "FPIN event %u received, unable to process\n",
- crq->fpin_status);
+ dev_err_ratelimited(vhost->dev, "FPIN event received, unable to process\n");
+
end:
kfree(aw);
}
/**
* ibmvfc_handle_async - Handle an async event from the adapter
- * @crq: crq to process
+ * @crq: ibmvfc_async_crq or ibmvfc_async_subq
* @vhost: ibmvfc host struct
+ * @is_subq: indicates whether the crq points to a struct ibmvfc_async_subq
*
**/
-VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
- struct ibmvfc_host *vhost)
+VISIBLE_IF_KUNIT void ibmvfc_handle_async(void *crq,
+ struct ibmvfc_host *vhost,
+ bool is_subq)
{
- const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be64_to_cpu(crq->event));
+ struct ibmvfc_async_crq *async_crq = NULL;
+ struct ibmvfc_async_subq *subq = NULL;
+ const struct ibmvfc_async_desc *desc;
+ struct ibmvfc_target *tgt, *next;
struct ibmvfc_async_work *aw;
- struct ibmvfc_target *tgt;
-
- ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx,"
- " node_name: %llx%s\n", desc->desc, be64_to_cpu(crq->scsi_id),
- be64_to_cpu(crq->wwpn), be64_to_cpu(crq->node_name),
- ibmvfc_get_link_state(crq->link_state));
-
- switch (be64_to_cpu(crq->event)) {
+ __be64 node_name;
+ __be64 scsi_id;
+ u8 link_state;
+ __be64 wwpn;
+ u64 event;
+
+ if (is_subq) {
+ subq = crq;
+ event = be16_to_cpu(subq->event);
+ link_state = subq->link_state;
+ scsi_id = 0;
+ wwpn = subq->wwpn;
+ node_name = subq->id.node_name;
+ } else {
+ async_crq = crq;
+ event = be64_to_cpu(async_crq->event);
+ link_state = async_crq->link_state;
+ scsi_id = async_crq->scsi_id;
+ wwpn = async_crq->wwpn;
+ node_name = async_crq->node_name;
+ }
+
+ desc = ibmvfc_get_ae_desc(event);
+ ibmvfc_log(vhost, desc->log_level,
+ "%s event received. scsi_id: %llx, wwpn: %llx, node_name: %llx, event %llx%s\n",
+ desc->desc, be64_to_cpu(scsi_id),
+ be64_to_cpu(wwpn), be64_to_cpu(node_name), event,
+ ibmvfc_get_link_state(link_state));
+
+ switch (event) {
case IBMVFC_AE_RESUME:
- switch (crq->link_state) {
+ switch (link_state) {
case IBMVFC_AE_LS_LINK_DOWN:
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
break;
@@ -3460,18 +3536,18 @@ VISIBLE_IF_KUNIT 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) {
- if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ list_for_each_entry_safe(tgt, next, &vhost->targets, queue) {
+ if (!scsi_id && !wwpn && !node_name)
break;
- if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
+ if (scsi_id && cpu_to_be64(tgt->scsi_id) != scsi_id)
continue;
- if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ if (wwpn && cpu_to_be64(tgt->ids.port_name) != wwpn)
continue;
- if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
+ if (node_name && cpu_to_be64(tgt->ids.node_name) != node_name)
continue;
- if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
+ if (tgt->need_login && event == IBMVFC_AE_ELS_LOGO)
tgt->logo_rcvd = 1;
- if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
+ if (!tgt->need_login || event == IBMVFC_AE_ELS_PLOGI) {
ibmvfc_del_tgt(tgt);
ibmvfc_reinit_host(vhost);
}
@@ -3492,16 +3568,26 @@ VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
if (aw) {
INIT_WORK(&aw->async_work_s, ibmvfc_process_async_work);
aw->vhost = vhost;
- aw->crq = *crq;
+ aw->is_subq = is_subq;
+ if (is_subq)
+ aw->crq.subq = *subq;
+ else
+ aw->crq.async_crq = *async_crq;
queue_work(vhost->fpin_workq, &aw->async_work_s);
} else
dev_err_ratelimited(vhost->dev,
"can't offload async CRQ to work queue\n");
break;
default:
- dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
+ dev_err(vhost->dev, "Unknown async event received: %llu\n", event);
break;
}
+
+ if (is_subq)
+ subq->valid = 0;
+ else
+ async_crq->valid = 0;
+ wmb();
}
EXPORT_SYMBOL_IF_KUNIT(ibmvfc_handle_async);
@@ -4040,7 +4126,7 @@ static void ibmvfc_tasklet(void *data)
while (!done) {
/* Pull all the valid messages off the async CRQ */
while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
- ibmvfc_handle_async(async, vhost);
+ ibmvfc_handle_async(async, vhost, false);
}
/* Pull all the valid messages off the CRQ */
@@ -4053,7 +4139,7 @@ static void ibmvfc_tasklet(void *data)
vio_enable_interrupts(vdev);
if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
- ibmvfc_handle_async(async, vhost);
+ ibmvfc_handle_async(async, vhost, false);
} else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
vio_disable_interrupts(vdev);
ibmvfc_handle_crq(crq, vhost, &evt_doneq);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index f38dfae9924c..bbf19220af70 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -712,12 +712,6 @@ struct ibmvfc_async_crq {
__be64 reserved;
} __packed __aligned(8);
-struct ibmvfc_async_work {
- struct ibmvfc_host *vhost;
- struct ibmvfc_async_crq crq;
- struct work_struct async_work_s;
-};
-
struct ibmvfc_async_subq {
volatile u8 valid;
#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
@@ -737,6 +731,15 @@ struct ibmvfc_async_subq {
} id;
} __packed __aligned(8);
+struct ibmvfc_async_work {
+ struct ibmvfc_host *vhost;
+ bool is_subq;
+ union {
+ struct ibmvfc_async_crq async_crq;
+ struct ibmvfc_async_subq subq;
+ } crq;
+ struct work_struct async_work_s;
+};
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
@@ -1008,7 +1011,7 @@ struct ibmvfc_host {
#endif
#ifdef VISIBLE_IF_KUNIT
-VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, struct ibmvfc_host *vhost);
+VISIBLE_IF_KUNIT void ibmvfc_handle_async(void *crq, struct ibmvfc_host *vhost, bool is_subq);
VISIBLE_IF_KUNIT struct list_head *ibmvfc_get_headp(void);
#endif
diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
index e41e2a49e549..6bde92197549 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
@@ -44,7 +44,7 @@ static void ibmvfc_async_fpin_test(struct kunit *test)
fc_host = shost_to_fc_host(vhost->host);
pre[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
- pre[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
+ pre[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn_device_specific);
pre[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
pre[IBMVFC_AE_FPIN_PORT_DEGRADED] = READ_ONCE(tgt->rport->fpin_stats.li_failure_unknown);
pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED] = READ_ONCE(fc_host->fpin_stats.cn_clear);
@@ -57,7 +57,8 @@ static void ibmvfc_async_fpin_test(struct kunit *test)
crq[fs].scsi_id = cpu_to_be64(tgt->scsi_id);
crq[fs].wwpn = cpu_to_be64(tgt->wwpn);
crq[fs].node_name = cpu_to_be64(tgt->ids.node_name);
- ibmvfc_handle_async(&crq[fs], vhost);
+ ibmvfc_handle_async(&crq[fs], vhost, false);
+ msleep(1U);
}
msleep(500U);
@@ -93,9 +94,8 @@ static void ibmvfc_async_fpin_test(struct kunit *test)
crq[0].scsi_id = cpu_to_be64(tgt->scsi_id);
crq[0].wwpn = cpu_to_be64(tgt->wwpn);
crq[0].node_name = cpu_to_be64(tgt->ids.node_name);
- ibmvfc_handle_async(&crq[0], vhost);
-
- msleep(500U);
+ ibmvfc_handle_async(&crq[0], vhost, false);
+ msleep(1U);
post[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
post[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
` (5 preceding siblings ...)
2026-07-02 17:59 ` [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:30 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 8/8] ibmvfc: handle extended FPIN events Dave Marquardt via B4 Relay
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Set up interrupt handling for asynchronous sub-queue, register the
asynchronous sub-queue as a channel, and enable its use by NPIV login.
---
drivers/scsi/ibmvscsi/ibmvfc.c | 128 ++++++++++++++++++++++++++++++++++-------
1 file changed, 106 insertions(+), 22 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 4678d76c84fe..36abca0bbd34 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1514,7 +1514,8 @@ 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 |
- IBMVFC_CAN_USE_NOOP_CMD);
+ IBMVFC_CAN_USE_NOOP_CMD | IBMVFC_YES_SCSI |
+ IBMVFC_USE_ASYNC_SUBQ | IBMVFC_CAN_HANDLE_FPIN);
if (vhost->mq_enabled || vhost->using_channels)
login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
@@ -4235,6 +4236,49 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
return crq;
}
+static void ibmvfc_drain_async_subq(struct ibmvfc_queue *scrq)
+{
+ struct ibmvfc_crq *crq;
+ unsigned long flags;
+ int done = 0;
+
+ spin_lock_irqsave(scrq->q_lock, flags);
+ while (!done) {
+ while ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
+ ibmvfc_handle_async(crq, scrq->vhost, true);
+ crq->valid = 0;
+ wmb(); /* complete write */
+ }
+
+ ibmvfc_toggle_scrq_irq(scrq, 1);
+ crq = ibmvfc_next_scrq(scrq);
+ if (crq != NULL) {
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_handle_async(crq, scrq->vhost, true);
+ crq->valid = 0;
+ wmb(); /* complete write */
+ } else
+ done = 1;
+ }
+ spin_unlock_irqrestore(scrq->q_lock, flags);
+}
+
+/**
+ * ibmvfc_interrupt_asyncq - Handle an async event from the adapter
+ * @irq: interrupt request
+ * @scrq_instance: async subq
+ *
+ **/
+static irqreturn_t ibmvfc_interrupt_async_subq(int irq, void *scrq_instance)
+{
+ struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance;
+
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_drain_async_subq(scrq);
+
+ return IRQ_HANDLED;
+}
+
static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)
{
struct ibmvfc_crq *crq;
@@ -6331,14 +6375,27 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
return retrc;
}
-static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
- struct ibmvfc_channels *channels,
- int index)
+static inline char *ibmvfc_channel_index(struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq,
+ char *buf, size_t bufsize)
+{
+ if (scrq < channels->scrqs || scrq >= channels->scrqs + channels->active_queues)
+ strscpy(buf, "async", 6);
+ else
+ snprintf(buf, bufsize, "%ld", scrq - channels->scrqs);
+ return buf;
+}
+
+static int ibmvfc_register_channel_handler(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq,
+ irq_handler_t irq)
{
struct device *dev = vhost->dev;
struct vio_dev *vdev = to_vio_dev(dev);
- struct ibmvfc_queue *scrq = &channels->scrqs[index];
+ long hcall_rc;
int rc = -ENOMEM;
+ char buf[16];
ENTER;
@@ -6357,20 +6414,23 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
if (!scrq->irq) {
rc = -EINVAL;
- dev_err(dev, "Error mapping sub-crq[%d] irq\n", index);
+ dev_err(dev, "Error mapping sub-crq[%s] irq\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
goto irq_failed;
}
switch (channels->protocol) {
case IBMVFC_PROTO_SCSI:
- snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%d",
- vdev->unit_address, index);
- scrq->handler = ibmvfc_interrupt_mq;
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%s",
+ vdev->unit_address,
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
+ scrq->handler = irq;
break;
case IBMVFC_PROTO_NVME:
- snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%d",
- vdev->unit_address, index);
- scrq->handler = ibmvfc_interrupt_mq;
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%s",
+ vdev->unit_address,
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
+ scrq->handler = irq;
break;
default:
dev_err(dev, "Unknown channel protocol (%d)\n",
@@ -6381,32 +6441,43 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
rc = request_irq(scrq->irq, scrq->handler, 0, scrq->name, scrq);
if (rc) {
- dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index);
+ dev_err(dev, "Couldn't register sub-crq[%s] irq\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
irq_dispose_mapping(scrq->irq);
+ scrq->irq = 0;
goto irq_failed;
}
- scrq->hwq_id = index;
+ if (scrq >= channels->scrqs && scrq < channels->scrqs + channels->max_queues)
+ scrq->hwq_id = scrq - channels->scrqs;
LEAVE;
return 0;
irq_failed:
do {
- rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
- } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+ hcall_rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
+ } while (hcall_rc == H_BUSY || H_IS_LONG_BUSY(hcall_rc));
reg_failed:
LEAVE;
return rc;
}
+static inline int
+ibmvfc_register_channel(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq)
+{
+ return ibmvfc_register_channel_handler(vhost, channels, scrq, ibmvfc_interrupt_mq);
+}
+
static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,
struct ibmvfc_channels *channels,
- int index)
+ struct ibmvfc_queue *scrq)
{
struct device *dev = vhost->dev;
struct vio_dev *vdev = to_vio_dev(dev);
- struct ibmvfc_queue *scrq = &channels->scrqs[index];
+ char buf[16];
long rc;
ENTER;
@@ -6421,7 +6492,8 @@ static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
if (rc)
- dev_err(dev, "Failed to free sub-crq[%d]: rc=%ld\n", index, rc);
+ dev_err(dev, "Failed to free sub-crq[%s]: rc=%ld\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)), rc);
/* Clean out the queue */
memset(scrq->msgs.crq, 0, PAGE_SIZE);
@@ -6439,10 +6511,21 @@ static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost,
if (!vhost->mq_enabled || !channels->scrqs)
return;
+ if (ibmvfc_register_channel_handler(vhost, channels,
+ channels->async_scrq,
+ ibmvfc_interrupt_async_subq)) {
+ vhost->do_enquiry = 0;
+ return;
+ }
+
for (i = 0; i < channels->max_queues; i++) {
- if (ibmvfc_register_channel(vhost, channels, i)) {
+ if (ibmvfc_register_channel(vhost, channels, &channels->scrqs[i])) {
for (j = i; j > 0; j--)
- ibmvfc_deregister_channel(vhost, channels, j - 1);
+ ibmvfc_deregister_channel(
+ vhost, channels, &channels->scrqs[j - 1]);
+ ibmvfc_deregister_channel(vhost, channels,
+ channels->async_scrq);
+
vhost->do_enquiry = 0;
return;
}
@@ -6461,7 +6544,8 @@ static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,
return;
for (i = 0; i < channels->max_queues; i++)
- ibmvfc_deregister_channel(vhost, channels, i);
+ ibmvfc_deregister_channel(vhost, channels, &channels->scrqs[i]);
+ ibmvfc_deregister_channel(vhost, channels, channels->async_scrq);
LEAVE;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 8/8] ibmvfc: handle extended FPIN events
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
` (6 preceding siblings ...)
2026-07-02 17:59 ` [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events Dave Marquardt via B4 Relay
@ 2026-07-02 17:59 ` Dave Marquardt via B4 Relay
2026-07-02 18:21 ` sashiko-bot
7 siblings, 1 reply; 17+ messages in thread
From: Dave Marquardt via B4 Relay @ 2026-07-02 17:59 UTC (permalink / raw)
To: James E.J. Bottomley, Martin K. Petersen, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Tyrel Datwyler
Cc: linux-kernel, linux-scsi, linuxppc-dev, Brian King, Greg Joyce,
Kyle Mahlkuch, Dave Marquardt
From: Dave Marquardt <davemarq@linux.ibm.com>
Add extended FPIN handling to ibmvfc driver. Tell VIOS ibmvfc can
handle extended FPIN messages, convert any received FPIN messages to
struct fc_els descriptors, and extend ibmvfc_process_async_work to
handle extended FPIN messages.
Signed-off-by: Dave Marquardt <davemarq@linux.ibm.com>
---
drivers/scsi/ibmvscsi/ibmvfc.c | 55 ++++++++++++++++-
drivers/scsi/ibmvscsi/ibmvfc.h | 31 ++++++++++
drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 114 ++++++++++++++++++++++++++++++++++-
3 files changed, 194 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 36abca0bbd34..93622cb76adc 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1515,7 +1515,8 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->capabilities =
cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN |
IBMVFC_CAN_USE_NOOP_CMD | IBMVFC_YES_SCSI |
- IBMVFC_USE_ASYNC_SUBQ | IBMVFC_CAN_HANDLE_FPIN);
+ IBMVFC_USE_ASYNC_SUBQ | IBMVFC_CAN_HANDLE_FPIN |
+ IBMVFC_CAN_HANDLE_FPIN_EXT);
if (vhost->mq_enabled || vhost->using_channels)
login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
@@ -3371,12 +3372,48 @@ ibmvfc_full_fpin_to_desc(struct ibmvfc_async_subq *ibmvfc_fpin)
cpu_to_be32(1));
}
+/**
+ * ibmvfc_ext_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct
+ * containing a descriptor.
+ * @ibmvfc_fpin: Pointer to async subq FPIN data
+ *
+ * Allocate a struct fc_els_fpin containing a descriptor and populate
+ * based on data from *ibmvfc_fpin.
+ *
+ * Return:
+ * NULL - unable to allocate structure
+ * non-NULL - pointer to populated struct fc_els_fpin
+ */
+static struct fc_els_fpin *
+ibmvfc_ext_fpin_to_desc(struct ibmvfc_async_subq_fpin *ibmvfc_fpin)
+{
+ u8 flags = ibmvfc_fpin->fpin_data.flags;
+ __be32 threshold = cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD);
+ __be16 modifier = 0;
+ __be32 count = cpu_to_be32(1);
+ __be16 type = 0;
+
+ if (flags & IBMVFC_FPIN_EVENT_TYPE_VALID)
+ type = ibmvfc_fpin->fpin_data.event_type;
+ if (flags & IBMVFC_FPIN_MODIFIER_VALID)
+ modifier = ibmvfc_fpin->fpin_data.event_type_modifier;
+ if (flags & IBMVFC_FPIN_THRESHOLD_VALID)
+ threshold = ibmvfc_fpin->fpin_data.event_threshold;
+ if (flags & IBMVFC_FPIN_EVENT_COUNT_VALID)
+ count = ibmvfc_fpin->fpin_data.event_data.event_count;
+
+ return ibmvfc_common_fpin_to_desc(ibmvfc_fpin->fpin_status,
+ ibmvfc_fpin->wwpn, type,
+ modifier, threshold, count);
+}
+
/**
* ibmvfc_process_async_work - Process IBMVFC_AE_FPIN async CRQ from work queue
* @work: pointer to work_struct
*/
static void ibmvfc_process_async_work(struct work_struct *work)
{
+ struct ibmvfc_async_subq_fpin *sqfpin;
struct ibmvfc_target *tgt, *next;
struct ibmvfc_async_subq *subq = NULL;
struct ibmvfc_async_work *aw;
@@ -3437,8 +3474,20 @@ static void ibmvfc_process_async_work(struct work_struct *work)
if (crq)
fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
- else
- fpin = ibmvfc_full_fpin_to_desc(subq);
+ else {
+ sqfpin = (struct ibmvfc_async_subq_fpin *)subq;
+ if ((subq->flags & IBMVFC_ASYNC_IS_FPIN_EXT) == 0) {
+ fpin = ibmvfc_full_fpin_to_desc(subq);
+ } else if (!(sqfpin->fpin_data.flags & IBMVFC_FPIN_EVENT_TYPE_VALID)) {
+ dev_err_ratelimited(vhost->dev,
+ "Invalid extended FPIN event received\n");
+ } else if (!ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_FPIN_EXT)) {
+ dev_err_ratelimited(vhost->dev,
+ "Unexpected extended FPIN event received\n");
+ } else {
+ fpin = ibmvfc_ext_fpin_to_desc(sqfpin);
+ }
+ }
if (fpin) {
fc_host_fpin_rcv(tgt->vhost->host,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index bbf19220af70..d9ee270e0ef9 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -184,6 +184,7 @@ struct ibmvfc_npiv_login {
#define IBMVFC_YES_SCSI 0x040
#define IBMVFC_USE_ASYNC_SUBQ 0x100
#define IBMVFC_CAN_USE_NOOP_CMD 0x200
+#define IBMVFC_CAN_HANDLE_FPIN_EXT 0x800
__be64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
@@ -233,6 +234,7 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_SUPPORT_SCSI 0x0200
#define IBMVFC_SUPPORT_ASYNC_SUBQ 0x0800
#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
+#define IBMVFC_SUPPORT_FPIN_EXT 0x2000
__be32 max_cmds;
__be32 scsi_id_sz;
__be64 max_dma_len;
@@ -715,6 +717,7 @@ struct ibmvfc_async_crq {
struct ibmvfc_async_subq {
volatile u8 valid;
#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
+#define IBMVFC_ASYNC_IS_FPIN_EXT 0x02
#define IBMVFC_FC_EEH 0x04
#define IBMVFC_FC_FW_UPDATE 0x08
#define IBMVFC_FC_FW_DUMP 0x10
@@ -731,6 +734,34 @@ struct ibmvfc_async_subq {
} id;
} __packed __aligned(8);
+struct ibmvfc_fpin_data {
+#define IBMVFC_FPIN_EVENT_TYPE_VALID 0x01
+#define IBMVFC_FPIN_MODIFIER_VALID 0x02
+#define IBMVFC_FPIN_THRESHOLD_VALID 0x04
+#define IBMVFC_FPIN_SEVERITY_VALID 0x08
+#define IBMVFC_FPIN_EVENT_COUNT_VALID 0x10
+ u8 flags;
+ u8 reserved[3];
+ __be16 event_type;
+ __be16 event_type_modifier;
+ __be32 event_threshold;
+ union {
+ u8 severity;
+ __be32 event_count;
+ } event_data;
+} __packed __aligned(8);
+
+struct ibmvfc_async_subq_fpin {
+ volatile u8 valid;
+ u8 flags;
+ u8 link_state;
+ u8 fpin_status;
+ __be16 event;
+ __be16 pad;
+ volatile __be64 wwpn;
+ struct ibmvfc_fpin_data fpin_data;
+} __packed __aligned(8);
+
struct ibmvfc_async_work {
struct ibmvfc_host *vhost;
bool is_subq;
diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
index 6bde92197549..fab36891dbc2 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
@@ -3,6 +3,7 @@
#include <kunit/visibility.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_els.h>
#include <linux/list.h>
#include <linux/delay.h>
#include "ibmvfc.h"
@@ -61,8 +62,6 @@ static void ibmvfc_async_fpin_test(struct kunit *test)
msleep(1U);
}
- msleep(500U);
-
post[IBMVFC_AE_FPIN_LINK_CONGESTED] = READ_ONCE(fc_host->fpin_stats.cn_device_specific);
post[IBMVFC_AE_FPIN_PORT_CONGESTED] = READ_ONCE(tgt->rport->fpin_stats.cn);
post[IBMVFC_AE_FPIN_PORT_CLEARED] = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
@@ -115,8 +114,117 @@ static void ibmvfc_async_fpin_test(struct kunit *test)
post[IBMVFC_AE_FPIN_CONGESTION_CLEARED]);
}
+#define IBMVFC_TEST_FPIN_EXT(fs, ev, stat, crq) { \
+ crq.valid = 0x80; \
+ crq.flags = IBMVFC_ASYNC_IS_FPIN_EXT; \
+ crq.link_state = IBMVFC_AE_LS_LINK_UP; \
+ crq.fpin_status = (fs); \
+ crq.event = cpu_to_be16(IBMVFC_AE_FPIN); \
+ crq.wwpn = cpu_to_be64(tgt->wwpn); \
+ crq.fpin_data.flags = IBMVFC_FPIN_EVENT_TYPE_VALID; \
+ crq.fpin_data.event_type = cpu_to_be16((ev)); \
+ pre = READ_ONCE(tgt->rport->fpin_stats.stat); \
+ ibmvfc_handle_async((struct ibmvfc_crq *)&crq, vhost, true); \
+ msleep(1U); \
+ post = READ_ONCE(tgt->rport->fpin_stats.stat); \
+}
+
+/**
+ * ibmvfc_extended_fpin_test - unit test for extended FPIN events
+ * @test: pointer to kunit structure
+ *
+ * Tests
+ *
+ * Return: void
+ */
+static void ibmvfc_extended_fpin_test(struct kunit *test)
+{
+ enum ibmvfc_ae_fpin_status fs;
+ struct ibmvfc_async_subq_fpin crq[IBMVFC_AE_FPIN_CONGESTION_CLEARED+1];
+ struct ibmvfc_async_subq_fpin
+ crqcn[IBMVFC_AE_FPIN_PORT_CONGESTED][FPIN_CONGN_DEVICE_SPEC+1];
+ struct ibmvfc_async_subq_fpin crqportdg[FPIN_LI_DEVICE_SPEC+1];
+ struct ibmvfc_target *tgt;
+ struct ibmvfc_host *vhost;
+ struct list_head *headp;
+ LIST_HEAD(evt_doneq);
+ u64 pre, post;
+
+ headp = ibmvfc_get_headp();
+ KUNIT_ASSERT_FALSE_MSG(test, list_empty(headp), "No ibmvfc devices available\n");
+ vhost = list_first_entry(headp, struct ibmvfc_host, queue);
+ KUNIT_ASSERT_GE_MSG(test, vhost->num_targets, 1, "No targets");
+
+ tgt = list_first_entry(&vhost->targets, struct ibmvfc_target, queue);
+ KUNIT_ASSERT_NOT_NULL(test, tgt->rport);
+
+ for (fs = IBMVFC_AE_FPIN_LINK_CONGESTED; fs <= IBMVFC_AE_FPIN_CONGESTION_CLEARED; fs++) {
+ switch (fs) {
+ case IBMVFC_AE_FPIN_PORT_CLEARED:
+ case IBMVFC_AE_FPIN_CONGESTION_CLEARED:
+ crq[fs].valid = 0x80;
+ crq[fs].flags = IBMVFC_ASYNC_IS_FPIN_EXT;
+ crq[fs].link_state = IBMVFC_AE_LS_LINK_UP;
+ crq[fs].fpin_status = fs;
+ crq[fs].event = cpu_to_be16(IBMVFC_AE_FPIN);
+ crq[fs].wwpn = cpu_to_be64(tgt->wwpn);
+ crq[fs].fpin_data.flags = IBMVFC_FPIN_EVENT_TYPE_VALID;
+ crq[fs].fpin_data.event_type = cpu_to_be16(FPIN_CONGN_CLEAR);
+ pre = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ ibmvfc_handle_async((struct ibmvfc_crq *)&crq[fs], vhost, true);
+ msleep(1U);
+ post = READ_ONCE(tgt->rport->fpin_stats.cn_clear);
+ break;
+ case IBMVFC_AE_FPIN_LINK_CONGESTED:
+ case IBMVFC_AE_FPIN_PORT_CONGESTED:
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_CONGN_CLEAR, cn_clear,
+ crqcn[fs-1][FPIN_CONGN_CLEAR]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_CONGN_LOST_CREDIT,
+ cn_lost_credit,
+ crqcn[fs-1][FPIN_CONGN_LOST_CREDIT]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_CONGN_CREDIT_STALL,
+ cn_credit_stall,
+ crqcn[fs-1][FPIN_CONGN_CREDIT_STALL]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_CONGN_OVERSUBSCRIPTION,
+ cn_oversubscription,
+ crqcn[fs-1][FPIN_CONGN_OVERSUBSCRIPTION]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_CONGN_DEVICE_SPEC,
+ cn_device_specific,
+ crqcn[fs-1][FPIN_CONGN_DEVICE_SPEC]);
+ break;
+ case IBMVFC_AE_FPIN_PORT_DEGRADED:
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_UNKNOWN,
+ li_failure_unknown,
+ crqportdg[FPIN_LI_UNKNOWN]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_LINK_FAILURE,
+ li_link_failure_count,
+ crqportdg[FPIN_LI_LINK_FAILURE]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_LOSS_OF_SYNC,
+ li_loss_of_sync_count,
+ crqportdg[FPIN_LI_LOSS_OF_SYNC]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_LOSS_OF_SIG,
+ li_loss_of_signals_count,
+ crqportdg[FPIN_LI_LOSS_OF_SIG]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_PRIM_SEQ_ERR,
+ li_prim_seq_err_count,
+ crqportdg[FPIN_LI_PRIM_SEQ_ERR]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_INVALID_TX_WD,
+ li_invalid_tx_word_count,
+ crqportdg[FPIN_LI_INVALID_TX_WD]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_INVALID_CRC,
+ li_invalid_crc_count,
+ crqportdg[FPIN_LI_INVALID_CRC]);
+ IBMVFC_TEST_FPIN_EXT(fs, FPIN_LI_DEVICE_SPEC,
+ li_device_specific,
+ crqportdg[FPIN_LI_DEVICE_SPEC]);
+ break;
+ }
+ }
+}
+
static struct kunit_case ibmvfc_fpin_test_cases[] = {
- KUNIT_CASE_SLOW(ibmvfc_async_fpin_test),
+ KUNIT_CASE(ibmvfc_async_fpin_test),
+ KUNIT_CASE(ibmvfc_extended_fpin_test),
{},
};
--
2.54.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v3 2/8] ibmvfc: Add NOOP command support
2026-07-02 17:59 ` [PATCH v3 2/8] ibmvfc: Add NOOP command support Dave Marquardt via B4 Relay
@ 2026-07-02 18:11 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:11 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Incomplete and insecure VFC_NOOP handling in `ibmvfc_handle_scrq` leads to protocol bypass and potential data corruption.
Pre-existing issues:
- [High] Untrusted Pointer Dereference in `ibmvfc_handle_scrq` leading to kernel panic.
--
commit 026aa88f6415105823a4b94ede2214dbab9c0f42
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: Add NOOP command support
This patch adds support for receiving and recognizing VFC_NOOP
messages from VIOS. This is done by defining the VFC_NOOP CRQ format,
recognizing the VFC_NOOP CRQ format in the CRQ handler, and setting
the "can use VFC_NOOP" bit in the capabilities sent during NPIV login.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index b63f41a6cfffd..110d77b9aac32 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -4099,6 +4109,10 @@ static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost
> return;
> }
>
> + /* Some CRQs, e.g. a VFC_NOOP command CRQ, do not have an ioba, so evt is NULL. */
> + if (!evt)
> + return;
> +
[Severity: High]
Does this blanket check safely handle VFC_NOOP messages?
If the VIOS sends a VFC_NOOP message with a valid, non-zero ioba that
corresponds to an active SCSI command, the sub-CRQ handler bypasses the
if (!evt) check. It will proceed to validate the event and erroneously
complete the active SCSI command using the NOOP's payload.
Additionally, this blanket check silently drops any other malformed zero-ioba
CRQs without logging them, and bypasses the IBMVFC_SUPPORT_NOOP_CMD capability
validation that is explicitly implemented in the main ibmvfc_handle_crq()
handler.
Should the Sub-CRQ handler explicitly verify crq->format == IBMVFC_VFC_NOOP
exactly like the main handler does?
[Severity: High]
This is a pre-existing issue, but can the event validation later in
ibmvfc_handle_scrq() cause a kernel panic due to a wild pointer dereference?
The ioba is cast directly to the evt pointer. If the hypervisor or VIOS sends
a Sub-CRQ message with a malicious or uninitialized non-zero ioba value, evt
is dereferenced before it is validated:
drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_handle_scrq() {
...
if (unlikely(!ibmvfc_valid_event(&evt->queue->evt_pool, evt))) {
...
}
Could the struct ibmvfc_queue be passed directly to the handler so
scrq->evt_pool can be used for validation without dereferencing the
untrusted evt pointer?
> /* The only kind of payload CRQs we should get are responses to
> * things we send. Make sure this response is to something we
> * actually sent
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue
2026-07-02 17:59 ` [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue Dave Marquardt via B4 Relay
@ 2026-07-02 18:12 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:12 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the driver reads `setup->num_scsi_subq_channels` directly from a hypervisor-modified DMA buffer without validating it against the allocated size of the internal queue array, leading to a potential heap buffer overflow.
--
commit caceab058540895b7bcd0e5a48d2dfd281f362ec
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: define asynchronous sub-queue
Defines the asynchronous sub-queue structure, modifies the existing
channel setup structure, adds the asynchronous sub-queue to the
channels structure, and adds flags needed to tell VIOS to use the
sub-queue.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
> index adfd67e85af86..f38dfae9924ca 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.h
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.h
[ ... ]
> @@ -579,6 +581,7 @@ struct ibmvfc_channel_setup {
> struct srp_direct_buf buffer;
> __be64 reserved2[5];
> __be64 channel_handles[IBMVFC_MAX_CHANNELS];
> + __be64 async_subq_handle;
[Severity: High]
This isn't a bug introduced by this patch, but since the driver reads
setup->num_scsi_subq_channels directly from a hypervisor-modified DMA buffer,
could this lead to a potential heap buffer overflow?
Looking at drivers/scsi/ibmvscsi/ibmvfc.c:ibmvfc_channel_setup_done():
active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
scrqs->active_queues = active_queues;
...
for (i = 0; i < active_queues; i++)
scrqs->scrqs[i].vios_cookie =
be64_to_cpu(setup->channel_handles[i]);
If the VIOS partition responds with an unexpectedly large value for
setup->num_scsi_subq_channels, would the driver write out of bounds into the
queue array without validating against the allocated size?
> } __packed __aligned(8);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events
2026-07-02 17:59 ` [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events Dave Marquardt via B4 Relay
@ 2026-07-02 18:14 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:14 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [Critical] Out-of-bounds Read and Type Confusion due to missing loop exit check.
- [High] Use-after-free race condition due to unlocked access without reference counting.
- [Medium] Unconditional read of `subq->id.node_name` without validating the `IBMVFC_ASYNC_ID_IS_ASSOC_ID` flag.
- [Low] Dead code due to impossible null check `(!crq && !subq)`.
Pre-existing issues:
- [High] Missing read-before-write memory barrier when yielding asynchronous queue descriptors.
--
commit a60f2aaa0542637dd0e4a584dadf2c01fa2e0409
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: extend async event handlers to handle async sub queue events
This patch extends the ibmvfc driver to process asynchronous sub-queue CRQs
alongside the existing async CRQs. It modifies ibmvfc_handle_async and
ibmvfc_process_async_work to handle the new sub-queue event structures.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 804887fb56357..4678d76c84fe2 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -3356,26 +3377,51 @@ static void ibmvfc_process_async_work(struct work_struct *work)
> {
[ ... ]
> aw = container_of(work, struct ibmvfc_async_work, async_work_s);
> vhost = aw->vhost;
> + is_subq = aw->is_subq;
> + if (is_subq)
> + subq = &aw->crq.subq;
> + else
> + crq = &aw->crq.async_crq;
>
> - if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
> + if (!crq && !subq) {
[Severity: Low]
Is this null check reachable? Since crq and subq are assigned the addresses
of fields inside the aw struct, it appears they can never evaluate to NULL,
making this check and the following error branch dead code.
> + dev_err_ratelimited(vhost->dev,
> + "FPIN event received, unable to process\n");
> + goto end;
> + }
> +
> + if (crq) {
> + wwpn = crq->wwpn;
> + node_name = crq->node_name;
> + scsi_id = crq->scsi_id;
> + } else {
> + wwpn = subq->wwpn;
> + node_name = subq->id.node_name;
[Severity: Medium]
Does this code safely read the node_name field?
The id field is a union of node_name and assoc_id. If the firmware sets the
IBMVFC_ASYNC_ID_IS_ASSOC_ID flag on this sub-queue event, wouldn't this
unconditional read of node_name result in comparing an association ID against
tgt->ids.node_name later on, potentially causing valid events to be discarded?
> + scsi_id = 0;
> + }
> +
> + if (!scsi_id && !wwpn && !node_name)
> goto end;
>
> spin_lock_irqsave(vhost->host->host_lock, flags);
> list_for_each_entry_safe(tgt, next, &vhost->targets, queue) {
> - if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
> + if (scsi_id && cpu_to_be64(tgt->scsi_id) != scsi_id)
> continue;
> - if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
> + if (wwpn && cpu_to_be64(tgt->ids.port_name) != wwpn)
> continue;
> - if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
> + if (node_name && cpu_to_be64(tgt->ids.node_name) != node_name)
> continue;
> if (!tgt->rport)
> continue;
> @@ -3388,41 +3434,71 @@ static void ibmvfc_process_async_work(struct work_struct *work)
> goto end;
> }
[Severity: Critical]
If the list iteration finishes without finding a match, doesn't tgt end up
pointing to the list head (&vhost->targets) rather than a valid target?
If so, the subsequent check if (!tgt || !tgt->rport) operates on this
invalid pointer. Since rport happens to be at an offset that might overlap
with valid data in the host struct, could this bypass the safety check
and lead to memory corruption when accessing tgt->wwpn or tgt->vhost->host
when creating the FPIN descriptor?
> - fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
[Severity: High]
Is it safe to drop the vhost->host->host_lock here without first acquiring
a reference to tgt via kref_get(&tgt->kref)?
Could a concurrent thread processing a logout execute ibmvfc_del_tgt and free
the target memory before this async work thread finishes accessing tgt->wwpn
and tgt->vhost->host?
[ ... ]
> @@ -3388,41 +3434,71 @@ VISIBLE_IF_KUNIT void ibmvfc_handle_async(void *crq,
> + if (is_subq) {
> + subq = crq;
> + event = be16_to_cpu(subq->event);
> + link_state = subq->link_state;
> + scsi_id = 0;
> + wwpn = subq->wwpn;
> + node_name = subq->id.node_name;
[Severity: Medium]
Similar to the question above in ibmvfc_process_async_work, is it safe to
read node_name here without first verifying the IBMVFC_ASYNC_ID_IS_ASSOC_ID
flag in subq->flags?
[ ... ]
> @@ -3492,16 +3568,26 @@ VISIBLE_IF_KUNIT void ibmvfc_handle_async(void *crq,
> default:
> - dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
> + dev_err(vhost->dev, "Unknown async event received: %llu\n", event);
> break;
> }
> +
> + if (is_subq)
> + subq->valid = 0;
> + else
> + async_crq->valid = 0;
> + wmb();
[Severity: High]
This is a pre-existing issue, but does this code need a full memory barrier
before clearing the valid bit?
The driver reads the DMA-mapped payload earlier in ibmvfc_handle_async, and
setting valid = 0 yields the slot back to the hardware. On architectures like
PowerPC, without a read-before-write mb() before the valid assignment, could
the CPU reorder the write before the payload reads, allowing hardware to
overwrite the event data while the driver is still processing it?
> }
> EXPORT_SYMBOL_IF_KUNIT(ibmvfc_handle_async);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=6
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 1/8] ibmvfc: add basic FPIN support
2026-07-02 17:59 ` [PATCH v3 1/8] ibmvfc: add basic FPIN support Dave Marquardt via B4 Relay
@ 2026-07-02 18:16 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:16 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Use-After-Free (UAF) on target structure during asynchronous FPIN processing.
- [High] Out-of-bounds Memory Access / Type Confusion due to misused list iterator.
- [Medium] Incorrect lifecycle cleanup ordering allows FPIN work item to run on a removed transport device.
- [Medium] The KUnit test unconditionally fails on systems without the IBM Virtual FC hardware instead of gracefully skipping.
--
commit ef1762da1f48d8a096031c317c4a9bf573f1b39e
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: add basic FPIN support
This commit adds basic FPIN message support to the ibmvfc driver. It adds
FPIN handling to the async event handler, offloads processing to a work
queue, and converts the VIOS FPIN messages to struct fc_els_fpin.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -3185,16 +3189,228 @@ static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
[ ... ]
> +static void ibmvfc_process_async_work(struct work_struct *work)
> +{
[ ... ]
> + spin_lock_irqsave(vhost->host->host_lock, flags);
> + list_for_each_entry_safe(tgt, next, &vhost->targets, queue) {
> + 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->rport)
> + continue;
> + break;
> + }
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> +
> + if (!tgt || !tgt->rport) {
[Severity: High]
Does this list iterator evaluation correctly determine if the target was found?
If an asynchronous FPIN event is received with a wwpn or scsi_id that does not
match any active target (for example, immediately after a target disconnects),
the list_for_each_entry_safe() loop finishes without executing a break.
At this point, the list iterator tgt points to the list head, not NULL.
The validation check if (!tgt || !tgt->rport) is bypassed because tgt is a
valid pointer, and its rport member maps to vhost->crq.msgs which contains a
valid initialized pointer. This causes the function to mistakenly treat the
vhost struct fields as a target struct, which can result in an out-of-bounds
read and a crash.
[Severity: Critical]
Is it possible for a concurrent system event to free the target structure here?
When the target is successfully matched in the loop, the spinlock
vhost->host->host_lock is immediately dropped. The function proceeds to access
tgt->wwpn and tgt->vhost->host to populate the FPIN descriptor without acquiring
a reference count via kref_get(&tgt->kref).
Because ibmvfc_process_async_work() runs asynchronously in fpin_workq, a
concurrent event (such as a link down or fabric logout) could acquire the
host lock, delete the target, and invoke kref_put(), freeing the memory for
tgt. The subsequent reads by this worker thread would then result in a
use-after-free.
> + dev_err_ratelimited(vhost->dev, "Invalid target for FPIN\n");
> + goto end;
> + }
[ ... ]
> @@ -6453,6 +6686,8 @@ static void ibmvfc_remove(struct vio_dev *vdev)
> ibmvfc_release_crq_queue(vhost);
>
> ibmvfc_free_mem(vhost);
> + destroy_workqueue(vhost->fpin_workq);
> + vhost->fpin_workq = NULL;
> spin_lock(&ibmvfc_driver_lock);
[Severity: Medium]
What prevents pending FPIN tasks from executing on removed infrastructure?
The destroy_workqueue(vhost->fpin_workq) call occurs at the very end of
ibmvfc_remove(), long after fc_remove_host(vhost->host) and
scsi_remove_host(vhost->host) have been called earlier in the lifecycle.
Because destroy_workqueue() flushes and executes all pending work items, any
queued asynchronous FPIN messages will be processed against a host whose rports
are deleted and whose sysfs transport representation has been torn down.
[ ... ]
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
> new file mode 100644
> index 0000000000000..e41e2a49e5497
> --- /dev/null
> +++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
[ ... ]
> @@ -0,0 +1,131 @@
[ ... ]
> +static void ibmvfc_async_fpin_test(struct kunit *test)
> +{
[ ... ]
> + headp = ibmvfc_get_headp();
> + KUNIT_ASSERT_FALSE_MSG(test, list_empty(headp), "No ibmvfc devices available\n");
[Severity: Medium]
Could this KUnit test gracefully skip instead of forcing a hard failure on
systems without the IBM Virtual FC hardware?
The test unconditionally asserts that ibmvfc_head is not empty. Executing this
module on architectures or systems lacking the specific IBM POWER Virtual FC
hardware directly invokes a failure instead of yielding via kunit_skip().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric
2026-07-02 17:59 ` [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric Dave Marquardt via B4 Relay
@ 2026-07-02 18:17 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:17 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] A recursive spinlock deadlock occurs if `ibmvfc_send_event()` fails to send the newly allocated Fabric Login MAD event.
--
commit f65f56e7064cfb403146e98d083f3a2365344ff8
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: make ibmvfc login to fabric
Add support for fabric login in order to support the asynchronous
event queue with its own interrupt as required by NPIV specification
to support the asynchronous sub-queue and interrupt and full and
extended FPIN messages.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 110d77b9aac32..6dd1e726d9b94 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -5261,6 +5261,88 @@ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
[ ... ]
> +static void ibmvfc_fabric_login(struct ibmvfc_host *vhost)
> +{
> + struct ibmvfc_fabric_login *mad;
> + struct ibmvfc_event *evt;
> + int level = IBMVFC_DEFAULT_LOG_LEVEL;
[ ... ]
> + ibmvfc_init_event(evt, ibmvfc_fabric_login_done, IBMVFC_MAD_FORMAT);
> + mad = &evt->iu.fabric_login;
> + memset(mad, 0, sizeof(*mad));
> + mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN);
> + mad->common.version = cpu_to_be32(1);
> + 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))
[Severity: High]
This is a pre-existing issue, but does this replicate a recursive spinlock
deadlock pattern if the event transmission fails?
If ibmvfc_send_event() encounters a failure (such as a hypervisor error), its
error path synchronously calls evt->done(evt).
Because this is initialized as a MAD event, the callback is handled by
ibmvfc_locked_done(), which unconditionally attempts to acquire
vhost->host->host_lock.
When called from an event completion handler like ibmvfc_channel_setup_done()
which already holds the host lock, could this sequence occur:
ibmvfc_channel_setup_done() (holds host_lock)
-> ibmvfc_fabric_login()
-> ibmvfc_send_event()
-> evt->done(evt) (which is ibmvfc_locked_done)
-> spin_lock_irqsave(evt->vhost->host->host_lock, flags)
and cause the CPU to deadlock?
> + ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 5/8] ibmvfc: allocate asynchronous sub-queue
2026-07-02 17:59 ` [PATCH v3 5/8] ibmvfc: allocate " Dave Marquardt via B4 Relay
@ 2026-07-02 18:20 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:20 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The dynamically allocated `channels->async_scrq` structure is leaked during channel release in `ibmvfc_release_channels()`.
- [High] Potential unconditional dereference of `async_scrq` in `ibmvfc_channel_setup_done()` when handling unexpected VIOS responses with MQ disabled.
--
commit ef672833546c2244a424ddb37eeea81b2e000303
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: allocate asynchronous sub-queue
Allocate and set up the asynchronous sub-queue for asynchronous
events, as required for full and extended FPIN support.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 6dd1e726d9b94..804887fb56357 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
> @@ -5371,6 +5371,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
> for (i = 0; i < active_queues; i++)
> scrqs->scrqs[i].vios_cookie =
> be64_to_cpu(setup->channel_handles[i]);
> + scrqs->async_scrq->vios_cookie =
> + be64_to_cpu(setup->async_subq_handle);
[Severity: High]
Does this code dereference a NULL pointer when MQ is disabled?
If vhost->mq_enabled is false, ibmvfc_alloc_channels() is skipped, leaving
scrqs->async_scrq as NULL. However, vhost->do_enquiry remains 1.
If the VIOS returns the IBMVFC_CAN_SUPPORT_CHANNELS capability,
ibmvfc_channel_setup() will be invoked with 0 channels and send
IBMVFC_CANCEL_CHANNELS. If the VIOS returns a success status but
unexpectedly omits the IBMVFC_CHANNELS_CANCELED flag in its response,
the driver falls through to the block containing this assignment.
Could this crash the kernel if the VIOS firmware responds unexpectedly?
[ ... ]
> @@ -6437,6 +6459,10 @@ static void ibmvfc_release_channels(struct ibmvfc_host *vhost,
>
> kfree(channels->scrqs);
> channels->scrqs = NULL;
> +
> + ibmvfc_free_queue(vhost, channels->async_scrq);
> + channels->async_scrq = NULL;
> +
[Severity: High]
Does this code leak the memory allocated for async_scrq?
In ibmvfc_alloc_channels(), async_scrq is allocated with kzalloc_obj().
During teardown here, ibmvfc_free_queue() correctly releases internal queue
resources, but kfree() is missing before setting the pointer to NULL.
Should kfree(channels->async_scrq) be called here similar to how
kfree(channels->scrqs) is handled above?
> channels->active_queues = 0;
> }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 8/8] ibmvfc: handle extended FPIN events
2026-07-02 17:59 ` [PATCH v3 8/8] ibmvfc: handle extended FPIN events Dave Marquardt via B4 Relay
@ 2026-07-02 18:21 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:21 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] Unconditional extraction of `node_name` from sub-CRQs causes an aliasing bug for extended FPIN events, leading to target lookup failures.
- [High] KUnit test structures for extended FPIN events are allocated on the stack without zero initialization.
- [Medium] KUnit tests lack validation assertions for statistics and suffer from race conditions due to an unsafe sleep-based wait.
Pre-existing issues:
- [Critical] Using the list iterator `tgt` after `list_for_each_entry_safe` terminates without a match causes severe out-of-bounds memory accesses.
- [High] Missing reference counting on `tgt` when accessed outside of `host_lock` leads to a potential Use-After-Free.
--
commit 5f5e44381670a22f5b1837898b25fb9f7a477edd
Author: Dave Marquardt <davemarq@linux.ibm.com>
Subject: ibmvfc: handle extended FPIN events
This commit adds extended FPIN handling to the ibmvfc driver. It tells the
VIOS that ibmvfc can handle extended FPIN messages and converts any received
FPIN messages to struct fc_els descriptors. It also extends
ibmvfc_process_async_work to handle these extended FPIN messages.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 36abca0bbd34d..93622cb76adc0 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
[ ... ]
> @@ -3437,8 +3474,20 @@ static void ibmvfc_process_async_work(struct work_struct *work)
>
> if (crq)
> fpin = ibmvfc_basic_fpin_to_desc(crq, tgt->wwpn);
> - else
> - fpin = ibmvfc_full_fpin_to_desc(subq);
> + else {
> + sqfpin = (struct ibmvfc_async_subq_fpin *)subq;
[Severity: High]
Does this function safely handle the node_name extraction for extended FPIN
events?
When an extended FPIN event is received, subq->id.node_name is read
unconditionally earlier in this function. For extended FPINs, this memory
region perfectly aliases with fpin_data.event_threshold and event_count.
This results in node_name being populated with this FPIN data. Will the
target lookup then compare the target's WWNN against this garbage node_name,
fail to find a match, and reject legitimate extended FPIN events?
[Severity: Critical]
This is a pre-existing issue, but does using tgt after the list lookup loop
terminates without a match cause out-of-bounds memory accesses?
If list_for_each_entry_safe() finishes without finding a match, tgt will
point to an invalid address computed from the list head. Since standard
Linux list iterators are not NULL when the loop terminates, a check like
(!tgt) will evaluate to false.
Could accessing tgt->wwpn or tgt->vhost here cause arbitrary memory reads
or a kernel panic?
[Severity: High]
This is a pre-existing issue, but is there a missing reference count on tgt
when it is accessed outside of the host_lock?
Earlier in the function, spin_unlock_irqrestore() drops the lock after
finding the target, but no kref is taken. If a concurrent teardown or link
down event frees the target, will accessing tgt->wwpn or tgt->vhost below
result in a use-after-free?
> + if ((subq->flags & IBMVFC_ASYNC_IS_FPIN_EXT) == 0) {
> + fpin = ibmvfc_full_fpin_to_desc(subq);
> + } else if (!(sqfpin->fpin_data.flags & IBMVFC_FPIN_EVENT_TYPE_VALID)) {
> + dev_err_ratelimited(vhost->dev,
> + "Invalid extended FPIN event received\n");
[ ... ]
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
> index 6bde921975492..fab36891dbc25 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
[ ... ]
> +#define IBMVFC_TEST_FPIN_EXT(fs, ev, stat, crq) { \
> + crq.valid = 0x80; \
> + crq.flags = IBMVFC_ASYNC_IS_FPIN_EXT; \
> + crq.link_state = IBMVFC_AE_LS_LINK_UP; \
> + crq.fpin_status = (fs); \
> + crq.event = cpu_to_be16(IBMVFC_AE_FPIN); \
> + crq.wwpn = cpu_to_be64(tgt->wwpn); \
> + crq.fpin_data.flags = IBMVFC_FPIN_EVENT_TYPE_VALID; \
> + crq.fpin_data.event_type = cpu_to_be16((ev)); \
> + pre = READ_ONCE(tgt->rport->fpin_stats.stat); \
> + ibmvfc_handle_async((struct ibmvfc_crq *)&crq, vhost, true); \
> + msleep(1U); \
> + post = READ_ONCE(tgt->rport->fpin_stats.stat); \
> +}
[Severity: Medium]
Does this macro actually verify the post-condition statistics?
The post statistics are read, but there are no KUNIT_EXPECT_* assertions to
validate them against the pre statistics. Will this allow the test to silently
pass even if the driver logic fails?
Additionally, is using msleep(1U) to wait for the asynchronous work queue to
process the event race-prone?
> +
> +/**
> + * ibmvfc_extended_fpin_test - unit test for extended FPIN events
> + * @test: pointer to kunit structure
> + *
> + * Tests
> + *
> + * Return: void
> + */
> +static void ibmvfc_extended_fpin_test(struct kunit *test)
> +{
> + enum ibmvfc_ae_fpin_status fs;
> + struct ibmvfc_async_subq_fpin crq[IBMVFC_AE_FPIN_CONGESTION_CLEARED+1];
[Severity: High]
Are these arrays of struct ibmvfc_async_subq_fpin safely initialized?
These arrays are allocated on the stack. The IBMVFC_TEST_FPIN_EXT macro
partially initializes them but leaves fields like fpin_data.event_threshold
uninitialized.
Could this stack garbage be passed to the driver, causing erratic test
behavior depending on previous stack contents?
> + struct ibmvfc_async_subq_fpin
> + crqcn[IBMVFC_AE_FPIN_PORT_CONGESTED][FPIN_CONGN_DEVICE_SPEC+1];
> + struct ibmvfc_async_subq_fpin crqportdg[FPIN_LI_DEVICE_SPEC+1];
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=8
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events
2026-07-02 17:59 ` [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events Dave Marquardt via B4 Relay
@ 2026-07-02 18:30 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-07-02 18:30 UTC (permalink / raw)
To: Dave Marquardt; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] Missing `host_lock` acquisition in `ibmvfc_drain_async_subq` leads to data races and kernel panics.
- [High] Invalid pointer dereference due to `list_for_each_entry_safe` cursor leak in `ibmvfc_process_async_work` triggers a kernel panic.
- [Medium] Protocol violation: `IBMVFC_USE_ASYNC_SUBQ` capability is unconditionally advertised to the VIOS.
- [Medium] Memory leak of `channels->async_scrq` in `ibmvfc_release_channels`.
- [Low] Incorrect queue naming in `ibmvfc_channel_index` due to `active_queues` being 0 during registration.
- [Low] The `async_scrq` is not assigned an `hwq_id`, leaving it at 0 and causing misleading debug logs.
--
commit 1426674b04407a5087c581ceae1dd979f487b41b
Author: Dave Marquardt <davemarq@linux.ibm.com>
ibmvfc: register and use asynchronous sub-queue for events
Set up interrupt handling for asynchronous sub-queue, register the
asynchronous sub-queue as a channel, and enable its use by NPIV login.
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index 4678d76c84fe2..36abca0bbd34d 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
> @@ -1514,7 +1514,8 @@ 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 |
> - IBMVFC_CAN_USE_NOOP_CMD);
> + IBMVFC_CAN_USE_NOOP_CMD | IBMVFC_YES_SCSI |
> + IBMVFC_USE_ASYNC_SUBQ | IBMVFC_CAN_HANDLE_FPIN);
[Severity: Medium]
Should IBMVFC_USE_ASYNC_SUBQ only be advertised when vhost->mq_enabled is
true?
If the user disables MQ, the async sub-queue is never registered, but the
VIOS might still attempt to send async sub-queue formatted events, falling
back to the legacy async_crq. The driver processes legacy CRQ events with
is_subq=false expecting the legacy layout, which could lead to incorrect
event processing.
>
> if (vhost->mq_enabled || vhost->using_channels)
> login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
[ ... ]
> @@ -4235,6 +4236,49 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
> return crq;
> }
>
> +static void ibmvfc_drain_async_subq(struct ibmvfc_queue *scrq)
> +{
> + struct ibmvfc_crq *crq;
> + unsigned long flags;
> + int done = 0;
> +
> + spin_lock_irqsave(scrq->q_lock, flags);
> + while (!done) {
> + while ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
> + ibmvfc_handle_async(crq, scrq->vhost, true);
[Severity: High]
Does this code call ibmvfc_handle_async() without holding the host_lock?
ibmvfc_handle_async() can modify host state and invoke functions like
__ibmvfc_reset_host(), which traverses vhost->targets. The global
vhost->host->host_lock is required to protect these lists. Executing them
concurrently without it could result in list corruption and kernel panics.
> + crq->valid = 0;
> + wmb(); /* complete write */
> + }
> +
> + ibmvfc_toggle_scrq_irq(scrq, 1);
> + crq = ibmvfc_next_scrq(scrq);
> + if (crq != NULL) {
> + ibmvfc_toggle_scrq_irq(scrq, 0);
> + ibmvfc_handle_async(crq, scrq->vhost, true);
> + crq->valid = 0;
> + wmb(); /* complete write */
> + } else
> + done = 1;
> + }
> + spin_unlock_irqrestore(scrq->q_lock, flags);
> +}
> +
[ ... ]
> @@ -6331,14 +6375,27 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
> return retrc;
> }
>
> -static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
> - struct ibmvfc_channels *channels,
> - int index)
> +static inline char *ibmvfc_channel_index(struct ibmvfc_channels *channels,
> + struct ibmvfc_queue *scrq,
> + char *buf, size_t bufsize)
> +{
> + if (scrq < channels->scrqs || scrq >= channels->scrqs + channels->active_queues)
[Severity: Low]
When ibmvfc_reg_sub_crqs() is executed during probe, is
channels->active_queues still 0 here?
If so, the condition will evaluate to true for all SCSI hardware queues,
incorrectly naming all sub-queues as "ibmvfc-<unit>-scsiasync" instead of
assigning their numeric index. This could break /proc/interrupts
identification.
> + strscpy(buf, "async", 6);
> + else
> + snprintf(buf, bufsize, "%ld", scrq - channels->scrqs);
> + return buf;
> +}
> +
> +static int ibmvfc_register_channel_handler(struct ibmvfc_host *vhost,
[ ... ]
> @@ -6381,32 +6441,43 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
> rc = request_irq(scrq->irq, scrq->handler, 0, scrq->name, scrq);
>
> if (rc) {
> - dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index);
> + dev_err(dev, "Couldn't register sub-crq[%s] irq\n",
> + ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
> irq_dispose_mapping(scrq->irq);
> + scrq->irq = 0;
> goto irq_failed;
> }
>
> - scrq->hwq_id = index;
> + if (scrq >= channels->scrqs && scrq < channels->scrqs + channels->max_queues)
> + scrq->hwq_id = scrq - channels->scrqs;
[Severity: Low]
Does this exclude channels->async_scrq from receiving an hwq_id?
Since it is allocated as a standalone struct outside the scrqs array, its
hwq_id would remain 0. Subsequent errors on the async queue would log
against sub-crq[0], which could mislead debugging efforts toward the first
SCSI queue.
>
> LEAVE;
> return 0;
[ ... ]
> @@ -6461,7 +6544,8 @@ static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,
> return;
>
> for (i = 0; i < channels->max_queues; i++)
> - ibmvfc_deregister_channel(vhost, channels, i);
> + ibmvfc_deregister_channel(vhost, channels, &channels->scrqs[i]);
> + ibmvfc_deregister_channel(vhost, channels, channels->async_scrq);
>
> LEAVE;
> }
[Severity: Medium]
Since channels->async_scrq is dynamically allocated, does
ibmvfc_release_channels() leak this memory?
When the driver tears down the channels in ibmvfc_release_channels(), it
calls ibmvfc_free_queue(vhost, channels->async_scrq) and then sets
channels->async_scrq = NULL without ever calling kfree(), which permanently
leaks the struct memory.
[Severity: High]
This is a pre-existing issue, but I have a question about cursor leaks in
ibmvfc_process_async_work().
ibmvfc_process_async_work() searches the vhost->targets list using
list_for_each_entry_safe(). If no target is found, the 'tgt' cursor exits
the loop pointing to the list head, not NULL. Does the subsequent check:
if (!tgt || !tgt->rport) {
evaluate this leaked cursor? Because tgt->rport evaluates to an
out-of-bounds offset that aliases vhost->sg_pool, the check might
incorrectly succeed. The function would then access out-of-bounds
properties to process an event, feeding a corrupted pointer into
fc_host_fpin_rcv() and causing a kernel panic.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260702-ibmvfc-fpin-support-v3-0-d95b9547cf88@linux.ibm.com?part=7
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-07-02 18:30 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 17:59 [PATCH v3 0/8] ibmvfc: make ibmvfc support FPIN messages Dave Marquardt via B4 Relay
2026-07-02 17:59 ` [PATCH v3 1/8] ibmvfc: add basic FPIN support Dave Marquardt via B4 Relay
2026-07-02 18:16 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 2/8] ibmvfc: Add NOOP command support Dave Marquardt via B4 Relay
2026-07-02 18:11 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 3/8] ibmvfc: make ibmvfc login to fabric Dave Marquardt via B4 Relay
2026-07-02 18:17 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 4/8] ibmvfc: define asynchronous sub-queue Dave Marquardt via B4 Relay
2026-07-02 18:12 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 5/8] ibmvfc: allocate " Dave Marquardt via B4 Relay
2026-07-02 18:20 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 6/8] ibmvfc: extend async event handlers to handle async sub queue events Dave Marquardt via B4 Relay
2026-07-02 18:14 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events Dave Marquardt via B4 Relay
2026-07-02 18:30 ` sashiko-bot
2026-07-02 17:59 ` [PATCH v3 8/8] ibmvfc: handle extended FPIN events Dave Marquardt via B4 Relay
2026-07-02 18:21 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox