uas: Use all available stream ids
[deliverable/linux.git] / drivers / usb / storage / uas.c
index d966b59f7d7b264fcd712963e97f487fee563266..3f021f2fafdf4e3c2dc586995273d6e257e07b5f 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/usb/uas.h>
 
 #include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
@@ -45,12 +46,15 @@ struct uas_dev_info {
        struct usb_anchor sense_urbs;
        struct usb_anchor data_urbs;
        int qdepth, resetting;
-       struct response_ui response;
+       struct response_iu response;
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
        unsigned use_streams:1;
        unsigned uas_sense_old:1;
        struct scsi_cmnd *cmnd;
        spinlock_t lock;
+       struct work_struct work;
+       struct list_head work_list;
+       struct list_head dead_list;
 };
 
 enum {
@@ -77,7 +81,8 @@ struct uas_cmd_info {
        struct urb *cmd_urb;
        struct urb *data_in_urb;
        struct urb *data_out_urb;
-       struct list_head list;
+       struct list_head work;
+       struct list_head dead;
 };
 
 /* I hate forward declarations, but I actually have a loop */
@@ -85,102 +90,108 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                                struct uas_dev_info *devinfo, gfp_t gfp);
 static void uas_do_work(struct work_struct *work);
 static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
+static void uas_configure_endpoints(struct uas_dev_info *devinfo);
+static void uas_free_streams(struct uas_dev_info *devinfo);
+static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller);
 
-static DECLARE_WORK(uas_work, uas_do_work);
-static DEFINE_SPINLOCK(uas_work_lock);
-static LIST_HEAD(uas_work_list);
-
+/* Must be called with devinfo->lock held, will temporary unlock the lock */
 static void uas_unlink_data_urbs(struct uas_dev_info *devinfo,
-                                struct uas_cmd_info *cmdinfo)
+                                struct uas_cmd_info *cmdinfo,
+                                unsigned long *lock_flags)
 {
-       unsigned long flags;
-
        /*
         * The UNLINK_DATA_URBS flag makes sure uas_try_complete
         * (called by urb completion) doesn't release cmdinfo
         * underneath us.
         */
-       spin_lock_irqsave(&devinfo->lock, flags);
        cmdinfo->state |= UNLINK_DATA_URBS;
-       spin_unlock_irqrestore(&devinfo->lock, flags);
+       spin_unlock_irqrestore(&devinfo->lock, *lock_flags);
 
        if (cmdinfo->data_in_urb)
                usb_unlink_urb(cmdinfo->data_in_urb);
        if (cmdinfo->data_out_urb)
                usb_unlink_urb(cmdinfo->data_out_urb);
 
-       spin_lock_irqsave(&devinfo->lock, flags);
+       spin_lock_irqsave(&devinfo->lock, *lock_flags);
        cmdinfo->state &= ~UNLINK_DATA_URBS;
-       spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
 static void uas_do_work(struct work_struct *work)
 {
+       struct uas_dev_info *devinfo =
+               container_of(work, struct uas_dev_info, work);
        struct uas_cmd_info *cmdinfo;
        struct uas_cmd_info *temp;
-       struct list_head list;
        unsigned long flags;
        int err;
 
-       spin_lock_irq(&uas_work_lock);
-       list_replace_init(&uas_work_list, &list);
-       spin_unlock_irq(&uas_work_lock);
-
-       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+       spin_lock_irqsave(&devinfo->lock, flags);
+       list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) {
                struct scsi_pointer *scp = (void *)cmdinfo;
-               struct scsi_cmnd *cmnd = container_of(scp,
-                                                       struct scsi_cmnd, SCp);
-               struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
-               spin_lock_irqsave(&devinfo->lock, flags);
+               struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
+                                                     SCp);
                err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
-               if (!err)
+               if (!err) {
                        cmdinfo->state &= ~IS_IN_WORK_LIST;
-               spin_unlock_irqrestore(&devinfo->lock, flags);
-               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);
+                       list_del(&cmdinfo->work);
+               } else {
+                       schedule_work(&devinfo->work);
                }
        }
