mirror of
https://github.com/apache/nuttx.git
synced 2026-06-07 01:05:54 +08:00
USB HUB: Fix a deadlock that can occur if the HCD and the HUB try to share the high priority work queue. Now how work must be done on the low priority work queue.
This commit is contained in:
@@ -73,8 +73,10 @@
|
||||
/* Configuration ***************************************************************/
|
||||
/* Pre-requisites */
|
||||
|
||||
#ifndef CONFIG_SCHED_WORKQUEUE
|
||||
#if !defined(CONFIG_SCHED_WORKQUEUE)
|
||||
# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
|
||||
#elif !defined(CONFIG_SCHED_HPWORK)
|
||||
# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK)
|
||||
#endif
|
||||
|
||||
/* Configurable number of Queue Head (QH) structures. The default is one per
|
||||
@@ -2570,7 +2572,7 @@ static inline int lpc31_asynch_setup(struct lpc31_rhport_s *rhport,
|
||||
int ret = -ENODEV;
|
||||
|
||||
DEBUGASSERT(rhport && epinfo && !epinfo->iocwait &&
|
||||
epinfo->callback == NULL);
|
||||
callback != NULL && epinfo->callback == NULL);
|
||||
|
||||
/* Is the device still connected? */
|
||||
|
||||
|
||||
@@ -73,8 +73,10 @@
|
||||
/* Configuration ***************************************************************/
|
||||
/* Pre-requisites */
|
||||
|
||||
#ifndef CONFIG_SCHED_WORKQUEUE
|
||||
#if !defined(CONFIG_SCHED_WORKQUEUE)
|
||||
# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
|
||||
#elif !defined(CONFIG_SCHED_HPWORK)
|
||||
# error Hi-priority work queue support is required (CONFIG_SCHED_HPWORK)
|
||||
#endif
|
||||
|
||||
/* Configurable number of Queue Head (QH) structures. The default is one per
|
||||
|
||||
@@ -668,8 +668,9 @@ Configurations
|
||||
CONFIG_LPC31_EHCI_PREALLOCATE=y
|
||||
|
||||
RTOS Features -> Work Queue Support
|
||||
CONFIG_SCHED_WORKQUEUE=y : Work queue support is needed
|
||||
CONFIG_SCHED_HPWORKSTACKSIZE=1536
|
||||
CONFIG_SCHED_WORKQUEUE=y : High priority queue support is needed
|
||||
CONFIG_SCHED_HPWORK=y
|
||||
CONFIG_SCHED_HPWORKSTACKSIZE=1536 (1024 seems to work okay too)
|
||||
|
||||
b. Hub Support.
|
||||
|
||||
@@ -678,11 +679,26 @@ Configurations
|
||||
CONFIG_USBHOST_HUB=y : Enable the hub class
|
||||
CONFIG_USBHOST_ASYNCH=y : Asynchronous I/O supported needed for hubs
|
||||
|
||||
System Type -> USB host configuration
|
||||
To be provided
|
||||
RTOS Features -> Work Queue Support
|
||||
CONFIG_SCHED_LPWORK=y : Low priority queue support is needed
|
||||
CONFIG_SCHED_LPNTHREADS=1
|
||||
CONFIG_SCHED_LPWORKSTACKSIZE=1024
|
||||
|
||||
Logic nesting becomes deeper with a hub and it may also be
|
||||
necessary to increase some stack sizes.
|
||||
NOTES:
|
||||
|
||||
1. It is necessary to perform work on the low-priority work queue
|
||||
(vs. the high priority work queue) because:
|
||||
|
||||
a. Deferred work requires some delays and waiting, and
|
||||
b. There are dependencies between the waiting and driver
|
||||
interrupt related work. Since that interrupt related work
|
||||
will performed on the high priority work queue, there would
|
||||
be the likelihood of deadlocks if you wait for events on the
|
||||
high priority work thread that can only occur if the high
|
||||
priority work thread is available to post those events.
|
||||
|
||||
2. Logic nesting becomes deeper with a hub and it may also be
|
||||
necessary to increase some stack sizes.
|
||||
|
||||
c. USB Mass Storage Class. With this class enabled, you can support
|
||||
connection of USB FLASH storage drives. Support for the USB
|
||||
|
||||
@@ -955,20 +955,36 @@ Configuration Sub-Directories
|
||||
2. I used this configuration to test the USB hub class. I did this
|
||||
testing with the following changes to the configuration:
|
||||
|
||||
CONFIG_USBHOST_HUB=y : Enable the hub class
|
||||
CONFIG_USBHOST_ASYNCH=y : Asynchonous I/O supported needed for hubs
|
||||
Drivers -> USB Host Driver Support
|
||||
CONFIG_USBHOST_HUB=y : Enable the hub class
|
||||
CONFIG_USBHOST_ASYNCH=y : Asynchonous I/O supported needed for hubs
|
||||
|
||||
CONFIG_LPC17_USBHOST_NASYNCH=8 : Allow up to 8 asynchronous requests
|
||||
CONFIG_USBHOST_NEDS=3 : Increase number of endpoint descriptors from 2
|
||||
CONFIG_USBHOST_NTDS=4 : Increase number of transfer descriptors from 3
|
||||
CONFIG_USBHOST_TDBUFFERS=4 : Increase number of transfer buffers from 3
|
||||
CONFIG_USBHOST_IOBUFSIZE=256 : Decrease the size of I/O buffers from 512
|
||||
System Type -> USB host configuration
|
||||
CONFIG_LPC17_USBHOST_NASYNCH=8 : Allow up to 8 asynchronous requests
|
||||
CONFIG_USBHOST_NEDS=3 : Increase number of endpoint descriptors from 2
|
||||
CONFIG_USBHOST_NTDS=4 : Increase number of transfer descriptors from 3
|
||||
CONFIG_USBHOST_TDBUFFERS=4 : Increase number of transfer buffers from 3
|
||||
CONFIG_USBHOST_IOBUFSIZE=256 : Decrease the size of I/O buffers from 512
|
||||
|
||||
I also increased some stack sizes:
|
||||
RTOS Features -> Work Queue Support
|
||||
CONFIG_SCHED_LPWORK=y : Low priority queue support is needed
|
||||
CONFIG_SCHED_LPNTHREADS=1
|
||||
CONFIG_SCHED_LPWORKSTACKSIZE=1024
|
||||
|
||||
CONFIG_EXAMPLES_HIDKBD_STACKSIZE=2048 : Was 1024
|
||||
CONFIG_HIDKBD_STACKSIZE=2048 : Was 1024
|
||||
CONFIG_SCHED_HPWORKSTACKSIZE=2048 : Was 1024
|
||||
NOTES:
|
||||
|
||||
1. It is necessary to perform work on the low-priority work queue
|
||||
(vs. the high priority work queue) because deferred hub-related
|
||||
work requires some delays and waiting that is not appropriate on
|
||||
the high priority work queue.
|
||||
|
||||
2. I also increased some stack sizes. These values are not tuned.
|
||||
When I ran into stack size issues, I just increased the size of
|
||||
all threads that had smaller stacks.
|
||||
|
||||
CONFIG_EXAMPLES_HIDKBD_STACKSIZE=2048 : Was 1024
|
||||
CONFIG_HIDKBD_STACKSIZE=2048 : Was 1024
|
||||
CONFIG_SCHED_HPWORKSTACKSIZE=2048 : Was 1024 (1024 is probably ok)
|
||||
|
||||
STATUS:
|
||||
2015-04-26: The hub basically works. I do get crashes in the LPC16 USB host driver
|
||||
|
||||
@@ -65,29 +65,29 @@
|
||||
****************************************************************************/
|
||||
|
||||
/* Configuration ************************************************************/
|
||||
/* It is necessary to perform work on the low-priority work queue (vs. the
|
||||
* high priority work queue) because:
|
||||
*
|
||||
* 1. Deferred work requires some delays and waiting, and
|
||||
* 2. There may be dependencies between the waiting and driver interrupt
|
||||
* related work. Since that interrupt related work will performed on the
|
||||
* high priority work queue, there would be the likelihood of deadlocks
|
||||
* if you wait for events on the high priority work thread that can only
|
||||
* occur if the high priority work thread is available to post those events.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_SCHED_WORKQUEUE
|
||||
# warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
|
||||
#if !defined(CONFIG_SCHED_WORKQUEUE)
|
||||
# error Work queue support is required (CONFIG_SCHED_WORKQUEUE)
|
||||
#elif !defined(CONFIG_SCHED_LPWORK)
|
||||
# error Low-priority work queue support is required (CONFIG_SCHED_LPWORK)
|
||||
#endif
|
||||
|
||||
/* Perform polling actions on the low priority work queue, if configured */
|
||||
|
||||
#ifndef CONFIG_SCHED_LPWORK
|
||||
# define POLL_WORK LPWORK
|
||||
#else
|
||||
# define POLL_WORK HPWORK
|
||||
#endif
|
||||
/* Perform polling actions with a delay on the low priority work queue, if
|
||||
* configured
|
||||
*/
|
||||
|
||||
#define POLL_DELAY MSEC2TICK(400)
|
||||
|
||||
/* Perform event processing on the high priority work queue, if configured */
|
||||
|
||||
#ifndef CONFIG_SCHED_HPWORK
|
||||
# define EVENT_WORK HPWORK
|
||||
#else
|
||||
# define EVENT_WORK LPWORK
|
||||
#endif
|
||||
|
||||
/* Used in usbhost_cfgdesc() */
|
||||
|
||||
#define USBHOST_IFFOUND 0x01 /* Required I/F descriptor found */
|
||||
@@ -1013,10 +1013,7 @@ static void usbhost_disconnect_event(FAR void *arg)
|
||||
|
||||
/* Cancel any pending port status change events */
|
||||
|
||||
work_cancel(EVENT_WORK, &priv->work);
|
||||
#if EVENT_WORK != POLL_WORK
|
||||
work_cancel(POLL_WORK, &priv->work);
|
||||
#endif
|
||||
work_cancel(LPWORK, &priv->work);
|
||||
|
||||
/* Disable power to all downstream ports */
|
||||
|
||||
@@ -1130,7 +1127,6 @@ static void usbhost_callback(FAR void *arg, int result)
|
||||
FAR struct usbhost_class_s *hubclass;
|
||||
FAR struct usbhost_hubpriv_s *priv;
|
||||
uint32_t delay = 0;
|
||||
int qid = EVENT_WORK;
|
||||
|
||||
DEBUGASSERT(arg != NULL);
|
||||
hubclass = (FAR struct usbhost_class_s *)arg;
|
||||
@@ -1157,7 +1153,6 @@ static void usbhost_callback(FAR void *arg, int result)
|
||||
* case.
|
||||
*/
|
||||
|
||||
qid = POLL_WORK;
|
||||
delay = POLL_DELAY;
|
||||
}
|
||||
|
||||
@@ -1168,7 +1163,7 @@ static void usbhost_callback(FAR void *arg, int result)
|
||||
|
||||
if (work_available(&priv->work))
|
||||
{
|
||||
(void)work_queue(qid, &priv->work, (worker_t)usbhost_hub_event,
|
||||
(void)work_queue(LPWORK, &priv->work, (worker_t)usbhost_hub_event,
|
||||
hubclass, delay);
|
||||
}
|
||||
}
|
||||
@@ -1419,7 +1414,7 @@ static int usbhost_disconnected(struct usbhost_class_s *hubclass)
|
||||
*/
|
||||
|
||||
flags = irqsave();
|
||||
ret = work_queue(EVENT_WORK, &priv->work,
|
||||
ret = work_queue(LPWORK, &priv->work,
|
||||
(worker_t)usbhost_disconnect_event, hubclass, 0);
|
||||
irqrestore(flags);
|
||||
return ret;
|
||||
|
||||
Reference in New Issue
Block a user