Re: [PATCH v4 18/21] ibmvfc: send Cancel MAD down each hw scsi channel

2021-01-12 Thread Brian King
On 1/11/21 5:12 PM, Tyrel Datwyler wrote:
> diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
> index b0b0212344f3..24e1278acfeb 100644
> --- a/drivers/scsi/ibmvscsi/ibmvfc.c
> +++ b/drivers/scsi/ibmvscsi/ibmvfc.c
> @@ -2418,18 +2418,79 @@ static struct ibmvfc_event *ibmvfc_init_tmf(struct 
> ibmvfc_queue *queue,
>   return evt;
>  }
>  
> -/**
> - * ibmvfc_cancel_all - Cancel all outstanding commands to the device
> - * @sdev:scsi device to cancel commands
> - * @type:type of error recovery being performed
> - *
> - * This sends a cancel to the VIOS for the specified device. This does
> - * NOT send any abort to the actual device. That must be done separately.
> - *
> - * Returns:
> - *   0 on success / other on failure
> - **/
> -static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
> +static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
> +{
> + struct ibmvfc_host *vhost = shost_priv(sdev->host);
> + struct ibmvfc_event *evt, *found_evt, *temp;
> + struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
> + unsigned long flags;
> + int num_hwq, i;
> + LIST_HEAD(cancelq);
> + u16 status;
> +
> + ENTER;
> + spin_lock_irqsave(vhost->host->host_lock, flags);
> + num_hwq = vhost->scsi_scrqs.active_queues;
> + for (i = 0; i < num_hwq; i++) {
> + spin_lock(queues[i].q_lock);
> + spin_lock(&queues[i].l_lock);
> + found_evt = NULL;
> + list_for_each_entry(evt, &queues[i].sent, queue_list) {
> + if (evt->cmnd && evt->cmnd->device == sdev) {
> + found_evt = evt;
> + break;
> + }
> + }
> + spin_unlock(&queues[i].l_lock);
> +

I really don't like the way the first for loop grabs all the q_locks, and then
there is a second for loop that drops all these locks... I think this code would
be cleaner if it looked like:

if (found_evt && vhost->logged_in) {
evt = ibmvfc_init_tmf(&queues[i], sdev, type);
evt->sync_iu = &queues[i].cancel_rsp;
ibmvfc_send_event(evt, vhost, default_timeout);
list_add_tail(&evt->cancel, &cancelq);
}

spin_unlock(queues[i].q_lock);

}

> + if (!found_evt)
> + continue;
> +
> + if (vhost->logged_in) {
> + evt = ibmvfc_init_tmf(&queues[i], sdev, type);
> + evt->sync_iu = &queues[i].cancel_rsp;
> + ibmvfc_send_event(evt, vhost, default_timeout);
> + list_add_tail(&evt->cancel, &cancelq);
> + }
> + }
> +
> + for (i = 0; i < num_hwq; i++)
> + spin_unlock(queues[i].q_lock);
> + spin_unlock_irqrestore(vhost->host->host_lock, flags);
> +
> + if (list_empty(&cancelq)) {
> + if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
> + sdev_printk(KERN_INFO, sdev, "No events found to 
> cancel\n");
> + return 0;
> + }
> +
> + sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
> +
> + list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
> + wait_for_completion(&evt->comp);
> + status = be16_to_cpu(evt->queue->cancel_rsp.mad_common.status);

You probably want a list_del(&evt->cancel) here.

> + ibmvfc_free_event(evt);
> +
> + if (status != IBMVFC_MAD_SUCCESS) {
> + sdev_printk(KERN_WARNING, sdev, "Cancel failed with 
> rc=%x\n", status);
> + switch (status) {
> + case IBMVFC_MAD_DRIVER_FAILED:
> + case IBMVFC_MAD_CRQ_ERROR:
> + /* Host adapter most likely going through reset, return 
> success to
> +  * the caller will wait for the command being cancelled 
> to get returned
> +  */
> + break;
> + default:
> + break;

I think this default break needs to be a return -EIO.

> + }
> + }
> + }
> +
> + sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding 
> commands\n");
> + return 0;
> +}
> +
> +static int ibmvfc_cancel_all_sq(struct scsi_device *sdev, int type)
>  {
>   struct ibmvfc_host *vhost = shost_priv(sdev->host);
>   struct ibmvfc_event *evt, *found_evt;
> @@ -2498,6 +2559,27 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, 
> int type)
>   return 0;
>  }
>  



-- 
Brian King
Power Linux I/O
IBM Linux Technology Center