+       spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
 static void uas_abort_work(struct uas_dev_info *devinfo)
 {
        struct uas_cmd_info *cmdinfo;
        struct uas_cmd_info *temp;
-       struct list_head list;
        unsigned long flags;
 
-       spin_lock_irq(&uas_work_lock);
-       list_replace_init(&uas_work_list, &list);
-       spin_unlock_irq(&uas_work_lock);
-
        spin_lock_irqsave(&devinfo->lock, flags);
-       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+       list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) {
                struct scsi_pointer *scp = (void *)cmdinfo;
-               struct scsi_cmnd *cmnd = container_of(scp,
-                                                       struct scsi_cmnd, SCp);
-               struct uas_dev_info *di = (void *)cmnd->device->hostdata;
+               struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
+                                                     SCp);
+               uas_log_cmd_state(cmnd, __func__);
+               WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED);
+               cmdinfo->state |= COMMAND_ABORTED;
+               cmdinfo->state &= ~IS_IN_WORK_LIST;
+               list_del(&cmdinfo->work);
+               list_add_tail(&cmdinfo->dead, &devinfo->dead_list);
+       }
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+}
 
-               if (di == devinfo) {
-                       cmdinfo->state |= COMMAND_ABORTED;
-                       cmdinfo->state &= ~IS_IN_WORK_LIST;
-                       if (devinfo->resetting) {
-                               /* uas_stat_cmplt() will not do that
-                                * when a device reset is in
-                                * progress */
-                               cmdinfo->state &= ~COMMAND_INFLIGHT;
-                       }
-                       uas_try_complete(cmnd, __func__);
-               } else {
-                       /* not our uas device, relink into list */
-                       list_del(&cmdinfo->list);
-                       spin_lock_irq(&uas_work_lock);
-                       list_add_tail(&cmdinfo->list, &uas_work_list);
-                       spin_unlock_irq(&uas_work_lock);
-               }
+static void uas_add_work(struct uas_cmd_info *cmdinfo)
+{
+       struct scsi_pointer *scp = (void *)cmdinfo;
+       struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp);
+       struct uas_dev_info *devinfo = cmnd->device->hostdata;
+
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
+       list_add_tail(&cmdinfo->work, &devinfo->work_list);
+       cmdinfo->state |= IS_IN_WORK_LIST;
+       schedule_work(&devinfo->work);
+}
+
+static void uas_zap_dead(struct uas_dev_info *devinfo)
+{
+       struct uas_cmd_info *cmdinfo;
+       struct uas_cmd_info *temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&devinfo->lock, flags);
+       list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, dead) {
+               struct scsi_pointer *scp = (void *)cmdinfo;
+               struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
+                                                     SCp);
+               uas_log_cmd_state(cmnd, __func__);
+               WARN_ON_ONCE(!(cmdinfo->state & COMMAND_ABORTED));
+               /* all urbs are killed, clear inflight bits */
+               cmdinfo->state &= ~(COMMAND_INFLIGHT |
+                                   DATA_IN_URB_INFLIGHT |
+                                   DATA_OUT_URB_INFLIGHT);
+               uas_try_complete(cmnd, __func__);
        }
        spin_unlock_irqrestore(&devinfo->lock, flags);
 }
@@ -259,19 +270,20 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
 
-       WARN_ON(!spin_is_locked(&devinfo->lock));
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
        if (cmdinfo->state & (COMMAND_INFLIGHT |
                              DATA_IN_URB_INFLIGHT |
                              DATA_OUT_URB_INFLIGHT |
                              UNLINK_DATA_URBS))
                return -EBUSY;
-       BUG_ON(cmdinfo->state & COMMAND_COMPLETED);
+       WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED);
        cmdinfo->state |= COMMAND_COMPLETED;
        usb_free_urb(cmdinfo->data_in_urb);
        usb_free_urb(cmdinfo->data_out_urb);
        if (cmdinfo->state & COMMAND_ABORTED) {
                scmd_printk(KERN_INFO, cmnd, "abort completed\n");
                cmnd->result = DID_ABORT << 16;
+               list_del(&cmdinfo->dead);
        }
        cmnd->scsi_done(cmnd);
        return 0;
