We need to give up on this TMF to have a free tag
again.

Signed-off-by: Oliver Neukum <oneu...@suse.com>
---
 drivers/usb/storage/uas.c | 167 ++++++++++++++++++++++++++++++----------------
 1 file changed, 110 insertions(+), 57 deletions(-)

diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 590c3a7..acd2034 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -31,6 +31,7 @@
 #include "scsiglue.h"
 
 #define MAX_CMNDS 256
+#define TAG_FOR_TMF 1
 
 /*
  * 1 is due to the different base
@@ -46,6 +47,7 @@ struct uas_dev_info {
        struct usb_anchor data_urbs;
        struct task_mgmt_iu *tmf_iu;
        struct urb *management_urb;
+       struct scsi_cmnd *deathrow;
        unsigned long flags;
        int qdepth, resetting;
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
@@ -68,7 +70,7 @@ enum {
        COMMAND_INFLIGHT        = BIT(8),
        DATA_IN_URB_INFLIGHT    = BIT(9),
        DATA_OUT_URB_INFLIGHT   = BIT(10),
-       COMMAND_ABORTED         = BIT(11),
+       COMMAND_ABORTING        = BIT(11),
        IS_IN_WORK_LIST         = BIT(12),
 };
 
@@ -201,7 +203,7 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const 
char *prefix,
                    (ci->state & COMMAND_INFLIGHT)      ? " CMD"   : "",
                    (ci->state & DATA_IN_URB_INFLIGHT)  ? " IN"    : "",
                    (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT"   : "",
-                   (ci->state & COMMAND_ABORTED)       ? " abort" : "",
+                   (ci->state & COMMAND_ABORTING)       ? " abort" : "",
                    (ci->state & IS_IN_WORK_LIST)       ? " work"  : "");
        scsi_print_command(cmnd);
 }
@@ -234,7 +236,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const 
char *caller)
        if (cmdinfo->state & (COMMAND_INFLIGHT |
                              DATA_IN_URB_INFLIGHT |
                              DATA_OUT_URB_INFLIGHT |
-                             COMMAND_ABORTED))
+                             COMMAND_ABORTING))
                return -EBUSY;
        devinfo->cmnd[cmdinfo->uas_tag - TAG_OFFSET] = NULL;
        uas_free_unsubmitted_urbs(cmnd);
@@ -255,6 +257,10 @@ static void uas_xfer_data(struct urb *urb, struct 
scsi_cmnd *cmnd,
        }
 }
 
+static void finish_tmf(struct uas_dev_info *devinfo, struct response_iu *riu)
+{
+}
+
 static bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd 
*cmnd)
 {
        u8 response_code = riu->response_code;
@@ -287,8 +293,9 @@ static void uas_stat_cmplt(struct urb *urb)
        struct urb *data_out_urb = NULL;
        struct scsi_cmnd *cmnd;
        struct uas_cmd_info *cmdinfo;
+       bool is_tmf;
        unsigned long flags;
-       unsigned int idx;
+       unsigned int idx, raw_tag;
        int status = urb->status;
        bool success;
 
@@ -303,60 +310,94 @@ static void uas_stat_cmplt(struct urb *urb)
                goto out;
        }
 
-       idx = be16_to_cpup(&iu->tag) - TAG_OFFSET;
-       if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) {
-               dev_err(&urb->dev->dev,
-                       "stat urb: no pending cmd for uas-tag %d\n", idx + 
TAG_OFFSET);
-               goto out;
-       }
-
-       cmnd = devinfo->cmnd[idx];
-       cmdinfo = (void *)&cmnd->SCp;
+       raw_tag = be16_to_cpup(&iu->tag);
+       is_tmf = raw_tag == TAG_FOR_TMF;
+       if (!is_tmf) {
+               idx = raw_tag - TAG_OFFSET;
+               if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) {
+                       dev_err(&urb->dev->dev,
+                               "stat urb: no pending cmd for uas-tag %d\n", 
idx + 1 + 1);
+                       goto out;
+               }
 
-       if (!(cmdinfo->state & COMMAND_INFLIGHT)) {
-               uas_log_cmd_state(cmnd, "unexpected status cmplt", 0);
-               goto out;
-       }
+               cmnd = devinfo->cmnd[idx];
+               cmdinfo = (void *)&cmnd->SCp;
 
-       switch (iu->iu_id) {
-       case IU_ID_STATUS:
-               uas_sense(urb, cmnd);
-               if (cmnd->result != 0) {
-                       /* cancel data transfers on error */
-                       data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
-                       data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+               if (!(cmdinfo->state & COMMAND_INFLIGHT)) {
+                       uas_log_cmd_state(cmnd, "unexpected status cmplt", 0);
+                       goto out;
                }
