diff options
| author | Oliver Neukum <oliver@neukum.org> | 2010-02-27 14:54:24 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-19 10:24:14 -0400 |
| commit | 860e41a71c1731e79e1920dc42676bafc925af5e (patch) | |
| tree | 3374857a0a3da76cf37ccde54c3fe3230223a034 /drivers/usb/class | |
| parent | aa4714560b4ea359bb7830188ebd06bce71bcdea (diff) | |
usb: cdc-wdm: Fix race between write and disconnect
Unify mutexes to fix a race between write and disconnect
and shift the test for disconnection to always report it.
Signed-off-by: Oliver Neukum <neukum@b1-systems.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class')
| -rw-r--r-- | drivers/usb/class/cdc-wdm.c | 84 |
1 files changed, 45 insertions, 39 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 18aafcb08fc8..cf1c5fb918dc 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c | |||
| @@ -87,9 +87,7 @@ struct wdm_device { | |||
| 87 | int count; | 87 | int count; |
| 88 | dma_addr_t shandle; | 88 | dma_addr_t shandle; |
| 89 | dma_addr_t ihandle; | 89 | dma_addr_t ihandle; |
| 90 | struct mutex wlock; | 90 | struct mutex lock; |
| 91 | struct mutex rlock; | ||
| 92 | struct mutex plock; | ||
| 93 | wait_queue_head_t wait; | 91 | wait_queue_head_t wait; |
| 94 | struct work_struct rxwork; | 92 | struct work_struct rxwork; |
| 95 | int werr; | 93 | int werr; |
| @@ -305,14 +303,38 @@ static ssize_t wdm_write | |||
| 305 | if (we < 0) | 303 | if (we < 0) |
| 306 | return -EIO; | 304 | return -EIO; |
| 307 | 305 | ||
| 308 | r = mutex_lock_interruptible(&desc->wlock); /* concurrent writes */ | 306 | desc->outbuf = buf = kmalloc(count, GFP_KERNEL); |
| 307 | if (!buf) { | ||
| 308 | rv = -ENOMEM; | ||
| 309 | goto outnl; | ||
| 310 | } | ||
| 311 | |||
| 312 | r = copy_from_user(buf, buffer, count); | ||
| 313 | if (r > 0) { | ||
| 314 | kfree(buf); | ||
| 315 | rv = -EFAULT; | ||
| 316 | goto outnl; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* concurrent writes and disconnect */ | ||
| 320 | r = mutex_lock_interruptible(&desc->lock); | ||
| 309 | rv = -ERESTARTSYS; | 321 | rv = -ERESTARTSYS; |
| 310 | if (r) | 322 | if (r) { |
| 323 | kfree(buf); | ||
| 311 | goto outnl; | 324 | goto outnl; |
| 325 | } | ||
| 326 | |||
| 327 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { | ||
| 328 | kfree(buf); | ||
| 329 | rv = -ENODEV; | ||
| 330 | goto outnp; | ||
| 331 | } | ||
| 312 | 332 | ||
| 313 | r = usb_autopm_get_interface(desc->intf); | 333 | r = usb_autopm_get_interface(desc->intf); |
| 314 | if (r < 0) | 334 | if (r < 0) { |
| 335 | kfree(buf); | ||
| 315 | goto outnp; | 336 | goto outnp; |
| 337 | } | ||
| 316 | 338 | ||
| 317 | if (!file->f_flags && O_NONBLOCK) | 339 | if (!file->f_flags && O_NONBLOCK) |
| 318 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, | 340 | r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE, |
| @@ -320,24 +342,8 @@ static ssize_t wdm_write | |||
| 320 | else | 342 | else |
| 321 | if (test_bit(WDM_IN_USE, &desc->flags)) | 343 | if (test_bit(WDM_IN_USE, &desc->flags)) |
| 322 | r = -EAGAIN; | 344 | r = -EAGAIN; |
| 323 | if (r < 0) | 345 | if (r < 0) { |
| 324 | goto out; | ||
| 325 | |||
| 326 | if (test_bit(WDM_DISCONNECTING, &desc->flags)) { | ||
| 327 | rv = -ENODEV; | ||
| 328 | goto out; | ||
| 329 | } | ||
| 330 | |||
| 331 | desc->outbuf = buf = kmalloc(count, GFP_KERNEL); | ||
| 332 | if (!buf) { | ||
| 333 | rv = -ENOMEM; | ||
| 334 | goto out; | ||
| 335 | } | ||
| 336 | |||
| 337 | r = copy_from_user(buf, buffer, count); | ||
| 338 | if (r > 0) { | ||
| 339 | kfree(buf); | 346 | kfree(buf); |
| 340 | rv = -EFAULT; | ||
| 341 | goto out; | 347 | goto out; |
| 342 | } | 348 | } |
| 343 | 349 | ||
| @@ -374,7 +380,7 @@ static ssize_t wdm_write | |||
| 374 | out: | 380 | out: |
| 375 | usb_autopm_put_interface(desc->intf); | 381 | usb_autopm_put_interface(desc->intf); |
| 376 | outnp: | 382 | outnp: |
| 377 | mutex_unlock(&desc->wlock); | 383 | mutex_unlock(&desc->lock); |
| 378 | outnl: | 384 | outnl: |
| 379 | return rv < 0 ? rv : count; | 385 | return rv < 0 ? rv : count; |
| 380 | } | 386 | } |
| @@ -387,7 +393,7 @@ static ssize_t wdm_read | |||
| 387 | struct wdm_device *desc = file->private_data; | 393 | struct wdm_device *desc = file->private_data; |
| 388 | 394 | ||
| 389 | 395 | ||
| 390 | rv = mutex_lock_interruptible(&desc->rlock); /*concurrent reads */ | 396 | rv = mutex_lock_interruptible(&desc->lock); /*concurrent reads */ |
| 391 | if (rv < 0) | 397 | if (rv < 0) |
| 392 | return -ERESTARTSYS; | 398 | return -ERESTARTSYS; |
| 393 | 399 | ||
| @@ -465,7 +471,7 @@ retry: | |||
| 465 | rv = cntr; | 471 | rv = cntr; |
| 466 | 472 | ||
| 467 | err: | 473 | err: |
| 468 | mutex_unlock(&desc->rlock); | 474 | mutex_unlock(&desc->lock); |
| 469 | if (rv < 0 && rv != -EAGAIN) | 475 | if (rv < 0 && rv != -EAGAIN) |
| 470 | dev_err(&desc->intf->dev, "wdm_read: exit error\n"); | 476 | dev_err(&desc->intf->dev, "wdm_read: exit error\n"); |
| 471 | return rv; | 477 | return rv; |
| @@ -533,7 +539,7 @@ static int wdm_open(struct inode *inode, struct file *file) | |||
| 533 | } | 539 | } |
| 534 | intf->needs_remote_wakeup = 1; | 540 | intf->needs_remote_wakeup = 1; |
| 535 | 541 | ||
| 536 | mutex_lock(&desc->plock); | 542 | mutex_lock(&desc->lock); |
| 537 | if (!desc->count++) { | 543 | if (!desc->count++) { |
| 538 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); | 544 | rv = usb_submit_urb(desc->validity, GFP_KERNEL); |
| 539 | if (rv < 0) { | 545 | if (rv < 0) { |
| @@ -544,7 +550,7 @@ static int wdm_open(struct inode *inode, struct file *file) | |||
| 544 | } else { | 550 | } else { |
| 545 | rv = 0; | 551 | rv = 0; |
| 546 | } | 552 | } |
| 547 | mutex_unlock(&desc->plock); | 553 | mutex_unlock(&desc->lock); |
| 548 | usb_autopm_put_interface(desc->intf); | 554 | usb_autopm_put_interface(desc->intf); |
| 549 | out: | 555 | out: |
| 550 | mutex_unlock(&wdm_mutex); | 556 | mutex_unlock(&wdm_mutex); |
| @@ -556,9 +562,9 @@ static int wdm_release(struct inode *inode, struct file *file) | |||
| 556 | struct wdm_device *desc = file->private_data; | 562 | struct wdm_device *desc = file->private_data; |
| 557 | 563 | ||
| 558 | mutex_lock(&wdm_mutex); | 564 | mutex_lock(&wdm_mutex); |
| 559 | mutex_lock(&desc->plock); | 565 | mutex_lock(&desc->lock); |
| 560 | desc->count--; | 566 | desc->count--; |
| 561 | mutex_unlock(&desc->plock); | 567 | mutex_unlock(&desc->lock); |
| 562 | 568 | ||
| 563 | if (!desc->count) { | 569 | if (!desc->count) { |
| 564 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); | 570 | dev_dbg(&desc->intf->dev, "wdm_release: cleanup"); |
| @@ -655,9 +661,7 @@ next_desc: | |||
| 655 | desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); | 661 | desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); |
| 656 | if (!desc) | 662 | if (!desc) |
| 657 | goto out; | 663 | goto out; |
| 658 | mutex_init(&desc->wlock); | 664 | mutex_init(&desc->lock); |
| 659 | mutex_init(&desc->rlock); | ||
| 660 | mutex_init(&desc->plock); | ||
| 661 | spin_lock_init(&desc->iuspin); | 665 | spin_lock_init(&desc->iuspin); |
| 662 | init_waitqueue_head(&desc->wait); | 666 | init_waitqueue_head(&desc->wait); |
| 663 | desc->wMaxCommand = maxcom; | 667 | desc->wMaxCommand = maxcom; |
| @@ -772,7 +776,9 @@ static void wdm_disconnect(struct usb_interface *intf) | |||
| 772 | clear_bit(WDM_IN_USE, &desc->flags); | 776 | clear_bit(WDM_IN_USE, &desc->flags); |
| 773 | spin_unlock_irqrestore(&desc->iuspin, flags); | 777 | spin_unlock_irqrestore(&desc->iuspin, flags); |
| 774 | cancel_work_sync(&desc->rxwork); | 778 | cancel_work_sync(&desc->rxwork); |
| 779 | mutex_lock(&desc->lock); | ||
| 775 | kill_urbs(desc); | 780 | kill_urbs(desc); |
| 781 | mutex_unlock(&desc->lock); | ||
| 776 | wake_up_all(&desc->wait); | 782 | wake_up_all(&desc->wait); |
| 777 | if (!desc->count) | 783 | if (!desc->count) |
| 778 | cleanup(desc); | 784 | cleanup(desc); |
| @@ -786,7 +792,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | |||
| 786 | 792 | ||
| 787 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); | 793 | dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor); |
| 788 | 794 | ||
| 789 | mutex_lock(&desc->plock); | 795 | mutex_lock(&desc->lock); |
| 790 | #ifdef CONFIG_PM | 796 | #ifdef CONFIG_PM |
| 791 | if ((message.event & PM_EVENT_AUTO) && | 797 | if ((message.event & PM_EVENT_AUTO) && |
| 792 | test_bit(WDM_IN_USE, &desc->flags)) { | 798 | test_bit(WDM_IN_USE, &desc->flags)) { |
| @@ -798,7 +804,7 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message) | |||
| 798 | #ifdef CONFIG_PM | 804 | #ifdef CONFIG_PM |
| 799 | } | 805 | } |
| 800 | #endif | 806 | #endif |
| 801 | mutex_unlock(&desc->plock); | 807 | mutex_unlock(&desc->lock); |
| 802 | 808 | ||
| 803 | return rv; | 809 | return rv; |
| 804 | } | 810 | } |
| @@ -821,9 +827,9 @@ static int wdm_resume(struct usb_interface *intf) | |||
| 821 | int rv; | 827 | int rv; |
| 822 | 828 | ||
| 823 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); | 829 | dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor); |
| 824 | mutex_lock(&desc->plock); | 830 | mutex_lock(&desc->lock); |
| 825 | rv = recover_from_urb_loss(desc); | 831 | rv = recover_from_urb_loss(desc); |
| 826 | mutex_unlock(&desc->plock); | 832 | mutex_unlock(&desc->lock); |
| 827 | return rv; | 833 | return rv; |
| 828 | } | 834 | } |
| 829 | 835 | ||
| @@ -831,7 +837,7 @@ static int wdm_pre_reset(struct usb_interface *intf) | |||
| 831 | { | 837 | { |
| 832 | struct wdm_device *desc = usb_get_intfdata(intf); | 838 | struct wdm_device *desc = usb_get_intfdata(intf); |
| 833 | 839 | ||
| 834 | mutex_lock(&desc->plock); | 840 | mutex_lock(&desc->lock); |
| 835 | return 0; | 841 | return 0; |
| 836 | } | 842 | } |
| 837 | 843 | ||
| @@ -841,7 +847,7 @@ static int wdm_post_reset(struct usb_interface *intf) | |||
| 841 | int rv; | 847 | int rv; |
| 842 | 848 | ||
| 843 | rv = recover_from_urb_loss(desc); | 849 | rv = recover_from_urb_loss(desc); |
| 844 | mutex_unlock(&desc->plock); | 850 | mutex_unlock(&desc->lock); |
| 845 | return 0; | 851 | return 0; |
| 846 | } | 852 | } |
| 847 | 853 | ||