@@ -286,11 +298,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
        cmdinfo->state |= direction | SUBMIT_STATUS_URB;
        err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
        if (err) {
-               spin_lock(&uas_work_lock);
-               list_add_tail(&cmdinfo->list, &uas_work_list);
-               cmdinfo->state |= IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
-               schedule_work(&uas_work);
+               uas_add_work(cmdinfo);
        }
 }
 
@@ -305,7 +313,13 @@ static void uas_stat_cmplt(struct urb *urb)
        u16 tag;
 
        if (urb->status) {
-               dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
+               if (urb->status == -ENOENT) {
+                       dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n",
+                               urb->stream_id);
+               } else {
+                       dev_err(&urb->dev->dev, "stat urb: status %d\n",
+                               urb->status);
+               }
                usb_free_urb(urb);
                return;
        }
@@ -346,9 +360,7 @@ static void uas_stat_cmplt(struct urb *urb)
                        uas_sense(urb, cmnd);
                if (cmnd->result != 0) {
                        /* cancel data transfers on error */
-                       spin_unlock_irqrestore(&devinfo->lock, flags);
-                       uas_unlink_data_urbs(devinfo, cmdinfo);
-                       spin_lock_irqsave(&devinfo->lock, flags);
+                       uas_unlink_data_urbs(devinfo, cmdinfo, &flags);
                }
                cmdinfo->state &= ~COMMAND_INFLIGHT;
                uas_try_complete(cmnd, __func__);
@@ -383,8 +395,9 @@ static void uas_data_cmplt(struct urb *urb)
                sdb = scsi_out(cmnd);
                cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT;
        }
-       BUG_ON(sdb == NULL);
-       if (urb->status) {
+       if (sdb == NULL) {
+               WARN_ON_ONCE(1);
+       } else if (urb->status) {
                /* error: no data transfered */
                sdb->resid = sdb->length;
        } else {
@@ -442,7 +455,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
 }
 
 static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
-                                       struct scsi_cmnd *cmnd, u16 stream_id)
+                                       struct scsi_cmnd *cmnd)
 {
        struct usb_device *udev = devinfo->udev;
        struct scsi_device *sdev = cmnd->device;
@@ -515,10 +528,12 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp,
                          usb_free_urb, NULL);
        urb->transfer_flags |= URB_FREE_BUFFER;
 
+       usb_anchor_urb(urb, &devinfo->cmd_urbs);
        err = usb_submit_urb(urb, gfp);
-       if (err)
+       if (err) {
+               usb_unanchor_urb(urb);
                goto err;
-       usb_anchor_urb(urb, &devinfo->cmd_urbs);
+       }
 
        return 0;
 
@@ -542,13 +557,14 @@ static int uas_submit_sense_urb(struct Scsi_Host *shost,
        urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream);
        if (!urb)
                return SCSI_MLQUEUE_DEVICE_BUSY;
+       usb_anchor_urb(urb, &devinfo->sense_urbs);
        if (usb_submit_urb(urb, gfp)) {
+               usb_unanchor_urb(urb);
                shost_printk(KERN_INFO, shost,
                             "sense urb submission failure\n");
                usb_free_urb(urb);
                return SCSI_MLQUEUE_DEVICE_BUSY;
        }
-       usb_anchor_urb(urb, &devinfo->sense_urbs);
        return 0;
 }
 
@@ -558,7 +574,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        int err;
 
-       WARN_ON(!spin_is_locked(&devinfo->lock));
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
        if (cmdinfo->state & SUBMIT_STATUS_URB) {
                err = uas_submit_sense_urb(cmnd->device->host, gfp,
                                           cmdinfo->stream);
@@ -578,14 +594,15 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        }
 
        if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
