diff options
author | Ruslan Bilovol <ruslan.bilovol@ti.com> | 2012-06-14 17:12:15 +0300 |
---|---|---|
committer | Ruslan Bilovol <ruslan.bilovol@ti.com> | 2012-08-30 14:37:33 +0300 |
commit | a4336ccb54a0f228f0b5676096d8c0da324072b0 (patch) | |
tree | 85e0e9aec655701df0173ed24396e5bbfdd3a39d | |
parent | 547b66d0802bb9dfac8f933bd9c00dda8dafb2eb (diff) | |
download | kernel_samsung_espresso10-a4336ccb54a0f228f0b5676096d8c0da324072b0.zip kernel_samsung_espresso10-a4336ccb54a0f228f0b5676096d8c0da324072b0.tar.gz kernel_samsung_espresso10-a4336ccb54a0f228f0b5676096d8c0da324072b0.tar.bz2 |
USB: musb: do not proceed with interrupt completions if qh is not ready
It's not safe to modify hw_ep if qh is not ready.
This may corrupt data in musb_qh. In RX/TX completions this
may happen on SMP system with current musb host implementation
The main issue is with musb_giveback function. It unlocks and
and after that locks musb->lock so during this time
musb_qh data may be corrupted. This results in null-pointers
dereferences / programming of busy DMA channel / etc in
different places.
This often happens during unlinking URB for ISO and looks like:
CPU0 CPU1
... musb_urb_dequeue
... spin_lock_irqsave(&musb->lock)
... (work with musb_qh)
... musb_giveback
... spin_unlock(&musb->lock)
generic_interrupt() ...
spin_lock_irqsave(&musb->lock) ...
(work with musb_qh) ...
spin_unlock_irqrestore(&musb->lock) ...
... spin_lock(&musb->lock)
... (continues work with musb_qh)
... spin_lock_irqrestore(&musb->lock)
Go out from completion if qh is not ready.
Change-Id: I9c9280a7ab7f8e89b2e8bf745800217f903108fc
Signed-off-by: Ruslan Bilovol <ruslan.bilovol@ti.com>
-rw-r--r-- | drivers/usb/musb/musb_host.c | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8a08dd3..329182e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1113,6 +1113,11 @@ void musb_host_tx(struct musb *musb, u8 epnum) return; } + if (!qh->is_ready) { + dev_dbg(musb->controller, "received TX%d completion when qh is not ready\n", epnum); + return; + } + pipe = urb->pipe; dma = is_dma_capable() ? hw_ep->tx_channel : NULL; dev_dbg(musb->controller, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, @@ -1451,6 +1456,12 @@ void musb_host_rx(struct musb *musb, u8 epnum) return; } + if (!qh->is_ready) { + dev_dbg(musb->controller, "received RX%d completion when qh is not ready\n", epnum); + musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); + return; + } + pipe = urb->pipe; dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", |