diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2012-09-25 04:47:08 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-09-25 18:32:23 -0400 |
commit | e064852072c47b69f62325c6b7fa4a58332655bd (patch) | |
tree | 6c73f09b979339b6740aafab36835c94e06af591 /drivers/usb | |
parent | 0871d7d86ce3788ba40391df3a259df2285776cd (diff) |
USB: uas: add locking
Add spinlock to protect uas data structures.
[ v2: s/GFP_NOIO/GFP_ATOMIC/, better don't sleep when holding a spinlock ]
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/storage/uas.c | 51 |
1 files changed, 44 insertions, 7 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index df1d72e46933..15789097edd6 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c | |||
@@ -50,6 +50,7 @@ struct uas_dev_info { | |||
50 | unsigned use_streams:1; | 50 | unsigned use_streams:1; |
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 | }; | 54 | }; |
54 | 55 | ||
55 | enum { | 56 | enum { |
@@ -91,6 +92,7 @@ static void uas_do_work(struct work_struct *work) | |||
91 | struct uas_cmd_info *cmdinfo; | 92 | struct uas_cmd_info *cmdinfo; |
92 | struct uas_cmd_info *temp; | 93 | struct uas_cmd_info *temp; |
93 | struct list_head list; | 94 | struct list_head list; |
95 | unsigned long flags; | ||
94 | int err; | 96 | int err; |
95 | 97 | ||
96 | spin_lock_irq(&uas_work_lock); | 98 | spin_lock_irq(&uas_work_lock); |
@@ -101,7 +103,10 @@ static void uas_do_work(struct work_struct *work) | |||
101 | struct scsi_pointer *scp = (void *)cmdinfo; | 103 | struct scsi_pointer *scp = (void *)cmdinfo; |
102 | struct scsi_cmnd *cmnd = container_of(scp, | 104 | struct scsi_cmnd *cmnd = container_of(scp, |
103 | struct scsi_cmnd, SCp); | 105 | struct scsi_cmnd, SCp); |
104 | err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); | 106 | struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; |
107 | spin_lock_irqsave(&devinfo->lock, flags); | ||
108 | err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); | ||
109 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
105 | if (err) { | 110 | if (err) { |
106 | list_del(&cmdinfo->list); | 111 | list_del(&cmdinfo->list); |
107 | spin_lock_irq(&uas_work_lock); | 112 | spin_lock_irq(&uas_work_lock); |
@@ -182,7 +187,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller) | |||
182 | static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) | 187 | static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) |
183 | { | 188 | { |
184 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; | 189 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
190 | struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; | ||
185 | 191 | ||
192 | WARN_ON(!spin_is_locked(&devinfo->lock)); | ||
186 | if (cmdinfo->state & (COMMAND_INFLIGHT | | 193 | if (cmdinfo->state & (COMMAND_INFLIGHT | |
187 | DATA_IN_URB_INFLIGHT | | 194 | DATA_IN_URB_INFLIGHT | |
188 | DATA_OUT_URB_INFLIGHT)) | 195 | DATA_OUT_URB_INFLIGHT)) |
@@ -222,6 +229,7 @@ static void uas_stat_cmplt(struct urb *urb) | |||
222 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; | 229 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; |
223 | struct scsi_cmnd *cmnd; | 230 | struct scsi_cmnd *cmnd; |
224 | struct uas_cmd_info *cmdinfo; | 231 | struct uas_cmd_info *cmdinfo; |
232 | unsigned long flags; | ||
225 | u16 tag; | 233 | u16 tag; |
226 | 234 | ||
227 | if (urb->status) { | 235 | if (urb->status) { |
@@ -235,6 +243,7 @@ static void uas_stat_cmplt(struct urb *urb) | |||
235 | return; | 243 | return; |
236 | } | 244 | } |
237 | 245 | ||
246 | spin_lock_irqsave(&devinfo->lock, flags); | ||
238 | tag = be16_to_cpup(&iu->tag) - 1; | 247 | tag = be16_to_cpup(&iu->tag) - 1; |
239 | if (tag == 0) | 248 | if (tag == 0) |
240 | cmnd = devinfo->cmnd; | 249 | cmnd = devinfo->cmnd; |
@@ -243,6 +252,7 @@ static void uas_stat_cmplt(struct urb *urb) | |||
243 | if (!cmnd) { | 252 | if (!cmnd) { |
244 | if (iu->iu_id != IU_ID_RESPONSE) { | 253 | if (iu->iu_id != IU_ID_RESPONSE) { |
245 | usb_free_urb(urb); | 254 | usb_free_urb(urb); |
255 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
246 | return; | 256 | return; |
247 | } | 257 | } |
248 | } else { | 258 | } else { |
@@ -262,10 +272,16 @@ static void uas_stat_cmplt(struct urb *urb) | |||
262 | uas_sense(urb, cmnd); | 272 | uas_sense(urb, cmnd); |
263 | if (cmnd->result != 0) { | 273 | if (cmnd->result != 0) { |
264 | /* cancel data transfers on error */ | 274 | /* cancel data transfers on error */ |
265 | if (cmdinfo->state & DATA_IN_URB_INFLIGHT) | 275 | if (cmdinfo->state & DATA_IN_URB_INFLIGHT) { |
276 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
266 | usb_unlink_urb(cmdinfo->data_in_urb); | 277 | usb_unlink_urb(cmdinfo->data_in_urb); |
267 | if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) | 278 | spin_lock_irqsave(&devinfo->lock, flags); |
279 | } | ||
280 | if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) { | ||
281 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
268 | usb_unlink_urb(cmdinfo->data_out_urb); | 282 | usb_unlink_urb(cmdinfo->data_out_urb); |
283 | spin_lock_irqsave(&devinfo->lock, flags); | ||
284 | } | ||
269 | } | 285 | } |
270 | cmdinfo->state &= ~COMMAND_INFLIGHT; | 286 | cmdinfo->state &= ~COMMAND_INFLIGHT; |
271 | uas_try_complete(cmnd, __func__); | 287 | uas_try_complete(cmnd, __func__); |
@@ -285,14 +301,18 @@ static void uas_stat_cmplt(struct urb *urb) | |||
285 | "Bogus IU (%d) received on status pipe\n", iu->iu_id); | 301 | "Bogus IU (%d) received on status pipe\n", iu->iu_id); |
286 | } | 302 | } |
287 | usb_free_urb(urb); | 303 | usb_free_urb(urb); |
304 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
288 | } | 305 | } |
289 | 306 | ||
290 | static void uas_data_cmplt(struct urb *urb) | 307 | static void uas_data_cmplt(struct urb *urb) |
291 | { | 308 | { |
292 | struct scsi_cmnd *cmnd = urb->context; | 309 | struct scsi_cmnd *cmnd = urb->context; |
293 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; | 310 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
311 | struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; | ||
294 | struct scsi_data_buffer *sdb = NULL; | 312 | struct scsi_data_buffer *sdb = NULL; |
313 | unsigned long flags; | ||
295 | 314 | ||
315 | spin_lock_irqsave(&devinfo->lock, flags); | ||
296 | if (cmdinfo->data_in_urb == urb) { | 316 | if (cmdinfo->data_in_urb == urb) { |
297 | sdb = scsi_in(cmnd); | 317 | sdb = scsi_in(cmnd); |
298 | cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; | 318 | cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; |
@@ -308,6 +328,7 @@ static void uas_data_cmplt(struct urb *urb) | |||
308 | sdb->resid = sdb->length - urb->actual_length; | 328 | sdb->resid = sdb->length - urb->actual_length; |
309 | } | 329 | } |
310 | uas_try_complete(cmnd, __func__); | 330 | uas_try_complete(cmnd, __func__); |
331 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
311 | } | 332 | } |
312 | 333 | ||
313 | static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, | 334 | static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, |
@@ -474,6 +495,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, | |||
474 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; | 495 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
475 | int err; | 496 | int err; |
476 | 497 | ||
498 | WARN_ON(!spin_is_locked(&devinfo->lock)); | ||
477 | if (cmdinfo->state & SUBMIT_STATUS_URB) { | 499 | if (cmdinfo->state & SUBMIT_STATUS_URB) { |
478 | err = uas_submit_sense_urb(cmnd->device->host, gfp, | 500 | err = uas_submit_sense_urb(cmnd->device->host, gfp, |
479 | cmdinfo->stream); | 501 | cmdinfo->stream); |
@@ -554,12 +576,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, | |||
554 | struct scsi_device *sdev = cmnd->device; | 576 | struct scsi_device *sdev = cmnd->device; |
555 | struct uas_dev_info *devinfo = sdev->hostdata; | 577 | struct uas_dev_info *devinfo = sdev->hostdata; |
556 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; | 578 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
579 | unsigned long flags; | ||
557 | int err; | 580 | int err; |
558 | 581 | ||
559 | BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); | 582 | BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); |
560 | 583 | ||
561 | if (devinfo->cmnd) | 584 | spin_lock_irqsave(&devinfo->lock, flags); |
585 | if (devinfo->cmnd) { | ||
586 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
562 | return SCSI_MLQUEUE_DEVICE_BUSY; | 587 | return SCSI_MLQUEUE_DEVICE_BUSY; |
588 | } | ||
563 | 589 | ||
564 | if (blk_rq_tagged(cmnd->request)) { | 590 | if (blk_rq_tagged(cmnd->request)) { |
565 | cmdinfo->stream = cmnd->request->tag + 2; | 591 | cmdinfo->stream = cmnd->request->tag + 2; |
@@ -594,6 +620,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, | |||
594 | if (err) { | 620 | if (err) { |
595 | /* If we did nothing, give up now */ | 621 | /* If we did nothing, give up now */ |
596 | if (cmdinfo->state & SUBMIT_STATUS_URB) { | 622 | if (cmdinfo->state & SUBMIT_STATUS_URB) { |
623 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
597 | return SCSI_MLQUEUE_DEVICE_BUSY; | 624 | return SCSI_MLQUEUE_DEVICE_BUSY; |
598 | } | 625 | } |
599 | spin_lock(&uas_work_lock); | 626 | spin_lock(&uas_work_lock); |
@@ -602,6 +629,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, | |||
602 | schedule_work(&uas_work); | 629 | schedule_work(&uas_work); |
603 | } | 630 | } |
604 | 631 | ||
632 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
605 | return 0; | 633 | return 0; |
606 | } | 634 | } |
607 | 635 | ||
@@ -613,21 +641,25 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, | |||
613 | struct Scsi_Host *shost = cmnd->device->host; | 641 | struct Scsi_Host *shost = cmnd->device->host; |
614 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; | 642 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; |
615 | u16 tag = devinfo->qdepth - 1; | 643 | u16 tag = devinfo->qdepth - 1; |
644 | unsigned long flags; | ||
616 | 645 | ||
646 | spin_lock_irqsave(&devinfo->lock, flags); | ||
617 | memset(&devinfo->response, 0, sizeof(devinfo->response)); | 647 | memset(&devinfo->response, 0, sizeof(devinfo->response)); |
618 | if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { | 648 | if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) { |
619 | shost_printk(KERN_INFO, shost, | 649 | shost_printk(KERN_INFO, shost, |
620 | "%s: %s: submit sense urb failed\n", | 650 | "%s: %s: submit sense urb failed\n", |
621 | __func__, fname); | 651 | __func__, fname); |
622 | return FAILED; | 652 | return FAILED; |
623 | } | 653 | } |
624 | if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { | 654 | if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) { |
625 | shost_printk(KERN_INFO, shost, | 655 | shost_printk(KERN_INFO, shost, |
626 | "%s: %s: submit task mgmt urb failed\n", | 656 | "%s: %s: submit task mgmt urb failed\n", |
627 | __func__, fname); | 657 | __func__, fname); |
628 | return FAILED; | 658 | return FAILED; |
629 | } | 659 | } |
630 | if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { | 660 | spin_unlock_irqrestore(&devinfo->lock, flags); |
661 | |||
662 | if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) { | ||
631 | shost_printk(KERN_INFO, shost, | 663 | shost_printk(KERN_INFO, shost, |
632 | "%s: %s timed out\n", __func__, fname); | 664 | "%s: %s timed out\n", __func__, fname); |
633 | return FAILED; | 665 | return FAILED; |
@@ -650,10 +682,14 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, | |||
650 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) | 682 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) |
651 | { | 683 | { |
652 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; | 684 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
685 | struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; | ||
686 | unsigned long flags; | ||
653 | int ret; | 687 | int ret; |
654 | 688 | ||
655 | uas_log_cmd_state(cmnd, __func__); | 689 | uas_log_cmd_state(cmnd, __func__); |
690 | spin_lock_irqsave(&devinfo->lock, flags); | ||
656 | cmdinfo->state |= COMMAND_ABORTED; | 691 | cmdinfo->state |= COMMAND_ABORTED; |
692 | spin_unlock_irqrestore(&devinfo->lock, flags); | ||
657 | ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); | 693 | ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); |
658 | return ret; | 694 | return ret; |
659 | } | 695 | } |
@@ -875,6 +911,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) | |||
875 | init_usb_anchor(&devinfo->cmd_urbs); | 911 | init_usb_anchor(&devinfo->cmd_urbs); |
876 | init_usb_anchor(&devinfo->sense_urbs); | 912 | init_usb_anchor(&devinfo->sense_urbs); |
877 | init_usb_anchor(&devinfo->data_urbs); | 913 | init_usb_anchor(&devinfo->data_urbs); |
914 | spin_lock_init(&devinfo->lock); | ||
878 | uas_configure_endpoints(devinfo); | 915 | uas_configure_endpoints(devinfo); |
879 | 916 | ||
880 | result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); | 917 | result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); |