+               usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
                if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) {
+                       usb_unanchor_urb(cmdinfo->data_in_urb);
                        scmd_printk(KERN_INFO, cmnd,
                                        "data in urb submission failure\n");
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
                cmdinfo->state |= DATA_IN_URB_INFLIGHT;
-               usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
@@ -598,33 +615,32 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        }
 
        if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
+               usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
                if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) {
+                       usb_unanchor_urb(cmdinfo->data_out_urb);
                        scmd_printk(KERN_INFO, cmnd,
                                        "data out urb submission failure\n");
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
                cmdinfo->state |= DATA_OUT_URB_INFLIGHT;
-               usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_CMD_URB) {
-               cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
-                                                    cmdinfo->stream);
+               cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd);
                if (!cmdinfo->cmd_urb)
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~ALLOC_CMD_URB;
        }
 
        if (cmdinfo->state & SUBMIT_CMD_URB) {
-               usb_get_urb(cmdinfo->cmd_urb);
+               usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
                if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) {
+                       usb_unanchor_urb(cmdinfo->cmd_urb);
                        scmd_printk(KERN_INFO, cmnd,
                                        "cmd urb submission failure\n");
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
-               usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
-               usb_put_urb(cmdinfo->cmd_urb);
                cmdinfo->cmd_urb = NULL;
                cmdinfo->state &= ~SUBMIT_CMD_URB;
                cmdinfo->state |= COMMAND_INFLIGHT;
@@ -692,11 +708,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
                        spin_unlock_irqrestore(&devinfo->lock, flags);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
-               spin_lock(&uas_work_lock);
-               list_add_tail(&cmdinfo->list, &uas_work_list);
-               cmdinfo->state |= IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
-               schedule_work(&uas_work);
+               uas_add_work(cmdinfo);
        }
 
        spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -710,7 +722,7 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
 {
        struct Scsi_Host *shost = cmnd->device->host;
        struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
-       u16 tag = devinfo->qdepth - 1;
+       u16 tag = devinfo->qdepth;
        unsigned long flags;
 
        spin_lock_irqsave(&devinfo->lock, flags);
@@ -760,20 +772,18 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
 
        uas_log_cmd_state(cmnd, __func__);
        spin_lock_irqsave(&devinfo->lock, flags);
+       WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED);
        cmdinfo->state |= COMMAND_ABORTED;
+       list_add_tail(&cmdinfo->dead, &devinfo->dead_list);
        if (cmdinfo->state & IS_IN_WORK_LIST) {
-               spin_lock(&uas_work_lock);
-               list_del(&cmdinfo->list);
+               list_del(&cmdinfo->work);
                cmdinfo->state &= ~IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
        }
        if (cmdinfo->state & COMMAND_INFLIGHT) {
                spin_unlock_irqrestore(&devinfo->lock, flags);
                ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
        } else {
-               spin_unlock_irqrestore(&devinfo->lock, flags);
-               uas_unlink_data_urbs(devinfo, cmdinfo);
-               spin_lock_irqsave(&devinfo->lock, flags);
+               uas_unlink_data_urbs(devinfo, cmdinfo, &flags);
                uas_try_complete(cmnd, __func__);
                spin_unlock_irqrestore(&devinfo->lock, flags);
                ret = SUCCESS;
@@ -795,14 +805,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
        struct usb_device *udev = devinfo->udev;
        int err;
 
+       err = usb_lock_device_for_reset(udev, devinfo->intf);
+       if (err) {
+               shost_printk(KERN_ERR, sdev->host,
+                            "%s FAILED to get lock err %d\n", __func__, err);
+               return FAILED;
+       }
+
+       shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__);
        devinfo->resetting = 1;
        uas_abort_work(devinfo);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
+       uas_zap_dead(devinfo);
        err = usb_reset_device(udev);
        devinfo->resetting = 0;
 
+       usb_unlock_device(udev);
+
        if (err) {
                shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__);
                return FAILED;
@@ -822,7 +843,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
 {
        struct uas_dev_info *devinfo = sdev->hostdata;
        scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
-       scsi_activate_tcq(sdev, devinfo->qdepth - 3);
+       scsi_activate_tcq(sdev, devinfo->qdepth - 2);
        return 0;
 }
 
