[PATCH v3 7/8] ibmvfc: register and use asynchronous sub-queue for events
From: Dave Marquardt via B4 Relay <devnull+davemarq.linux.ibm.com@kernel.org>
Date: 2026-07-02 17:59:39
Also in:
b4-sent, linux-scsi, lkml
Subsystem:
ibm power virtual fc device drivers, linux for powerpc (32-bit and 64-bit), scsi subsystem, the rest · Maintainers:
Tyrel Datwyler, Madhavan Srinivasan, Michael Ellerman, "James E.J. Bottomley", "Martin K. Petersen", Linus Torvalds
From: Dave Marquardt <redacted> 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