diff options
author | Radoslav Gerganov <rgerganov@vmware.com> | 2019-03-05 05:10:34 -0500 |
---|---|---|
committer | Felipe Balbi <felipe.balbi@linux.intel.com> | 2019-03-20 04:58:48 -0400 |
commit | 072684e8c58d17e853f8e8b9f6d9ce2e58d2b036 (patch) | |
tree | 829c8ca42b08f53db0733dcb6abe376dc8ef576f | |
parent | 091dacc3cc10979ab0422f0a9f7fcc27eee97e69 (diff) |
USB: gadget: f_hid: fix deadlock in f_hidg_write()
In f_hidg_write() the write_spinlock is acquired before calling
usb_ep_queue() which causes a deadlock when dummy_hcd is being used.
This is because dummy_queue() callbacks into f_hidg_req_complete() which
tries to acquire the same spinlock. This is (part of) the backtrace when
the deadlock occurs:
0xffffffffc06b1410 in f_hidg_req_complete
0xffffffffc06a590a in usb_gadget_giveback_request
0xffffffffc06cfff2 in dummy_queue
0xffffffffc06a4b96 in usb_ep_queue
0xffffffffc06b1eb6 in f_hidg_write
0xffffffff8127730b in __vfs_write
0xffffffff812774d1 in vfs_write
0xffffffff81277725 in SYSC_write
Fix this by releasing the write_spinlock before calling usb_ep_queue()
Reviewed-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Tested-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Cc: stable@vger.kernel.org # 4.11+
Fixes: 749494b6bdbb ("usb: gadget: f_hid: fix: Move IN request allocation to set_alt()")
Signed-off-by: Radoslav Gerganov <rgerganov@vmware.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r-- | drivers/usb/gadget/function/f_hid.c | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 75b113a5b25c..f3816a5c861e 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c | |||
@@ -391,20 +391,20 @@ try_again: | |||
391 | req->complete = f_hidg_req_complete; | 391 | req->complete = f_hidg_req_complete; |
392 | req->context = hidg; | 392 | req->context = hidg; |
393 | 393 | ||
394 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | ||
395 | |||
394 | status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); | 396 | status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); |
395 | if (status < 0) { | 397 | if (status < 0) { |
396 | ERROR(hidg->func.config->cdev, | 398 | ERROR(hidg->func.config->cdev, |
397 | "usb_ep_queue error on int endpoint %zd\n", status); | 399 | "usb_ep_queue error on int endpoint %zd\n", status); |
398 | goto release_write_pending_unlocked; | 400 | goto release_write_pending; |
399 | } else { | 401 | } else { |
400 | status = count; | 402 | status = count; |
401 | } | 403 | } |
402 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | ||
403 | 404 | ||
404 | return status; | 405 | return status; |
405 | release_write_pending: | 406 | release_write_pending: |
406 | spin_lock_irqsave(&hidg->write_spinlock, flags); | 407 | spin_lock_irqsave(&hidg->write_spinlock, flags); |
407 | release_write_pending_unlocked: | ||
408 | hidg->write_pending = 0; | 408 | hidg->write_pending = 0; |
409 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | 409 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); |
410 | 410 | ||