-               cmdinfo->state &= ~COMMAND_INFLIGHT;
-               uas_try_complete(cmnd, __func__);
-               break;
-       case IU_ID_READ_READY:
-               if (!cmdinfo->data_in_urb ||
+
+               switch (iu->iu_id) {
+               case IU_ID_STATUS:
+                       uas_sense(urb, cmnd);
+                       if (cmnd->result != 0) {
+                               /* cancel data transfers on error */
+                               data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
+                               data_out_urb = 
usb_get_urb(cmdinfo->data_out_urb);
+                       }
+                       cmdinfo->state &= ~COMMAND_INFLIGHT;
+                       uas_try_complete(cmnd, __func__);
+                       break;
+               case IU_ID_READ_READY:
+                       if (!cmdinfo->data_in_urb ||
                                (cmdinfo->state & DATA_IN_URB_INFLIGHT)) {
-                       uas_log_cmd_state(cmnd, "unexpected read rdy", 0);
+                               uas_log_cmd_state(cmnd, "unexpected read rdy", 
0);
+                               break;
+                       }
+                       uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
                        break;
-               }
-               uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
-               break;
-       case IU_ID_WRITE_READY:
-               if (!cmdinfo->data_out_urb ||
+               case IU_ID_WRITE_READY:
+                       if (!cmdinfo->data_out_urb ||
                                (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) {
-                       uas_log_cmd_state(cmnd, "unexpected write rdy", 0);
+                               uas_log_cmd_state(cmnd, "unexpected write rdy", 
0);
+                               break;
+                       }
+                       uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
+                       break;
+               case IU_ID_RESPONSE:
+                       /*
+                        * Two possibilities
+                        * 
+                        * 1. This is a strange answer to a command UI
+                        *    A few devices do that under some circumstances
+                        *    We treat this like sort of a STATUS iu
+                        * 2. A genuine answer to our TMF
+                        *    That is a special case we handle in the other
+                        *    branch.
+                        */
+                       cmdinfo->state &= ~COMMAND_INFLIGHT;
+                       success = uas_evaluate_response_iu((struct response_iu 
*)iu, cmnd);
+                       if (!success) {
+                               /* Error, cancel data transfers */
+                               data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
+                               data_out_urb = 
usb_get_urb(cmdinfo->data_out_urb);
+                       }
+                       uas_try_complete(cmnd, __func__);
                        break;
+               default:
+                       uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id);
                }
-               uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
-               break;
-       case IU_ID_RESPONSE:
-               cmdinfo->state &= ~COMMAND_INFLIGHT;
-               success = uas_evaluate_response_iu((struct response_iu *)iu, 
cmnd);
-               if (!success) {
-                       /* Error, cancel data transfers */
-                       data_in_urb = usb_get_urb(cmdinfo->data_in_urb);
-                       data_out_urb = usb_get_urb(cmdinfo->data_out_urb);
+       } else {
+               cmnd = devinfo->deathrow;
+               if (!cmnd)
+                       goto confusion;
+               cmdinfo = (void *)&cmnd->SCp;
+               switch (iu->iu_id) {
+               case IU_ID_RESPONSE:
+                       finish_tmf(devinfo, (struct response_iu *)iu);
+                       break;
+               case IU_ID_STATUS:
+               case IU_ID_READ_READY:
+               case IU_ID_WRITE_READY:
+               default:
+                       uas_log_cmd_state(cmnd,
+                               "Unexpected IU %d for TMF response",
+                               iu->iu_id);
                }
-               uas_try_complete(cmnd, __func__);
-               break;
-       default:
-               uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id);
+confusion:
+       devinfo->deathrow = NULL;
+       complete(&devinfo->deathknell);
        }
 out:
        usb_free_urb(urb);
@@ -431,10 +472,12 @@ static void uas_cmd_cmplt(struct urb *urb)
 
 static void uas_tmf_cmplt(struct urb *urb)
 {
-       struct scsi_cmnd *cmnd = urb->context;
-       struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
-
-       complete(&devinfo->deathknell);
+       /*
+        * Do exactly nothing
+        * At this point the device has acknowledged
+        * the reception of the TMF, but not the execution
+        * We cannot act on that.
+        */
 }
 
 static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
@@ -728,16 +771,17 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
        struct urb *data_in_urb = NULL;
        struct urb *data_out_urb = NULL;
        unsigned long flags;
-       int err;
+       int err, time;
 
        spin_lock_irqsave(&devinfo->lock, flags);
 
        uas_log_cmd_state(cmnd, __func__, 0);
 
        /* Ensure that try_complete does not call scsi_done */
-       cmdinfo->state |= COMMAND_ABORTED;
+       cmdinfo->state |= COMMAND_ABORTING;
 
        init_completion(&devinfo->deathknell);
+       devinfo->deathrow = cmnd;
        usb_fill_bulk_urb(devinfo->management_urb,
                          devinfo->udev,
                          devinfo->cmd_pipe, /* shared */
@@ -746,7 +790,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
                          uas_tmf_cmplt,
                          cmnd->device->host);
        tmf->iu_id = IU_ID_TASK_MGMT;
-       tmf->tag = cpu_to_be16(1);
+       tmf->tag = cpu_to_be16( TAG_FOR_TMF );
        tmf->function = TMF_ABORT_TASK;
        tmf->task_tag = cmdinfo->uas_tag; /* already BE */
 
@@ -754,9 +798,18 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
        if (err < 0) /* unkillable */
                goto give_up;
 
-       wait_for_completion_timeout(&devinfo->deathknell, USB_CTRL_GET_TIMEOUT);
+       time = wait_for_completion_timeout(&devinfo->deathknell, 
USB_CTRL_GET_TIMEOUT);
        /* in case of timeout */
        usb_kill_urb(devinfo->management_urb);
+       if (time) {
+               cmdinfo->state &= ~COMMAND_ABORTING;
+               /*
+                * manually finish as resources must be freed only once
+                */
+               cmnd->result = DID_ABORT << 16;
+               cmnd->scsi_done(cmnd);
+       }
+
 give_up:
 
        /* Drop all refs to this cmnd, kill data urbs to break their ref */
-- 
2.1.4

--
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