If any of the four URBs that represent a SCSI command fail to be submitted, they will be queued to the UAS work queue. If the SCSI layer wants to cancel that command later because it timed out, we need to be able remove that command from the work queue, potentially out of the list that the work queue function is currently walking across. Make the work queue function take the work queue lock before it manipulates any part of the work list, and make the command abort handler take that lock before attempting to remove the item off the work queue. Make sure to initialize the list_head in the command during allocation, and remove the item from the work queue if it was successfully submitted. Once the command is off the work queue, kill any anchored URBs (waiting for them to complete), and free any URBs that were allocated but not submitted. Return FAILED for now, until we issue the ABORT TASK command to the device (in the next patch). Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> Cc: Matthew Wilcox <willy@xxxxxxxxxxxxxxx> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> --- drivers/usb/storage/uas.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 2598cbf..f61de1f 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -144,19 +144,19 @@ static void uas_do_work(struct work_struct *work) list_replace_init(&uas_work_list, &list); spin_unlock_irq(&uas_work_lock); + spin_lock_irq(&uas_work_lock); list_for_each_entry_safe(cmdinfo, temp, &list, list) { struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); + list_del_init(&cmdinfo->list); if (err) { - list_del(&cmdinfo->list); - spin_lock_irq(&uas_work_lock); list_add_tail(&cmdinfo->list, &uas_work_list); - spin_unlock_irq(&uas_work_lock); schedule_work(&uas_work); } } + spin_unlock_irq(&uas_work_lock); } static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) @@ -501,6 +501,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; + INIT_LIST_HEAD(&cmdinfo->list); switch (cmnd->sc_data_direction) { case DMA_FROM_DEVICE: @@ -537,11 +538,47 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, static DEF_SCSI_QCMD(uas_queuecommand) +static void uas_kill_tagged_urbs(struct usb_anchor *anchor, + struct uas_cmd_info *cmdinfo) +{ + usb_kill_anchored_urbs(anchor); + + /* Free any URBs that weren't submitted. */ + if (cmdinfo->state & SUBMIT_STATUS_URB) { + kfree(cmdinfo->status_urb->transfer_buffer); + usb_free_urb(cmdinfo->status_urb); + } + if (cmdinfo->state & SUBMIT_DATA_IN_URB) { + kfree(cmdinfo->data_in_urb->transfer_buffer); + usb_free_urb(cmdinfo->data_in_urb); + } + if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { + kfree(cmdinfo->data_out_urb->transfer_buffer); + usb_free_urb(cmdinfo->data_out_urb); + } + if (cmdinfo->state & SUBMIT_CMD_URB) { + kfree(cmdinfo->cmd_urb->transfer_buffer); + usb_free_urb(cmdinfo->cmd_urb); + } +} + static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) { struct scsi_device *sdev = cmnd->device; + struct uas_dev_info *devinfo = sdev->hostdata; + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, cmnd->request->tag); + spin_lock_irq(&uas_work_lock); + if (!list_empty(&cmdinfo->list)) + list_del_init(&cmdinfo->list); + spin_unlock_irq(&uas_work_lock); + + if (blk_rq_tagged(cmnd->request)) + uas_kill_tagged_urbs(&devinfo->anchors[cmnd->request->tag], cmdinfo); + else + uas_kill_tagged_urbs(&devinfo->anchors[0], cmdinfo); /* XXX: Send ABORT TASK Task Management command */ return FAILED; -- 1.7.5.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