Since we use a fixed tag / stream for tasks we cannot allow more then one
to run at the same time. This could happen before this time if a task timed
out.

Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 drivers/usb/storage/uas.c | 39 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 34 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 3da185d..4d205f0 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -53,6 +53,7 @@ struct uas_dev_info {
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
        unsigned use_streams:1;
        unsigned uas_sense_old:1;
+       unsigned running_task:1;
        struct scsi_cmnd *cmnd;
        spinlock_t lock;
        struct work_struct work;
@@ -195,6 +196,7 @@ static void uas_zap_dead(struct uas_dev_info *devinfo)
                                    DATA_OUT_URB_INFLIGHT);
                uas_try_complete(cmnd, __func__);
        }
+       devinfo->running_task = 0;
        spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
@@ -340,6 +342,9 @@ static void uas_stat_cmplt(struct urb *urb)
 
        if (!cmnd) {
                if (iu->iu_id == IU_ID_RESPONSE) {
+                       if (!devinfo->running_task)
+                               dev_warn(&urb->dev->dev,
+                                   "stat urb: recv unexpected response iu\n");
                        /* store results for uas_eh_task_mgmt() */
                        memcpy(&devinfo->response, iu, 
sizeof(devinfo->response));
                }
@@ -728,8 +733,19 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
        u16 tag = devinfo->qdepth;
        unsigned long flags;
        struct urb *sense_urb;
+       int result = SUCCESS;
 
        spin_lock_irqsave(&devinfo->lock, flags);
+
+       if (devinfo->running_task) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s: error already running a task\n",
+                            __func__, fname);
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               return FAILED;
+       }
+
+       devinfo->running_task = 1;
        memset(&devinfo->response, 0, sizeof(devinfo->response));
        sense_urb = uas_submit_sense_urb(shost, sizeof(struct response_iu),
                                         GFP_ATOMIC, tag);
@@ -737,6 +753,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
                shost_printk(KERN_INFO, shost,
                             "%s: %s: submit sense urb failed\n",
                             __func__, fname);
+               devinfo->running_task = 0;
                spin_unlock_irqrestore(&devinfo->lock, flags);
                return FAILED;
        }
@@ -744,6 +761,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
                shost_printk(KERN_INFO, shost,
                             "%s: %s: submit task mgmt urb failed\n",
                             __func__, fname);
+               devinfo->running_task = 0;
                spin_unlock_irqrestore(&devinfo->lock, flags);
                usb_kill_urb(sense_urb);
                return FAILED;
@@ -751,23 +769,33 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
        spin_unlock_irqrestore(&devinfo->lock, flags);
 
        if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) {
+               /*
+                * Note we deliberately do not clear running_task here. If we
+                * allow new tasks to be submitted, there is no way to figure
+                * out if a received response_iu is for the failed task or for
+                * the new one. A bus-reset will eventually clear running_task.
+                */
                shost_printk(KERN_INFO, shost,
                             "%s: %s timed out\n", __func__, fname);
                return FAILED;
        }
+
+       spin_lock_irqsave(&devinfo->lock, flags);
+       devinfo->running_task = 0;
        if (be16_to_cpu(devinfo->response.tag) != tag) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s failed (wrong tag %d/%d)\n", __func__,
                             fname, be16_to_cpu(devinfo->response.tag), tag);
-               return FAILED;
-       }
-       if (devinfo->response.response_code != RC_TMF_COMPLETE) {
+               result = FAILED;
+       } else if (devinfo->response.response_code != RC_TMF_COMPLETE) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s failed (rc 0x%x)\n", __func__,
                             fname, devinfo->response.response_code);
-               return FAILED;
+               result = FAILED;
        }
-       return SUCCESS;
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+
+       return result;
 }
 
 static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
@@ -999,6 +1027,7 @@ static int uas_probe(struct usb_interface *intf, const 
struct usb_device_id *id)
        devinfo->intf = intf;
        devinfo->udev = udev;
        devinfo->resetting = 0;
+       devinfo->running_task = 0;
        init_usb_anchor(&devinfo->cmd_urbs);
        init_usb_anchor(&devinfo->sense_urbs);
        init_usb_anchor(&devinfo->data_urbs);
-- 
1.8.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to