arch/arm/sama5: Use a recursive mutex to fix OHCI deadlock.
Unplugging a USB device from an OHCI root hub will cause a deadlock if DRVR_EPFREE is called from sam_rhsc_bottomhalf. A typical call chain looks like this: sam_rhsc_bottomhalf-> CLASS_DISCONNECTED->usbhost_destroy->DRVR_EPFREE. In this case DRVR_EPFREE tries to lock a locked mutex. A recursive mutex prevents this deadlock.
This commit is contained in:
parent
79574c18d8
commit
e2083354f1
@ -256,7 +256,7 @@ struct sam_ohci_s
|
||||
uint8_t outinterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */
|
||||
#endif
|
||||
|
||||
mutex_t lock; /* Support mutually exclusive access */
|
||||
rmutex_t lock; /* Support mutually exclusive access */
|
||||
sem_t pscsem; /* Semaphore to wait Writeback Done Head event */
|
||||
struct work_s work; /* Supports interrupt bottom half */
|
||||
|
||||
@ -461,7 +461,7 @@ static void sam_disconnect(struct usbhost_driver_s *drvr,
|
||||
|
||||
static struct sam_ohci_s g_ohci =
|
||||
{
|
||||
.lock = NXMUTEX_INITIALIZER,
|
||||
.lock = NXRMUTEX_INITIALIZER,
|
||||
.pscsem = SEM_INITIALIZER(0),
|
||||
};
|
||||
|
||||
@ -1871,7 +1871,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport,
|
||||
* wakes this thread up needs the lock).
|
||||
*/
|
||||
#warning REVISIT
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
|
||||
/* Wait for the Writeback Done Head interrupt Loop to handle any false
|
||||
* alarm semaphore counts.
|
||||
@ -1886,7 +1886,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport,
|
||||
* this upon return.
|
||||
*/
|
||||
|
||||
ret2 = nxmutex_lock(&g_ohci.lock);
|
||||
ret2 = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret2 < 0)
|
||||
{
|
||||
ret = ret2;
|
||||
@ -2242,7 +2242,7 @@ static void sam_ohci_bottomhalf(void *arg)
|
||||
* real option (other than to reschedule and delay).
|
||||
*/
|
||||
|
||||
nxmutex_lock(&g_ohci.lock);
|
||||
nxrmutex_lock(&g_ohci.lock);
|
||||
|
||||
/* Root hub status change interrupt */
|
||||
|
||||
@ -2295,7 +2295,7 @@ static void sam_ohci_bottomhalf(void *arg)
|
||||
/* Now re-enable interrupts */
|
||||
|
||||
sam_putreg(OHCI_INT_MIE, SAM_USBHOST_INTEN);
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -2591,7 +2591,7 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
|
||||
/* We must have exclusive access to EP0 and the control list */
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@ -2614,7 +2614,7 @@ static int sam_ep0configure(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
|
||||
up_clean_dcache((uintptr_t)edctrl,
|
||||
(uintptr_t)edctrl + sizeof(struct ohci_ed_s));
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
|
||||
usbhost_vtrace2(OHCI_VTRACE2_EP0CTRLED, RHPORT(rhport),
|
||||
(uint16_t)edctrl->hw.ctrl);
|
||||
@ -2683,7 +2683,7 @@ static int sam_epalloc(struct usbhost_driver_s *drvr,
|
||||
* periodic list, and the interrupt table.
|
||||
*/
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_eplist;
|
||||
@ -2814,7 +2814,7 @@ static int sam_epalloc(struct usbhost_driver_s *drvr,
|
||||
/* Success.. return an opaque reference to the endpoint list container */
|
||||
|
||||
*ep = (usbhost_ep_t)eplist;
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return OK;
|
||||
|
||||
errout_with_td:
|
||||
@ -2822,7 +2822,7 @@ errout_with_td:
|
||||
errout_with_ed:
|
||||
sam_edfree(ed);
|
||||
errout_with_lock:
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
errout_with_eplist:
|
||||
kmm_free(eplist);
|
||||
errout:
|
||||
@ -2869,7 +2869,7 @@ static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
||||
* periodic list and the interrupt table.
|
||||
*/
|
||||
|
||||
ret2 = nxmutex_lock(&g_ohci.lock);
|
||||
ret2 = nxrmutex_lock(&g_ohci.lock);
|
||||
|
||||
/* Remove the ED to the correct list depending on the transfer type */
|
||||
|
||||
@ -2905,7 +2905,7 @@ static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
||||
|
||||
nxsem_destroy(&eplist->wdhsem);
|
||||
kmm_free(eplist);
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return ret < 0 ? ret : ret2;
|
||||
}
|
||||
|
||||
@ -2951,7 +2951,7 @@ static int sam_alloc(struct usbhost_driver_s *drvr,
|
||||
|
||||
/* We must have exclusive access to the transfer buffer pool */
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@ -2966,7 +2966,7 @@ static int sam_alloc(struct usbhost_driver_s *drvr,
|
||||
ret = OK;
|
||||
}
|
||||
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3002,9 +3002,9 @@ static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer)
|
||||
|
||||
/* We must have exclusive access to the transfer buffer pool */
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
sam_tbfree(buffer);
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3140,7 +3140,7 @@ static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
|
||||
/* We must have exclusive access to EP0 and the control list */
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@ -3166,7 +3166,7 @@ static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
* it to be reloaded from RAM after the DMA.
|
||||
*/
|
||||
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + len);
|
||||
return ret;
|
||||
}
|
||||
@ -3194,7 +3194,7 @@ static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
|
||||
/* We must have exclusive access to EP0 and the control list */
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@ -3217,7 +3217,7 @@ static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
|
||||
}
|
||||
}
|
||||
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -3366,7 +3366,7 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
* table.
|
||||
*/
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return (ssize_t)ret;
|
||||
@ -3403,7 +3403,7 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
*/
|
||||
|
||||
#warning REVISIT
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
|
||||
/* Wait for the Writeback Done Head interrupt Loop to handle any false
|
||||
* alarm semaphore counts.
|
||||
@ -3418,7 +3418,7 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
* this upon return.
|
||||
*/
|
||||
|
||||
ret2 = nxmutex_lock(&g_ohci.lock);
|
||||
ret2 = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret2 < 0)
|
||||
{
|
||||
ret = ret2;
|
||||
@ -3451,7 +3451,7 @@ static ssize_t sam_transfer(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
nbytes = eplist->xfrd;
|
||||
DEBUGASSERT(nbytes >= 0 && nbytes <= buflen);
|
||||
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
@ -3478,7 +3478,7 @@ errout:
|
||||
/* Make sure that there is no outstanding request on this endpoint */
|
||||
|
||||
eplist->wdhwait = false;
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return (ssize_t)ret;
|
||||
}
|
||||
|
||||
@ -3634,7 +3634,7 @@ static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
* table.
|
||||
*/
|
||||
|
||||
ret = nxmutex_lock(&g_ohci.lock);
|
||||
ret = nxrmutex_lock(&g_ohci.lock);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@ -3664,7 +3664,7 @@ static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
||||
* when the transfer completes.
|
||||
*/
|
||||
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return OK;
|
||||
|
||||
errout:
|
||||
@ -3673,7 +3673,7 @@ errout:
|
||||
|
||||
eplist->callback = NULL;
|
||||
eplist->arg = NULL;
|
||||
nxmutex_unlock(&g_ohci.lock);
|
||||
nxrmutex_unlock(&g_ohci.lock);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USBHOST_ASYNCH */
|
||||
|
Loading…
Reference in New Issue
Block a user