From: Dave Marquardt <[email protected]>

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



Reply via email to