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/storage | |
| 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/storage')
| -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 df1d72e4693..15789097edd 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); |
