Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[platform/kernel/linux-rpi.git] / drivers / scsi / ibmvscsi / ibmvfc.c
index e663085..1b68734 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/bsg-lib.h>
 #include <asm/firmware.h>
 #include <asm/irq.h>
+#include <asm/rtas.h>
 #include <asm/vio.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -158,6 +159,9 @@ static void ibmvfc_npiv_logout(struct ibmvfc_host *);
 static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *);
 static void ibmvfc_tgt_move_login(struct ibmvfc_target *);
 
+static void ibmvfc_release_sub_crqs(struct ibmvfc_host *);
+static void ibmvfc_init_sub_crqs(struct ibmvfc_host *);
+
 static const char *unknown_error = "unknown error";
 
 static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
@@ -899,6 +903,9 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
 {
        int rc = 0;
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
+       unsigned long flags;
+
+       ibmvfc_release_sub_crqs(vhost);
 
        /* Re-enable the CRQ */
        do {
@@ -910,6 +917,15 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
        if (rc)
                dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
 
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       spin_lock(vhost->crq.q_lock);
+       vhost->do_enquiry = 1;
+       vhost->using_channels = 0;
+       spin_unlock(vhost->crq.q_lock);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       ibmvfc_init_sub_crqs(vhost);
+
        return rc;
 }
 
@@ -926,8 +942,8 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
        unsigned long flags;
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
        struct ibmvfc_queue *crq = &vhost->crq;
-       struct ibmvfc_queue *scrq;
-       int i;
+
+       ibmvfc_release_sub_crqs(vhost);
 
        /* Close the CRQ */
        do {
@@ -947,16 +963,6 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
        memset(crq->msgs.crq, 0, PAGE_SIZE);
        crq->cur = 0;
 
-       if (vhost->scsi_scrqs.scrqs) {
-               for (i = 0; i < nr_scsi_hw_queues; i++) {
-                       scrq = &vhost->scsi_scrqs.scrqs[i];
-                       spin_lock(scrq->q_lock);
-                       memset(scrq->msgs.scrq, 0, PAGE_SIZE);
-                       scrq->cur = 0;
-                       spin_unlock(scrq->q_lock);
-               }
-       }
-
        /* And re-open it again */
        rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
                                crq->msg_token, PAGE_SIZE);
@@ -966,9 +972,12 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
                dev_warn(vhost->dev, "Partner adapter not ready\n");
        else if (rc != 0)
                dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
+
        spin_unlock(vhost->crq.q_lock);
        spin_unlock_irqrestore(vhost->host->host_lock, flags);
 
+       ibmvfc_init_sub_crqs(vhost);
+
        return rc;
 }
 
@@ -5642,7 +5651,8 @@ static int ibmvfc_register_scsi_channel(struct ibmvfc_host *vhost,
        rc = h_reg_sub_crq(vdev->unit_address, scrq->msg_token, PAGE_SIZE,
                           &scrq->cookie, &scrq->hw_irq);
 
-       if (rc) {
+       /* H_CLOSED indicates successful register, but no CRQ partner */
+       if (rc && rc != H_CLOSED) {
                dev_warn(dev, "Error registering sub-crq: %d\n", rc);
                if (rc == H_PARAMETER)
                        dev_warn_once(dev, "Firmware may not support MQ\n");
@@ -5675,8 +5685,8 @@ static int ibmvfc_register_scsi_channel(struct ibmvfc_host *vhost,
 
 irq_failed:
        do {
-               plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
-       } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+               rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
+       } while (rtas_busy_delay(rc));
 reg_failed:
        ibmvfc_free_queue(vhost, scrq);
        LEAVE;
@@ -5694,6 +5704,7 @@ static void ibmvfc_deregister_scsi_channel(struct ibmvfc_host *vhost, int index)
 
        free_irq(scrq->irq, scrq);
        irq_dispose_mapping(scrq->irq);
+       scrq->irq = 0;
 
        do {
                rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address,
@@ -5707,17 +5718,21 @@ static void ibmvfc_deregister_scsi_channel(struct ibmvfc_host *vhost, int index)
        LEAVE;
 }
 
-static int ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
+static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
 {
        int i, j;
 
        ENTER;
+       if (!vhost->mq_enabled)
+               return;
 
        vhost->scsi_scrqs.scrqs = kcalloc(nr_scsi_hw_queues,
                                          sizeof(*vhost->scsi_scrqs.scrqs),
                                          GFP_KERNEL);
-       if (!vhost->scsi_scrqs.scrqs)
-               return -1;
+       if (!vhost->scsi_scrqs.scrqs) {
+               vhost->do_enquiry = 0;
+               return;
+       }
 
        for (i = 0; i < nr_scsi_hw_queues; i++) {
                if (ibmvfc_register_scsi_channel(vhost, i)) {
@@ -5726,13 +5741,12 @@ static int ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
                        kfree(vhost->scsi_scrqs.scrqs);
                        vhost->scsi_scrqs.scrqs = NULL;
                        vhost->scsi_scrqs.active_queues = 0;
-                       LEAVE;
-                       return -1;
+                       vhost->do_enquiry = 0;
+                       break;
                }
        }
 
        LEAVE;
-       return 0;
 }
 
 static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
@@ -5999,11 +6013,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
                goto remove_shost;
        }
 
-       if (vhost->mq_enabled) {
-               rc = ibmvfc_init_sub_crqs(vhost);
-               if (rc)
-                       dev_warn(dev, "Failed to allocate Sub-CRQs. rc=%d\n", rc);
-       }
+       ibmvfc_init_sub_crqs(vhost);
 
        if (shost_to_fc_host(shost)->rqst_q)
                blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1);