aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/uas.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-12-02 14:55:44 -0500
committerSebastian Andrzej Siewior <bigeasy@linutronix.de>2011-12-22 04:12:54 -0500
commitea9da1c79eb9a28176550d0b8ba9166e6e5f42b8 (patch)
tree658a52b671d92b75db90153a832434c82504a4f7 /drivers/usb/storage/uas.c
parent5611cc4572e889b62a7b4c72a413536bf6a9c416 (diff)
UAS: Re-add workqueue items if submission fails.
If the original submission (or allocation) of the URBs for a SCSI command fails, the UAS driver sticks the command structure in a workqueue and schedules uas_do_work() to run. That function removes the entire queue before walking across it and attempting to resubmit. Unfortunately, if the second submission fails, we will leak memory (because an allocated URB was not submitted) and possibly leave the SCSI command partially enqueued on some of the stream rings. Fix this by checking whether the second submission failed and re-queueing the command to the UAS workqueue and scheduling it. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: Matthew Wilcox <willy@linux.intel.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Diffstat (limited to 'drivers/usb/storage/uas.c')
-rw-r--r--drivers/usb/storage/uas.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 1d10d5b8204c..4bbaf6e150e4 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -125,29 +125,38 @@ struct uas_cmd_info {
125/* I hate forward declarations, but I actually have a loop */ 125/* I hate forward declarations, but I actually have a loop */
126static int uas_submit_urbs(struct scsi_cmnd *cmnd, 126static int uas_submit_urbs(struct scsi_cmnd *cmnd,
127 struct uas_dev_info *devinfo, gfp_t gfp); 127 struct uas_dev_info *devinfo, gfp_t gfp);
128static void uas_do_work(struct work_struct *work);
128 129
130static DECLARE_WORK(uas_work, uas_do_work);
129static DEFINE_SPINLOCK(uas_work_lock); 131static DEFINE_SPINLOCK(uas_work_lock);
130static LIST_HEAD(uas_work_list); 132static LIST_HEAD(uas_work_list);
131 133
132static void uas_do_work(struct work_struct *work) 134static void uas_do_work(struct work_struct *work)
133{ 135{
134 struct uas_cmd_info *cmdinfo; 136 struct uas_cmd_info *cmdinfo;
137 struct uas_cmd_info *temp;
135 struct list_head list; 138 struct list_head list;
139 int err;
136 140
137 spin_lock_irq(&uas_work_lock); 141 spin_lock_irq(&uas_work_lock);
138 list_replace_init(&uas_work_list, &list); 142 list_replace_init(&uas_work_list, &list);
139 spin_unlock_irq(&uas_work_lock); 143 spin_unlock_irq(&uas_work_lock);
140 144
141 list_for_each_entry(cmdinfo, &list, list) { 145 list_for_each_entry_safe(cmdinfo, temp, &list, list) {
142 struct scsi_pointer *scp = (void *)cmdinfo; 146 struct scsi_pointer *scp = (void *)cmdinfo;
143 struct scsi_cmnd *cmnd = container_of(scp, 147 struct scsi_cmnd *cmnd = container_of(scp,
144 struct scsi_cmnd, SCp); 148 struct scsi_cmnd, SCp);
145 uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); 149 err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
150 if (err) {
151 list_del(&cmdinfo->list);
152 spin_lock_irq(&uas_work_lock);
153 list_add_tail(&cmdinfo->list, &uas_work_list);
154 spin_unlock_irq(&uas_work_lock);
155 schedule_work(&uas_work);
156 }
146 } 157 }
147} 158}
148 159
149static DECLARE_WORK(uas_work, uas_do_work);
150
151static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) 160static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
152{ 161{
153 struct sense_iu *sense_iu = urb->transfer_buffer; 162 struct sense_iu *sense_iu = urb->transfer_buffer;