Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deadlock in USB_DEVICE_Detach() #19

Open
js-nano opened this issue Jan 13, 2022 · 2 comments
Open

Deadlock in USB_DEVICE_Detach() #19

js-nano opened this issue Jan 13, 2022 · 2 comments

Comments

@js-nano
Copy link

js-nano commented Jan 13, 2022

There's a deadlock when calling USB_DEVICE_Detach()

Cause of the deadlock

If USB_DEVICE_Detach() is called from a non-interrupt context, a mutex is locked:

if(OSAL_MUTEX_Lock((OSAL_MUTEX_HANDLE_TYPE *)&hDriver->mutexID, OSAL_WAIT_FOREVER) == OSAL_RESULT_TRUE)

USB_DEVICE_Detach() then calls DRV_USBDP_EndpointDisable()...

DRV_USBDP_EndpointDisable((DRV_HANDLE)hDriver, DRV_USB_DEVICE_ENDPOINT_ALL);

... which then proceeds to try to lock the same mutex, with a timeout of OSAL_WAIT_FOREVER:

if(OSAL_MUTEX_Lock((OSAL_MUTEX_HANDLE_TYPE *)&hDriver->mutexID, OSAL_WAIT_FOREVER) == OSAL_RESULT_TRUE)

This sequence is guaranteed to cause a deadlock (assuming USB_DEVICE_Detach() is not called from an interrupt context).

Steps leading to the deadlock

I encountered this problem when trying to use both FreeRTOS and the default USB stack on a SAM G55 XPlained Pro board.

The docs say to call USB_DEVICE_Detach() in response to the USB_DEVICE_EVENT_POWER_REMOVED (1).

But the VBUS monitoring occurrs in DRV_USBDP_Tasks(), which is called in a task context (not an interrupt context) when using FreeRTOS. This means that when the USB_DEVICE_EVENT_POWER_REMOVED event is passed into the task event handler, the task is not running in an interrupt context

Why does the demo project work?

Calling USB_DEVICE_Detach() in the G55 cdc_com_port_single demo project works, because the OSAL implementation used is osal_implementation_basic.h. In this implementation, the mutex locking doesn't take the timeout into account, so the deadlock doesn't occur:
https://github.com/Microchip-MPLAB-Harmony/usb_apps_device/blob/e8089912b07a7c4eaec5d5ee81c6929badfc9a9f/apps/cdc_com_port_single/firmware/src/config/sam_g55_xpro/osal/osal_impl_basic.h#L274-L282

I've not got the hardware to test one of the FreeRTOS demo projects, so I can't say if they work

Thanks,
-Jon

@js-nano
Copy link
Author

js-nano commented Jan 19, 2022

Modifying osal_freertos.h so that all the OSAL_Mutex functions use a recursive mutex solves the problem, indicating that the problem is indeed caused by the mutex locking.

@otonoton
Copy link

otonoton commented Mar 8, 2024

Are you using polled usb mode instead of the default interrupt mode? I believe that is not recommended, but I agree this shouldn't happen either way. FWIW my event handler by default runs in an interrupt context and Detach() works fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants