aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2013-09-13 07:27:12 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2014-03-04 18:38:05 -0500
commit1bf8198e6b2da3e30960e95f8d215f44572515ce (patch)
treed1904687076cb3cee8afc81bce412e6928464da5 /drivers/usb/storage
parentd89bd835326947e6618b97469159d3266016fe0a (diff)
uas: make work list per-device
Simplifies locking, we'll protect the list with the device spin lock. Also plugs races which can happen when two devices operate on the global list. While being at it rename the list head from "list" to "work", preparing for the addition of a second list. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r--drivers/usb/storage/uas.c106
1 files changed, 44 insertions, 62 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index fc08ee919439..3cf5a5ff3d53 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -51,6 +51,8 @@ struct uas_dev_info {
51 unsigned uas_sense_old:1; 51 unsigned uas_sense_old:1;
52 struct scsi_cmnd *cmnd; 52 struct scsi_cmnd *cmnd;
53 spinlock_t lock; 53 spinlock_t lock;
54 struct work_struct work;
55 struct list_head work_list;
54}; 56};
55 57
56enum { 58enum {
@@ -77,7 +79,7 @@ struct uas_cmd_info {
77 struct urb *cmd_urb; 79 struct urb *cmd_urb;
78 struct urb *data_in_urb; 80 struct urb *data_in_urb;
79 struct urb *data_out_urb; 81 struct urb *data_out_urb;
80 struct list_head list; 82 struct list_head work;
81}; 83};
82 84
83/* I hate forward declarations, but I actually have a loop */ 85/* I hate forward declarations, but I actually have a loop */
@@ -88,10 +90,6 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
88static void uas_configure_endpoints(struct uas_dev_info *devinfo); 90static void uas_configure_endpoints(struct uas_dev_info *devinfo);
89static void uas_free_streams(struct uas_dev_info *devinfo); 91static void uas_free_streams(struct uas_dev_info *devinfo);
90 92
91static DECLARE_WORK(uas_work, uas_do_work);
92static DEFINE_SPINLOCK(uas_work_lock);
93static LIST_HEAD(uas_work_list);
94
95static void uas_unlink_data_urbs(struct uas_dev_info *devinfo, 93static void uas_unlink_data_urbs(struct uas_dev_info *devinfo,
96 struct uas_cmd_info *cmdinfo) 94 struct uas_cmd_info *cmdinfo)
97{ 95{
@@ -118,75 +116,66 @@ static void uas_unlink_data_urbs(struct uas_dev_info *devinfo,
118 116
119static void uas_do_work(struct work_struct *work) 117static void uas_do_work(struct work_struct *work)
120{ 118{
119 struct uas_dev_info *devinfo =
120 container_of(work, struct uas_dev_info, work);
121 struct uas_cmd_info *cmdinfo; 121 struct uas_cmd_info *cmdinfo;
122 struct uas_cmd_info *temp; 122 struct uas_cmd_info *temp;
123 struct list_head list;
124 unsigned long flags; 123 unsigned long flags;
125 int err; 124 int err;
126 125
127 spin_lock_irq(&uas_work_lock); 126 spin_lock_irqsave(&devinfo->lock, flags);
128 list_replace_init(&uas_work_list, &list); 127 list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) {
129 spin_unlock_irq(&uas_work_lock);
130
131 list_for_each_entry_safe(cmdinfo, temp, &list, list) {
132 struct scsi_pointer *scp = (void *)cmdinfo; 128 struct scsi_pointer *scp = (void *)cmdinfo;
133 struct scsi_cmnd *cmnd = container_of(scp, 129 struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
134 struct scsi_cmnd, SCp); 130 SCp);
135 struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
136 spin_lock_irqsave(&devinfo->lock, flags);
137 err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); 131 err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
138 if (!err) 132 if (!err) {
139 cmdinfo->state &= ~IS_IN_WORK_LIST; 133 cmdinfo->state &= ~IS_IN_WORK_LIST;
140 spin_unlock_irqrestore(&devinfo->lock, flags); 134 list_del(&cmdinfo->work);
141 if (err) { 135 } else {
142 list_del(&cmdinfo->list); 136 schedule_work(&devinfo->work);
143 spin_lock_irq(&uas_work_lock);
144 list_add_tail(&cmdinfo->list, &uas_work_list);
145 spin_unlock_irq(&uas_work_lock);
146 schedule_work(&uas_work);
147 } 137 }
148 } 138 }
139 spin_unlock_irqrestore(&devinfo->lock, flags);
149} 140}
150 141
151static void uas_abort_work(struct uas_dev_info *devinfo) 142static void uas_abort_work(struct uas_dev_info *devinfo)
152{ 143{
153 struct uas_cmd_info *cmdinfo; 144 struct uas_cmd_info *cmdinfo;
154 struct uas_cmd_info *temp; 145 struct uas_cmd_info *temp;
155 struct list_head list;
156 unsigned long flags; 146 unsigned long flags;
157 147
158 spin_lock_irq(&uas_work_lock);
159 list_replace_init(&uas_work_list, &list);
160 spin_unlock_irq(&uas_work_lock);
161
162 spin_lock_irqsave(&devinfo->lock, flags); 148 spin_lock_irqsave(&devinfo->lock, flags);
163 list_for_each_entry_safe(cmdinfo, temp, &list, list) { 149 list_for_each_entry_safe(cmdinfo, temp, &devinfo->work_list, work) {
164 struct scsi_pointer *scp = (void *)cmdinfo; 150 struct scsi_pointer *scp = (void *)cmdinfo;
165 struct scsi_cmnd *cmnd = container_of(scp, 151 struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
166 struct scsi_cmnd, SCp); 152 SCp);
167 struct uas_dev_info *di = (void *)cmnd->device->hostdata; 153 cmdinfo->state |= COMMAND_ABORTED;
168 154 cmdinfo->state &= ~IS_IN_WORK_LIST;
169 if (di == devinfo) { 155 if (devinfo->resetting) {
170 cmdinfo->state |= COMMAND_ABORTED; 156 /* uas_stat_cmplt() will not do that
171 cmdinfo->state &= ~IS_IN_WORK_LIST; 157 * when a device reset is in
172 if (devinfo->resetting) { 158 * progress */
173 /* uas_stat_cmplt() will not do that 159 cmdinfo->state &= ~COMMAND_INFLIGHT;
174 * when a device reset is in
175 * progress */
176 cmdinfo->state &= ~COMMAND_INFLIGHT;
177 }
178 uas_try_complete(cmnd, __func__);
179 } else {
180 /* not our uas device, relink into list */
181 list_del(&cmdinfo->list);
182 spin_lock_irq(&uas_work_lock);
183 list_add_tail(&cmdinfo->list, &uas_work_list);
184 spin_unlock_irq(&uas_work_lock);
185 } 160 }
161 uas_try_complete(cmnd, __func__);
162 list_del(&cmdinfo->work);
186 } 163 }
187 spin_unlock_irqrestore(&devinfo->lock, flags); 164 spin_unlock_irqrestore(&devinfo->lock, flags);
188} 165}
189 166
167static void uas_add_work(struct uas_cmd_info *cmdinfo)
168{
169 struct scsi_pointer *scp = (void *)cmdinfo;
170 struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp);
171 struct uas_dev_info *devinfo = cmnd->device->hostdata;
172
173 WARN_ON(!spin_is_locked(&devinfo->lock));
174 list_add_tail(&cmdinfo->work, &devinfo->work_list);
175 cmdinfo->state |= IS_IN_WORK_LIST;
176 schedule_work(&devinfo->work);
177}
178
190static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) 179static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
191{ 180{
192 struct sense_iu *sense_iu = urb->transfer_buffer; 181 struct sense_iu *sense_iu = urb->transfer_buffer;
@@ -288,11 +277,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
288 cmdinfo->state |= direction | SUBMIT_STATUS_URB; 277 cmdinfo->state |= direction | SUBMIT_STATUS_URB;
289 err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); 278 err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
290 if (err) { 279 if (err) {
291 spin_lock(&uas_work_lock); 280 uas_add_work(cmdinfo);
292 list_add_tail(&cmdinfo->list, &uas_work_list);
293 cmdinfo->state |= IS_IN_WORK_LIST;
294 spin_unlock(&uas_work_lock);
295 schedule_work(&uas_work);
296 } 281 }
297} 282}
298 283
@@ -694,11 +679,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
694 spin_unlock_irqrestore(&devinfo->lock, flags); 679 spin_unlock_irqrestore(&devinfo->lock, flags);
695 return SCSI_MLQUEUE_DEVICE_BUSY; 680 return SCSI_MLQUEUE_DEVICE_BUSY;
696 } 681 }
697 spin_lock(&uas_work_lock); 682 uas_add_work(cmdinfo);
698 list_add_tail(&cmdinfo->list, &uas_work_list);
699 cmdinfo->state |= IS_IN_WORK_LIST;
700 spin_unlock(&uas_work_lock);
701 schedule_work(&uas_work);
702 } 683 }
703 684
704 spin_unlock_irqrestore(&devinfo->lock, flags); 685 spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -764,10 +745,8 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
764 spin_lock_irqsave(&devinfo->lock, flags); 745 spin_lock_irqsave(&devinfo->lock, flags);
765 cmdinfo->state |= COMMAND_ABORTED; 746 cmdinfo->state |= COMMAND_ABORTED;
766 if (cmdinfo->state & IS_IN_WORK_LIST) { 747 if (cmdinfo->state & IS_IN_WORK_LIST) {
767 spin_lock(&uas_work_lock); 748 list_del(&cmdinfo->work);
768 list_del(&cmdinfo->list);
769 cmdinfo->state &= ~IS_IN_WORK_LIST; 749 cmdinfo->state &= ~IS_IN_WORK_LIST;
770 spin_unlock(&uas_work_lock);
771 } 750 }
772 if (cmdinfo->state & COMMAND_INFLIGHT) { 751 if (cmdinfo->state & COMMAND_INFLIGHT) {
773 spin_unlock_irqrestore(&devinfo->lock, flags); 752 spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -1007,6 +986,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
1007 init_usb_anchor(&devinfo->sense_urbs); 986 init_usb_anchor(&devinfo->sense_urbs);
1008 init_usb_anchor(&devinfo->data_urbs); 987 init_usb_anchor(&devinfo->data_urbs);
1009 spin_lock_init(&devinfo->lock); 988 spin_lock_init(&devinfo->lock);
989 INIT_WORK(&devinfo->work, uas_do_work);
990 INIT_LIST_HEAD(&devinfo->work_list);
1010 uas_configure_endpoints(devinfo); 991 uas_configure_endpoints(devinfo);
1011 992
1012 result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); 993 result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3);
@@ -1050,6 +1031,7 @@ static void uas_disconnect(struct usb_interface *intf)
1050 struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; 1031 struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
1051 1032
1052 devinfo->resetting = 1; 1033 devinfo->resetting = 1;
1034 cancel_work_sync(&devinfo->work);
1053 uas_abort_work(devinfo); 1035 uas_abort_work(devinfo);
1054 usb_kill_anchored_urbs(&devinfo->cmd_urbs); 1036 usb_kill_anchored_urbs(&devinfo->cmd_urbs);
1055 usb_kill_anchored_urbs(&devinfo->sense_urbs); 1037 usb_kill_anchored_urbs(&devinfo->sense_urbs);