aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage
diff options
context:
space:
mode:
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>2012-01-25 05:48:40 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-02-02 17:51:30 -0500
commite4d8318a85779b25b880187b1b1c44e797bd7d4b (patch)
treeff7fd6fda4ad80fcf35c199e27550b3e3239152e /drivers/usb/storage
parentee398b59ec1dc3ce1d60518f4ea644907893414b (diff)
usb/uas: make sure data urb is gone if we receive status before that
Just run into the following: - new disk arrived in the system - udev couldn't wait to get its hands on to to run ata_id /dev/sda - this sent the cdb 0xa1 to the device. - my UAS-gadget recevied the cdb and had no idea what to do with it. It decided to send a status URB back with sense set to invalid opcode. - the host side received it status and completed the scsi command. - the host sent another scsi with 4kib data buffer - Now I was confused why the data transfer is only 512 bytes instead of 4kib since the host is always allocating the complete transfer in one go. - Finally the system crashed while walking through the sg list. This patch adds three new flags in order to distinguish between DATA URB completed and outstanding. If we receive status before data, we cancel data and let data complete the command. This solves the problem for IN and OUT transfers but does not work for BIDI. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r--drivers/usb/storage/uas.c90
1 files changed, 75 insertions, 15 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index f98ba40352c..8ec8a6e66f5 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -58,6 +58,9 @@ enum {
58 SUBMIT_DATA_OUT_URB = (1 << 5), 58 SUBMIT_DATA_OUT_URB = (1 << 5),
59 ALLOC_CMD_URB = (1 << 6), 59 ALLOC_CMD_URB = (1 << 6),
60 SUBMIT_CMD_URB = (1 << 7), 60 SUBMIT_CMD_URB = (1 << 7),
61 COMPLETED_DATA_IN = (1 << 8),
62 COMPLETED_DATA_OUT = (1 << 9),
63 DATA_COMPLETES_CMD = (1 << 10),
61}; 64};
62 65
63/* Overrides scsi_pointer */ 66/* Overrides scsi_pointer */
@@ -111,6 +114,7 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
111{ 114{
112 struct sense_iu *sense_iu = urb->transfer_buffer; 115 struct sense_iu *sense_iu = urb->transfer_buffer;
113 struct scsi_device *sdev = cmnd->device; 116 struct scsi_device *sdev = cmnd->device;
117 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
114 118
115 if (urb->actual_length > 16) { 119 if (urb->actual_length > 16) {
116 unsigned len = be16_to_cpup(&sense_iu->len); 120 unsigned len = be16_to_cpup(&sense_iu->len);
@@ -128,13 +132,15 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
128 } 132 }
129 133
130 cmnd->result = sense_iu->status; 134 cmnd->result = sense_iu->status;
131 cmnd->scsi_done(cmnd); 135 if (!(cmdinfo->state & DATA_COMPLETES_CMD))
136 cmnd->scsi_done(cmnd);
132} 137}
133 138
134static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) 139static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
135{ 140{
136 struct sense_iu_old *sense_iu = urb->transfer_buffer; 141 struct sense_iu_old *sense_iu = urb->transfer_buffer;
137 struct scsi_device *sdev = cmnd->device; 142 struct scsi_device *sdev = cmnd->device;
143 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
138 144
139 if (urb->actual_length > 8) { 145 if (urb->actual_length > 8) {
140 unsigned len = be16_to_cpup(&sense_iu->len) - 2; 146 unsigned len = be16_to_cpup(&sense_iu->len) - 2;
@@ -152,7 +158,8 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
152 } 158 }
153 159
154 cmnd->result = sense_iu->status; 160 cmnd->result = sense_iu->status;
155 cmnd->scsi_done(cmnd); 161 if (!(cmdinfo->state & DATA_COMPLETES_CMD))
162 cmnd->scsi_done(cmnd);
156} 163}
157 164
158static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, 165static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
@@ -177,6 +184,7 @@ static void uas_stat_cmplt(struct urb *urb)
177 struct Scsi_Host *shost = urb->context; 184 struct Scsi_Host *shost = urb->context;
178 struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; 185 struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
179 struct scsi_cmnd *cmnd; 186 struct scsi_cmnd *cmnd;
187 struct uas_cmd_info *cmdinfo;
180 u16 tag; 188 u16 tag;
181 int ret; 189 int ret;
182 190
@@ -202,12 +210,32 @@ static void uas_stat_cmplt(struct urb *urb)
202 dev_err(&urb->dev->dev, "failed submit status urb\n"); 210 dev_err(&urb->dev->dev, "failed submit status urb\n");
203 return; 211 return;
204 } 212 }
213 cmdinfo = (void *)&cmnd->SCp;
205 214
206 switch (iu->iu_id) { 215 switch (iu->iu_id) {
207 case IU_ID_STATUS: 216 case IU_ID_STATUS:
208 if (devinfo->cmnd == cmnd) 217 if (devinfo->cmnd == cmnd)
209 devinfo->cmnd = NULL; 218 devinfo->cmnd = NULL;
210 219
220 if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
221 cmdinfo->data_in_urb) {
222 if (devinfo->use_streams) {
223 cmdinfo->state |= DATA_COMPLETES_CMD;
224 usb_unlink_urb(cmdinfo->data_in_urb);
225 } else {
226 usb_free_urb(cmdinfo->data_in_urb);
227 }
228 }
229 if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
230 cmdinfo->data_out_urb) {
231 if (devinfo->use_streams) {
232 cmdinfo->state |= DATA_COMPLETES_CMD;
233 usb_unlink_urb(cmdinfo->data_in_urb);
234 } else {
235 usb_free_urb(cmdinfo->data_out_urb);
236 }
237 }
238
211 if (urb->actual_length < 16) 239 if (urb->actual_length < 16)
212 devinfo->uas_sense_old = 1; 240 devinfo->uas_sense_old = 1;
213 if (devinfo->uas_sense_old) 241 if (devinfo->uas_sense_old)
@@ -236,27 +264,59 @@ static void uas_stat_cmplt(struct urb *urb)
236 dev_err(&urb->dev->dev, "failed submit status urb\n"); 264 dev_err(&urb->dev->dev, "failed submit status urb\n");
237} 265}
238 266
239static void uas_data_cmplt(struct urb *urb) 267static void uas_data_out_cmplt(struct urb *urb)
268{
269 struct scsi_cmnd *cmnd = urb->context;
270 struct scsi_data_buffer *sdb = scsi_out(cmnd);
271 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
272
273 cmdinfo->state |= COMPLETED_DATA_OUT;
274
275 sdb->resid = sdb->length - urb->actual_length;
276 usb_free_urb(urb);
277
278 if (cmdinfo->state & DATA_COMPLETES_CMD)
279 cmnd->scsi_done(cmnd);
280}
281
282static void uas_data_in_cmplt(struct urb *urb)
240{ 283{
241 struct scsi_data_buffer *sdb = urb->context; 284 struct scsi_cmnd *cmnd = urb->context;
285 struct scsi_data_buffer *sdb = scsi_in(cmnd);
286 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
287
288 cmdinfo->state |= COMPLETED_DATA_IN;
289
242 sdb->resid = sdb->length - urb->actual_length; 290 sdb->resid = sdb->length - urb->actual_length;
243 usb_free_urb(urb); 291 usb_free_urb(urb);
292
293 if (cmdinfo->state & DATA_COMPLETES_CMD)
294 cmnd->scsi_done(cmnd);
244} 295}
245 296
246static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, 297static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
247 unsigned int pipe, u16 stream_id, 298 unsigned int pipe, struct scsi_cmnd *cmnd,
248 struct scsi_data_buffer *sdb, 299 enum dma_data_direction dir)
249 enum dma_data_direction dir)
250{ 300{
301 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
251 struct usb_device *udev = devinfo->udev; 302 struct usb_device *udev = devinfo->udev;
252 struct urb *urb = usb_alloc_urb(0, gfp); 303 struct urb *urb = usb_alloc_urb(0, gfp);
304 struct scsi_data_buffer *sdb;
305 usb_complete_t complete_fn;
306 u16 stream_id = cmdinfo->stream;
253 307
254 if (!urb) 308 if (!urb)
255 goto out; 309 goto out;
256 usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, 310 if (dir == DMA_FROM_DEVICE) {
257 sdb); 311 sdb = scsi_in(cmnd);
258 if (devinfo->use_streams) 312 complete_fn = uas_data_in_cmplt;
259 urb->stream_id = stream_id; 313 } else {
314 sdb = scsi_out(cmnd);
315 complete_fn = uas_data_out_cmplt;
316 }
317 usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
318 complete_fn, cmnd);
319 urb->stream_id = stream_id;
260 urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; 320 urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
261 urb->sg = sdb->table.sgl; 321 urb->sg = sdb->table.sgl;
262 out: 322 out:
@@ -358,8 +418,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
358 418
359 if (cmdinfo->state & ALLOC_DATA_IN_URB) { 419 if (cmdinfo->state & ALLOC_DATA_IN_URB) {
360 cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, 420 cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
361 devinfo->data_in_pipe, cmdinfo->stream, 421 devinfo->data_in_pipe, cmnd,
362 scsi_in(cmnd), DMA_FROM_DEVICE); 422 DMA_FROM_DEVICE);
363 if (!cmdinfo->data_in_urb) 423 if (!cmdinfo->data_in_urb)
364 return SCSI_MLQUEUE_DEVICE_BUSY; 424 return SCSI_MLQUEUE_DEVICE_BUSY;
365 cmdinfo->state &= ~ALLOC_DATA_IN_URB; 425 cmdinfo->state &= ~ALLOC_DATA_IN_URB;
@@ -376,8 +436,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
376 436
377 if (cmdinfo->state & ALLOC_DATA_OUT_URB) { 437 if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
378 cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, 438 cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
379 devinfo->data_out_pipe, cmdinfo->stream, 439 devinfo->data_out_pipe, cmnd,
380 scsi_out(cmnd), DMA_TO_DEVICE); 440 DMA_TO_DEVICE);
381 if (!cmdinfo->data_out_urb) 441 if (!cmdinfo->data_out_urb)
382 return SCSI_MLQUEUE_DEVICE_BUSY; 442 return SCSI_MLQUEUE_DEVICE_BUSY;
383 cmdinfo->state &= ~ALLOC_DATA_OUT_URB; 443 cmdinfo->state &= ~ALLOC_DATA_OUT_URB;