We need to give up on this TMF to have a free tag
again.
Signed-off-by: Oliver Neukum <[email protected]>
---
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html