diff options
| author | Gerd Hoffmann <kraxel@redhat.com> | 2012-11-30 05:54:44 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-11 15:14:18 -0500 |
| commit | 4c456971f8c90b8179bf5fd5ae2d9b734085c19d (patch) | |
| tree | ddeb1204bc07ef3dbaa5cfc5b5f10838d2cb18a6 /drivers/usb/storage | |
| parent | 5d390403fee54a64c660b7d42f7b38d99a486b88 (diff) | |
uas: improve device reset
Add new function to unlink and abort requests from the work
list, call it on bus reset and disconnect where we kill all
in-flight urbs. Also reorder calls in disconnect to first
cancel transfers, then remove the scsi hba.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/storage')
| -rw-r--r-- | drivers/usb/storage/uas.c | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 5416f2a8f566..547f96acad9c 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c | |||
| @@ -84,6 +84,7 @@ struct uas_cmd_info { | |||
| 84 | static int uas_submit_urbs(struct scsi_cmnd *cmnd, | 84 | static int uas_submit_urbs(struct scsi_cmnd *cmnd, |
| 85 | struct uas_dev_info *devinfo, gfp_t gfp); | 85 | struct uas_dev_info *devinfo, gfp_t gfp); |
| 86 | static void uas_do_work(struct work_struct *work); | 86 | static void uas_do_work(struct work_struct *work); |
| 87 | static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); | ||
| 87 | 88 | ||
| 88 | static DECLARE_WORK(uas_work, uas_do_work); | 89 | static DECLARE_WORK(uas_work, uas_do_work); |
| 89 | static DEFINE_SPINLOCK(uas_work_lock); | 90 | static DEFINE_SPINLOCK(uas_work_lock); |
| @@ -145,6 +146,45 @@ static void uas_do_work(struct work_struct *work) | |||
| 145 | } | 146 | } |
| 146 | } | 147 | } |
| 147 | 148 | ||
| 149 | static void uas_abort_work(struct uas_dev_info *devinfo) | ||
| 150 | { | ||
| 151 | struct uas_cmd_info *cmdinfo; | ||
| 152 | struct uas_cmd_info *temp; | ||
| 153 | struct list_head list; | ||
| 154 | unsigned long flags; | ||
| 155 | |||
| 156 | spin_lock_irq(&uas_work_lock); | ||
| 157 | list_replace_init(&uas_work_list, &list); | ||
| 158 | spin_unlock_irq(&uas_work_lock); | ||
| 159 | |||
| 160 | spin_lock_irqsave(&devinfo->lock, flags); | ||
| 161 | list_for_each_entry_safe(cmdinfo, temp, &list, list) { | ||
| 162 | struct scsi_pointer *scp = (void *)cmdinfo; | ||
| 163 | struct scsi_cmnd *cmnd = container_of(scp, | ||
| 164 | struct scsi_cmnd, SCp); | ||
| 165 | struct uas_dev_info *di = (void *)cmnd->device->hostdata; | ||
| 166 | |||
| 167 | if (di == devinfo) { | ||
| 168 | cmdinfo->state |= COMMAND_ABORTED; | ||
| 169 | cmdinfo->state &= ~IS_IN_WORK_LIST; | ||
| 170 | if (devinfo->resetting) { | ||
| 171 | /* uas_stat_cmplt() will not do that | ||
| 172 | * when a device reset is in | ||
| 173 | * progress */ | ||
| 174 | cmdinfo->state &= ~COMMAND_INFLIGHT; | ||
| 175 | } | ||
| 176 | uas_try_complete(cmnd, __func__); | ||
| 177 | } else { | ||
| 178 | /* not our uas device, relink into list */ | ||
| 179 | list_del(&cmdinfo->list); | ||
| 180 | spin_lock_irq(&uas_work_lock); | ||
| 181 | list_add_tail(&cmdinfo->list, &uas_work_list); | ||
| 182 | spin_unlock_irq(&uas_work_lock); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
| 186 | } | ||
| 187 | |||
| 148 | static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) | 188 | static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) |
| 149 | { | 189 | { |
| 150 | struct sense_iu *sense_iu = urb->transfer_buffer; | 190 | struct sense_iu *sense_iu = urb->transfer_buffer; |
| @@ -750,6 +790,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
| 750 | int err; | 790 | int err; |
| 751 | 791 | ||
| 752 | devinfo->resetting = 1; | 792 | devinfo->resetting = 1; |
| 793 | uas_abort_work(devinfo); | ||
| 753 | usb_kill_anchored_urbs(&devinfo->cmd_urbs); | 794 | usb_kill_anchored_urbs(&devinfo->cmd_urbs); |
| 754 | usb_kill_anchored_urbs(&devinfo->sense_urbs); | 795 | usb_kill_anchored_urbs(&devinfo->sense_urbs); |
| 755 | usb_kill_anchored_urbs(&devinfo->data_urbs); | 796 | usb_kill_anchored_urbs(&devinfo->data_urbs); |
| @@ -995,10 +1036,12 @@ static void uas_disconnect(struct usb_interface *intf) | |||
| 995 | struct Scsi_Host *shost = usb_get_intfdata(intf); | 1036 | struct Scsi_Host *shost = usb_get_intfdata(intf); |
| 996 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; | 1037 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; |
| 997 | 1038 | ||
| 998 | scsi_remove_host(shost); | 1039 | devinfo->resetting = 1; |
| 1040 | uas_abort_work(devinfo); | ||
| 999 | usb_kill_anchored_urbs(&devinfo->cmd_urbs); | 1041 | usb_kill_anchored_urbs(&devinfo->cmd_urbs); |
| 1000 | usb_kill_anchored_urbs(&devinfo->sense_urbs); | 1042 | usb_kill_anchored_urbs(&devinfo->sense_urbs); |
| 1001 | usb_kill_anchored_urbs(&devinfo->data_urbs); | 1043 | usb_kill_anchored_urbs(&devinfo->data_urbs); |
| 1044 | scsi_remove_host(shost); | ||
| 1002 | uas_free_streams(devinfo); | 1045 | uas_free_streams(devinfo); |
| 1003 | kfree(devinfo); | 1046 | kfree(devinfo); |
| 1004 | } | 1047 | } |
