From: Dave Marquardt <[email protected]>

Complete async sub-queue integration by setting up interrupt handling,
registering the queue as a channel, and enabling its use during NPIV
login.

Add ibmvfc_interrupt_async_subq() interrupt handler and
ibmvfc_drain_async_subq() to process events from the async sub-queue.
Refactor ibmvfc_register_channel() into ibmvfc_register_channel_common()
to support both regular sub-CRQs and the async sub-queue with different
interrupt handlers.

Update ibmvfc_set_login_info() to set IBMVFC_CAN_USE_CHANNELS,
IBMVFC_YES_SCSI, IBMVFC_USE_ASYNC_SUBQ, and IBMVFC_CAN_HANDLE_FPIN
capability bits when channels are enabled, informing VIOS that the client
supports async sub-queue and FPIN handling.

Register async_scrq during channel initialization and unregister during
cleanup.
---
 drivers/scsi/ibmvscsi/ibmvfc.c | 146 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 124 insertions(+), 22 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index ee56f13f1a97..eb786ac24274 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1517,7 +1517,9 @@ static void ibmvfc_set_login_info(struct ibmvfc_host 
*vhost)
                            IBMVFC_CAN_USE_NOOP_CMD);
 
        if (vhost->mq_enabled || vhost->using_channels)
-               login_info->capabilities |= 
cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+               login_info->capabilities |=
+                       cpu_to_be64(IBMVFC_CAN_USE_CHANNELS | IBMVFC_YES_SCSI |
+                                   IBMVFC_USE_ASYNC_SUBQ | 
IBMVFC_CAN_HANDLE_FPIN);
 
        login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
        login_info->async.len = cpu_to_be32(async_crq->size *
@@ -4243,6 +4245,52 @@ 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_host *vhost = scrq->vhost;
+       struct ibmvfc_crq *crq;
+       unsigned long flags;
+       int done = 0;
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       spin_lock(scrq->q_lock);
+       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(scrq->q_lock);
+       spin_unlock_irqrestore(vhost->host->host_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;
@@ -6340,14 +6388,29 @@ 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)
+/**
+ * ibmvfc_register_channel_common - Register a sub-CRQ with the hypervisor
+ * @vhost:     ibmvfc host struct
+ * @channels:  ibmvfc channels struct
+ * @scrq:      sub-CRQ to register
+ * @index:     channel index (negative for async)
+ * @irq:       interrupt handler for the sub-CRQ
+ *
+ * Return value:
+ *     0 on success / non-zero on failure
+ **/
+static int ibmvfc_register_channel_common(struct ibmvfc_host *vhost,
+                                         struct ibmvfc_channels *channels,
+                                         struct ibmvfc_queue *scrq,
+                                         int index,
+                                         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;
+       const char *name_suffix;
+       bool is_async = (index < 0);
 
        ENTER;
 
@@ -6366,20 +6429,19 @@ 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);
+               if (is_async)
+                       dev_err(dev, "Error mapping sub-crq[%s] irq\n", 
"async");
+               else
+                       dev_err(dev, "Error mapping sub-crq[%d] irq\n", index);
                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;
+               name_suffix = "scsi";
                break;
        case IBMVFC_PROTO_NVME:
-               snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%d",
-                        vdev->unit_address, index);
-               scrq->handler = ibmvfc_interrupt_mq;
+               name_suffix = "nvmf";
                break;
        default:
                dev_err(dev, "Unknown channel protocol (%d)\n",
@@ -6387,35 +6449,63 @@ static int ibmvfc_register_channel(struct ibmvfc_host 
*vhost,
                goto irq_failed;
        }
 
+       if (is_async) {
+               snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-%s%s",
+                        vdev->unit_address, name_suffix, "async");
+               scrq->handler = irq;
+       } else {
+               snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-%s%d",
+                        vdev->unit_address, name_suffix, index);
+               scrq->handler = irq ? irq : ibmvfc_interrupt_mq;
+               scrq->hwq_id = index;
+       }
+
        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);
+               if (is_async)
+                       dev_err(dev, "Couldn't register sub-crq[%s] irq\n", 
"async");
+               else
+                       dev_err(dev, "Couldn't register sub-crq[%d] irq\n", 
index);
                irq_dispose_mapping(scrq->irq);
                goto irq_failed;
        }
 
-       scrq->hwq_id = index;
-
        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 int ibmvfc_register_channel_async(struct ibmvfc_host *vhost,
+                                          struct ibmvfc_channels *channels,
+                                          struct ibmvfc_queue *scrq,
+                                          irq_handler_t irq)
+{
+       return ibmvfc_register_channel_common(vhost, channels, scrq, -1, irq);
+}
+
+static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
+                                  struct ibmvfc_channels *channels,
+                                  int index)
+{
+       struct ibmvfc_queue *scrq = &channels->scrqs[index];
+
+       return ibmvfc_register_channel_common(vhost, channels, scrq, index, 
NULL);
+}
+
 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];
        long rc;
 
        ENTER;
@@ -6430,7 +6520,7 @@ 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", 
scrq->name, rc);
 
        /* Clean out the queue */
        memset(scrq->msgs.crq, 0, PAGE_SIZE);
@@ -6448,10 +6538,21 @@ static void ibmvfc_reg_sub_crqs(struct ibmvfc_host 
*vhost,
        if (!vhost->mq_enabled || !channels->scrqs)
                return;
 
+       if (ibmvfc_register_channel_async(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)) {
                        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;
                }
@@ -6470,7 +6571,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.55.0



Reply via email to