diff options
author | Hans de Goede <hdegoede@redhat.com> | 2014-09-13 10:26:40 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-09-24 04:42:11 (GMT) |
commit | 616f0e6cab4698309ff9e48ee2a85b5eb78cf31a (patch) | |
tree | e73bcd7c2e9095e4eb57a7aa963317954bb26fc5 /drivers/usb | |
parent | b6823c51fcd82e993275f5403e120279232ecaec (diff) | |
download | linux-616f0e6cab4698309ff9e48ee2a85b5eb78cf31a.tar.xz |
uas: Drop all references to a scsi_cmnd once it has been aborted
Do not keep references around to a cmnd which is under error handling.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/storage/uas.c | 47 |
1 files changed, 44 insertions, 3 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 8421dcd..b1a1acb 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -257,12 +257,11 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) lockdep_assert_held(&devinfo->lock); if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | - DATA_OUT_URB_INFLIGHT)) + DATA_OUT_URB_INFLIGHT | + COMMAND_ABORTED)) return -EBUSY; WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED); cmdinfo->state |= COMMAND_COMPLETED; - if (cmdinfo->state & COMMAND_ABORTED) - scmd_printk(KERN_INFO, cmnd, "abort completed\n"); devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL; cmnd->scsi_done(cmnd); return 0; @@ -712,6 +711,47 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, static DEF_SCSI_QCMD(uas_queuecommand) +/* + * For now we do not support actually sending an abort to the device, so + * this eh always fails. Still we must define it to make sure that we've + * dropped all references to the cmnd in question once this function exits. + */ +static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) +{ + struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; + struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; + struct urb *data_in_urb = NULL; + struct urb *data_out_urb = NULL; + unsigned long flags; + + spin_lock_irqsave(&devinfo->lock, flags); + + uas_log_cmd_state(cmnd, __func__); + + /* Ensure that try_complete does not call scsi_done */ + cmdinfo->state |= COMMAND_ABORTED; + + /* Drop all refs to this cmnd, kill data urbs to break their ref */ + devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL; + if (cmdinfo->state & DATA_IN_URB_INFLIGHT) + data_in_urb = usb_get_urb(cmdinfo->data_in_urb); + if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) + data_out_urb = usb_get_urb(cmdinfo->data_out_urb); + + spin_unlock_irqrestore(&devinfo->lock, flags); + + if (data_in_urb) { + usb_kill_urb(data_in_urb); + usb_put_urb(data_in_urb); + } + if (data_out_urb) { + usb_kill_urb(data_out_urb); + usb_put_urb(data_out_urb); + } + + return FAILED; +} + static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) { struct scsi_device *sdev = cmnd->device; @@ -797,6 +837,7 @@ static struct scsi_host_template uas_host_template = { .queuecommand = uas_queuecommand, .slave_alloc = uas_slave_alloc, .slave_configure = uas_slave_configure, + .eh_abort_handler = uas_eh_abort_handler, .eh_bus_reset_handler = uas_eh_bus_reset_handler, .can_queue = 65536, /* Is there a limit on the _host_ ? */ .this_id = -1, |