[PATCH v4 18/21] ibmvfc: send Cancel MAD down each hw scsi channel

2021-01-11 Thread Tyrel Datwyler
In general the client needs to send Cancel MADs and task management
commands down the same channel as the command(s) intended to cancel or
abort. The client assigns cancel keys per LUN and thus must send a
Cancel down each channel commands were submitted for that LUN. Further,
the client then must wait for those cancel completions prior to
submitting a LUN RESET or ABORT TASK SET.

Add a cancel rsp iu syncronization field to the ibmvfc_queue struct such
that the cancel routine can sync the cancel response to each queue that
requires a cancel command. Build a list of each cancel event sent and
wait for the completion of each submitted cancel.

Signed-off-by: Tyrel Datwyler 
---
 drivers/scsi/ibmvscsi/ibmvfc.c | 106 +
 drivers/scsi/ibmvscsi/ibmvfc.h |   3 +
 2 files changed, 97 insertions(+), 12 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index b0b0212344f3..24e1278acfeb 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -2418,18 +2418,79 @@ static struct ibmvfc_event *ibmvfc_init_tmf(struct 
ibmvfc_queue *queue,
return evt;
 }
 
-/**
- * ibmvfc_cancel_all - Cancel all outstanding commands to the device
- * @sdev:  scsi device to cancel commands
- * @type:  type of error recovery being performed
- *
- * This sends a cancel to the VIOS for the specified device. This does
- * NOT send any abort to the actual device. That must be done separately.
- *
- * Returns:
- * 0 on success / other on failure
- **/
-static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
+static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
+{
+   struct ibmvfc_host *vhost = shost_priv(sdev->host);
+   struct ibmvfc_event *evt, *found_evt, *temp;
+   struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+   unsigned long flags;
+   int num_hwq, i;
+   LIST_HEAD(cancelq);
+   u16 status;
+
+   ENTER;
+   spin_lock_irqsave(vhost->host->host_lock, flags);
+   num_hwq = vhost->scsi_scrqs.active_queues;
+   for (i = 0; i < num_hwq; i++) {
+   spin_lock(queues[i].q_lock);
+   spin_lock(&queues[i].l_lock);
+   found_evt = NULL;
+   list_for_each_entry(evt, &queues[i].sent, queue_list) {
+   if (evt->cmnd && evt->cmnd->device == sdev) {
+   found_evt = evt;
+   break;
+   }
+   }
+   spin_unlock(&queues[i].l_lock);
+
+   if (!found_evt)
+   continue;
+
+   if (vhost->logged_in) {
+   evt = ibmvfc_init_tmf(&queues[i], sdev, type);
+   evt->sync_iu = &queues[i].cancel_rsp;
+   ibmvfc_send_event(evt, vhost, default_timeout);
+   list_add_tail(&evt->cancel, &cancelq);
+   }
+   }
+
+   for (i = 0; i < num_hwq; i++)
+   spin_unlock(queues[i].q_lock);
+   spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+   if (list_empty(&cancelq)) {
+   if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+   sdev_printk(KERN_INFO, sdev, "No events found to 
cancel\n");
+   return 0;
+   }
+
+   sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
+
+   list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
+   wait_for_completion(&evt->comp);
+   status = be16_to_cpu(evt->queue->cancel_rsp.mad_common.status);
+   ibmvfc_free_event(evt);
+
+   if (status != IBMVFC_MAD_SUCCESS) {
+   sdev_printk(KERN_WARNING, sdev, "Cancel failed with 
rc=%x\n", status);
+   switch (status) {
+   case IBMVFC_MAD_DRIVER_FAILED:
+   case IBMVFC_MAD_CRQ_ERROR:
+   /* Host adapter most likely going through reset, return 
success to
+* the caller will wait for the command being cancelled 
to get returned
+*/
+   break;
+   default:
+   break;
+   }
+   }
+   }
+
+   sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding 
commands\n");
+   return 0;
+}
+
+static int ibmvfc_cancel_all_sq(struct scsi_device *sdev, int type)
 {
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct ibmvfc_event *evt, *found_evt;
@@ -2498,6 +2559,27 @@ static int ibmvfc_cancel_all(struct scsi_device *sdev, 
int type)
return 0;
 }
 
+/**
+ * ibmvfc_cancel_all - Cancel all outstanding commands to the device
+ * @sdev:  scsi device to cancel commands
+ * @type:  type of error recovery being performed
+ *
+ * This sends a cancel to the VIOS for the specified devi