diff options
author | Mayank Rana <mrana@codeaurora.org> | 2017-10-06 10:45:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-10-17 04:38:13 -0400 |
commit | b3207c65dfafae27e7c492cb9188c0dc0eeaf3fd (patch) | |
tree | e2e01323c0bd94fdb7df61652cd455611e7c971a /drivers | |
parent | 810a624bd1b64b13ddcc2eb5c1880526a750a870 (diff) |
usb: xhci: Handle error condition in xhci_stop_device()
xhci_stop_device() calls xhci_queue_stop_endpoint() multiple times
without checking the return value. xhci_queue_stop_endpoint() can
return error if the HC is already halted or unable to queue commands.
This can cause a deadlock condition as xhci_stop_device() would
end up waiting indefinitely for a completion for the command that
didn't get queued. Fix this by checking the return value and bailing
out of xhci_stop_device() in case of error. This patch happens to fix
potential memory leaks of the allocated command structures as well.
Fixes: c311e391a7ef ("xhci: rework command timeout and cancellation,")
Cc: <stable@vger.kernel.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index da9158f171cb..a2336deb5e36 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -420,14 +420,25 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) | |||
420 | GFP_NOWAIT); | 420 | GFP_NOWAIT); |
421 | if (!command) { | 421 | if (!command) { |
422 | spin_unlock_irqrestore(&xhci->lock, flags); | 422 | spin_unlock_irqrestore(&xhci->lock, flags); |
423 | xhci_free_command(xhci, cmd); | 423 | ret = -ENOMEM; |
424 | return -ENOMEM; | 424 | goto cmd_cleanup; |
425 | } | ||
426 | |||
427 | ret = xhci_queue_stop_endpoint(xhci, command, slot_id, | ||
428 | i, suspend); | ||
429 | if (ret) { | ||
430 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
431 | xhci_free_command(xhci, command); | ||
432 | goto cmd_cleanup; | ||
425 | } | 433 | } |
426 | xhci_queue_stop_endpoint(xhci, command, slot_id, i, | ||
427 | suspend); | ||
428 | } | 434 | } |
429 | } | 435 | } |
430 | xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend); | 436 | ret = xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend); |
437 | if (ret) { | ||
438 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
439 | goto cmd_cleanup; | ||
440 | } | ||
441 | |||
431 | xhci_ring_cmd_db(xhci); | 442 | xhci_ring_cmd_db(xhci); |
432 | spin_unlock_irqrestore(&xhci->lock, flags); | 443 | spin_unlock_irqrestore(&xhci->lock, flags); |
433 | 444 | ||
@@ -439,6 +450,8 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) | |||
439 | xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); | 450 | xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); |
440 | ret = -ETIME; | 451 | ret = -ETIME; |
441 | } | 452 | } |
453 | |||
454 | cmd_cleanup: | ||
442 | xhci_free_command(xhci, cmd); | 455 | xhci_free_command(xhci, cmd); |
443 | return ret; | 456 | return ret; |
444 | } | 457 | } |