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