@@ -934,13 +955,13 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
                eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
        } else {
                devinfo->cmd_pipe = usb_sndbulkpipe(udev,
-                                               eps[0]->desc.bEndpointAddress);
+                                            usb_endpoint_num(&eps[0]->desc));
                devinfo->status_pipe = usb_rcvbulkpipe(udev,
-                                               eps[1]->desc.bEndpointAddress);
+                                            usb_endpoint_num(&eps[1]->desc));
                devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
-                                               eps[2]->desc.bEndpointAddress);
+                                            usb_endpoint_num(&eps[2]->desc));
                devinfo->data_out_pipe = usb_sndbulkpipe(udev,
-                                               eps[3]->desc.bEndpointAddress);
+                                            usb_endpoint_num(&eps[3]->desc));
        }
 
        devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256,
@@ -972,8 +993,8 @@ static void uas_free_streams(struct uas_dev_info *devinfo)
  */
 static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-       int result;
-       struct Scsi_Host *shost;
+       int result = -ENOMEM;
+       struct Scsi_Host *shost = NULL;
        struct uas_dev_info *devinfo;
        struct usb_device *udev = interface_to_usbdev(intf);
 
@@ -982,12 +1003,11 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
        if (!devinfo)
-               return -ENOMEM;
+               goto set_alt0;
 
-       result = -ENOMEM;
        shost = scsi_host_alloc(&uas_host_template, sizeof(void *));
        if (!shost)
-               goto free;
+               goto set_alt0;
 
        shost->max_cmd_len = 16 + 252;
        shost->max_id = 1;
@@ -1002,15 +1022,18 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
        init_usb_anchor(&devinfo->sense_urbs);
        init_usb_anchor(&devinfo->data_urbs);
        spin_lock_init(&devinfo->lock);
+       INIT_WORK(&devinfo->work, uas_do_work);
+       INIT_LIST_HEAD(&devinfo->work_list);
+       INIT_LIST_HEAD(&devinfo->dead_list);
        uas_configure_endpoints(devinfo);
 
-       result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3);
+       result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
        if (result)
-               goto free;
+               goto free_streams;
 
        result = scsi_add_host(shost, &intf->dev);
        if (result)
-               goto deconfig_eps;
+               goto free_streams;
 
        shost->hostdata[0] = (unsigned long)devinfo;
 
@@ -1018,9 +1041,10 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
        usb_set_intfdata(intf, shost);
        return result;
 
-deconfig_eps:
+free_streams:
        uas_free_streams(devinfo);
- free:
+set_alt0:
+       usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
        kfree(devinfo);
        if (shost)
                scsi_host_put(shost);
@@ -1029,13 +1053,41 @@ deconfig_eps:
 
 static int uas_pre_reset(struct usb_interface *intf)
 {
-/* XXX: Need to return 1 if it's not our device in error handling */
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       unsigned long flags;
+
+       /* Block new requests */
+       spin_lock_irqsave(shost->host_lock, flags);
+       scsi_block_requests(shost);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       /* Wait for any pending requests to complete */
+       flush_work(&devinfo->work);
+       if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) {
+               shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__);
+               return 1;
+       }
+
+       uas_free_streams(devinfo);
+
        return 0;
 }
 
 static int uas_post_reset(struct usb_interface *intf)
 {
-/* XXX: Need to return 1 if it's not our device in error handling */
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       unsigned long flags;
+
+       uas_configure_endpoints(devinfo);
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       scsi_report_bus_reset(shost, 0);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       scsi_unblock_requests(shost);
+
        return 0;
 }
 
@@ -1045,10 +1097,12 @@ static void uas_disconnect(struct usb_interface *intf)
        struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
 
        devinfo->resetting = 1;
+       cancel_work_sync(&devinfo->work);
        uas_abort_work(devinfo);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
+       uas_zap_dead(devinfo);
        scsi_remove_host(shost);
        uas_free_streams(devinfo);
        kfree(devinfo);
This page took 0.069702 seconds and 5 git commands to generate.