Drain the command queue and place all commands on a completion list.
Perform command completion on that list outside the host/queue locks.
Further, move purged command compeletions outside the host_lock as well.

Signed-off-by: Tyrel Datwyler <tyr...@linux.ibm.com>
Reviewed-by: Brian King <brk...@linux.vnet.ibm.com>
---
 drivers/scsi/ibmvscsi/ibmvfc.c | 58 ++++++++++++++++++++++++++--------
 drivers/scsi/ibmvscsi/ibmvfc.h |  3 +-
 2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 69a6401ca504..f680f96d5d06 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -894,7 +894,7 @@ static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
  * @purge_list:                list head of failed commands
  *
  * This function runs completions on commands to fail as a result of a
- * host reset or platform migration. Caller must hold host_lock.
+ * host reset or platform migration.
  **/
 static void ibmvfc_complete_purge(struct list_head *purge_list)
 {
@@ -1407,6 +1407,23 @@ static struct ibmvfc_event *ibmvfc_get_event(struct 
ibmvfc_queue *queue)
        return evt;
 }
 
+/**
+ * ibmvfc_locked_done - Calls evt completion with host_lock held
+ * @evt:       ibmvfc evt to complete
+ *
+ * All non-scsi command completion callbacks have the expectation that the
+ * host_lock is held. This callback is used by ibmvfc_init_event to wrap a
+ * MAD evt with the host_lock.
+ **/
+static void ibmvfc_locked_done(struct ibmvfc_event *evt)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(evt->vhost->host->host_lock, flags);
+       evt->_done(evt);
+       spin_unlock_irqrestore(evt->vhost->host->host_lock, flags);
+}
+
 /**
  * ibmvfc_init_event - Initialize fields in an event struct that are always
  *                             required.
@@ -1419,9 +1436,14 @@ static void ibmvfc_init_event(struct ibmvfc_event *evt,
 {
        evt->cmnd = NULL;
        evt->sync_iu = NULL;
-       evt->crq.format = format;
-       evt->done = done;
        evt->eh_comp = NULL;
+       evt->crq.format = format;
+       if (format == IBMVFC_CMD_FORMAT)
+               evt->done = done;
+       else {
+               evt->_done = done;
+               evt->done = ibmvfc_locked_done;
+       }
 }
 
 /**
@@ -1640,7 +1662,9 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
        struct ibmvfc_host *vhost = shost_priv(sdev->host);
        struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
        struct ibmvfc_target *tgt;
+       unsigned long flags;
 
+       spin_lock_irqsave(vhost->host->host_lock, flags);
        list_for_each_entry(tgt, &vhost->targets, queue) {
                if (rport == tgt->rport) {
                        ibmvfc_del_tgt(tgt);
@@ -1649,6 +1673,7 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
        }
 
        ibmvfc_reinit_host(vhost);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
 }
 
 /**
@@ -2901,7 +2926,8 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq 
*crq,
  * @vhost:     ibmvfc host struct
  *
  **/
-static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host 
*vhost)
+static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host 
*vhost,
+                             struct list_head *evt_doneq)
 {
        long rc;
        struct ibmvfc_event *evt = (struct ibmvfc_event 
*)be64_to_cpu(crq->ioba);
@@ -2972,12 +2998,9 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, 
struct ibmvfc_host *vhost)
                return;
        }
 
-       del_timer(&evt->timer);
        spin_lock(&evt->queue->l_lock);
-       list_del(&evt->queue_list);
+       list_move_tail(&evt->queue_list, evt_doneq);
        spin_unlock(&evt->queue->l_lock);
-       ibmvfc_trc_end(evt);
-       evt->done(evt);
 }
 
 /**
@@ -3364,8 +3387,10 @@ static void ibmvfc_tasklet(void *data)
        struct vio_dev *vdev = to_vio_dev(vhost->dev);
        struct ibmvfc_crq *crq;
        struct ibmvfc_async_crq *async;
+       struct ibmvfc_event *evt, *temp;
        unsigned long flags;
        int done = 0;
+       LIST_HEAD(evt_doneq);
 
        spin_lock_irqsave(vhost->host->host_lock, flags);
        spin_lock(vhost->crq.q_lock);
@@ -3379,7 +3404,7 @@ static void ibmvfc_tasklet(void *data)
 
                /* Pull all the valid messages off the CRQ */
                while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
-                       ibmvfc_handle_crq(crq, vhost);
+                       ibmvfc_handle_crq(crq, vhost, &evt_doneq);
                        crq->valid = 0;
                        wmb();
                }
@@ -3392,7 +3417,7 @@ static void ibmvfc_tasklet(void *data)
                        wmb();
                } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
                        vio_disable_interrupts(vdev);
-                       ibmvfc_handle_crq(crq, vhost);
+                       ibmvfc_handle_crq(crq, vhost, &evt_doneq);
                        crq->valid = 0;
                        wmb();
                } else
@@ -3401,6 +3426,13 @@ static void ibmvfc_tasklet(void *data)
 
        spin_unlock(vhost->crq.q_lock);
        spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
+               del_timer(&evt->timer);
+               list_del(&evt->queue_list);
+               ibmvfc_trc_end(evt);
+               evt->done(evt);
+       }
 }
 
 /**
@@ -4790,8 +4822,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
        case IBMVFC_HOST_ACTION_RESET:
                vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
                list_splice_init(&vhost->purge, &purge);
-               ibmvfc_complete_purge(&purge);
                spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               ibmvfc_complete_purge(&purge);
                rc = ibmvfc_reset_crq(vhost);
                spin_lock_irqsave(vhost->host->host_lock, flags);
                if (rc == H_CLOSED)
@@ -4805,8 +4837,8 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
        case IBMVFC_HOST_ACTION_REENABLE:
                vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
                list_splice_init(&vhost->purge, &purge);
-               ibmvfc_complete_purge(&purge);
                spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               ibmvfc_complete_purge(&purge);
                rc = ibmvfc_reenable_crq_queue(vhost);
                spin_lock_irqsave(vhost->host->host_lock, flags);
                if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
@@ -5369,8 +5401,8 @@ static int ibmvfc_remove(struct vio_dev *vdev)
        spin_lock_irqsave(vhost->host->host_lock, flags);
        ibmvfc_purge_requests(vhost, DID_ERROR);
        list_splice_init(&vhost->purge, &purge);
-       ibmvfc_complete_purge(&purge);
        spin_unlock_irqrestore(vhost->host->host_lock, flags);
+       ibmvfc_complete_purge(&purge);
        ibmvfc_free_event_pool(vhost, &vhost->crq);
 
        ibmvfc_free_mem(vhost);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index faf5b50d65b9..632e977449c5 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -733,7 +733,8 @@ struct ibmvfc_event {
        struct scsi_cmnd *cmnd;
        atomic_t free;
        union ibmvfc_iu *xfer_iu;
-       void (*done) (struct ibmvfc_event *);
+       void (*done)(struct ibmvfc_event *evt);
+       void (*_done)(struct ibmvfc_event *evt);
        struct ibmvfc_crq crq;
        union ibmvfc_iu iu;
        union ibmvfc_iu *sync_iu;
-- 
2.27.0

Reply via email to