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 <tyreld@xxxxxxxxxxxxx> Reviewed-by: Brian King <brking@xxxxxxxxxxxxxxxxxx> --- 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