Commit 967fec97 authored by hselasky's avatar hselasky
Browse files

Improve the realtime properties of USB transfers for embedded systems

like RPI-B and RPI-2.

Description of problem:
USB transfers can process data in their callbacks sometimes causing
unacceptable latency for other USB transfers. Separate BULK completion
callbacks from CONTROL, INTERRUPT and ISOCHRONOUS callbacks, and give
BULK completion callbacks lesser execution priority than the
others. This way USB audio won't be interfered by heavy USB ethernet
usage for example.

Further serve USB transfer completion in a round robin fashion,
instead of only serving the most CPU hungry. This has been done by
adding a third flag to USB transfer queue structure which keeps track
of looping callbacks. The "command" callback function then decides
what to do when looping.

MFC after:		2 weeks
parent 42db8f8b
......@@ -43,7 +43,8 @@
#define M_USBDEV 0
#define USB_PROC_MAX 3
#define USB_BUS_GIANT_PROC(bus) (usb_process + 2)
#define USB_BUS_NON_GIANT_PROC(bus) (usb_process + 2)
#define USB_BUS_NON_GIANT_BULK_PROC(bus) (usb_process + 2)
#define USB_BUS_NON_GIANT_ISOC_PROC(bus) (usb_process + 2)
#define USB_BUS_EXPLORE_PROC(bus) (usb_process + 0)
#define USB_BUS_CONTROL_XFER_PROC(bus) (usb_process + 1)
#define SYSCTL_DECL(...)
......
......@@ -231,7 +231,8 @@ usb_detach(device_t dev)
/* Get rid of USB callback processes */
usb_proc_free(USB_BUS_GIANT_PROC(bus));
usb_proc_free(USB_BUS_NON_GIANT_PROC(bus));
usb_proc_free(USB_BUS_NON_GIANT_ISOC_PROC(bus));
usb_proc_free(USB_BUS_NON_GIANT_BULK_PROC(bus));
/* Get rid of USB explore process */
......@@ -395,7 +396,8 @@ usb_bus_explore(struct usb_proc_msg *pm)
*/
usb_proc_rewakeup(USB_BUS_CONTROL_XFER_PROC(bus));
usb_proc_rewakeup(USB_BUS_GIANT_PROC(bus));
usb_proc_rewakeup(USB_BUS_NON_GIANT_PROC(bus));
usb_proc_rewakeup(USB_BUS_NON_GIANT_ISOC_PROC(bus));
usb_proc_rewakeup(USB_BUS_NON_GIANT_BULK_PROC(bus));
#endif
USB_BUS_UNLOCK(bus);
......@@ -860,9 +862,13 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
&bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) {
device_printf(dev, "WARNING: Creation of USB Giant "
"callback process failed.\n");
} else if (usb_proc_create(USB_BUS_NON_GIANT_PROC(bus),
} else if (usb_proc_create(USB_BUS_NON_GIANT_ISOC_PROC(bus),
&bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGHEST)) {
device_printf(dev, "WARNING: Creation of USB non-Giant ISOC "
"callback process failed.\n");
} else if (usb_proc_create(USB_BUS_NON_GIANT_BULK_PROC(bus),
&bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGH)) {
device_printf(dev, "WARNING: Creation of USB non-Giant "
device_printf(dev, "WARNING: Creation of USB non-Giant BULK "
"callback process failed.\n");
} else if (usb_proc_create(USB_BUS_EXPLORE_PROC(bus),
&bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) {
......
......@@ -57,19 +57,26 @@ struct usb_bus {
struct root_hold_token *bus_roothold;
#endif
/* convenience macros */
#define USB_BUS_TT_PROC(bus) USB_BUS_NON_GIANT_ISOC_PROC(bus)
#define USB_BUS_CS_PROC(bus) USB_BUS_NON_GIANT_ISOC_PROC(bus)
#if USB_HAVE_PER_BUS_PROCESS
#define USB_BUS_GIANT_PROC(bus) (&(bus)->giant_callback_proc)
#define USB_BUS_NON_GIANT_PROC(bus) (&(bus)->non_giant_callback_proc)
#define USB_BUS_NON_GIANT_ISOC_PROC(bus) (&(bus)->non_giant_isoc_callback_proc)
#define USB_BUS_NON_GIANT_BULK_PROC(bus) (&(bus)->non_giant_bulk_callback_proc)
#define USB_BUS_EXPLORE_PROC(bus) (&(bus)->explore_proc)
#define USB_BUS_CONTROL_XFER_PROC(bus) (&(bus)->control_xfer_proc)
/*
* There are two callback processes. One for Giant locked
* callbacks. One for non-Giant locked callbacks. This should
* avoid congestion and reduce response time in most cases.
* There are three callback processes. One for Giant locked
* callbacks. One for non-Giant locked non-periodic callbacks
* and one for non-Giant locked periodic callbacks. This
* should avoid congestion and reduce response time in most
* cases.
*/
struct usb_process giant_callback_proc;
struct usb_process non_giant_callback_proc;
struct usb_process non_giant_isoc_callback_proc;
struct usb_process non_giant_bulk_callback_proc;
/* Explore process */
struct usb_process explore_proc;
......
......@@ -2181,7 +2181,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
* anywhere:
*/
USB_BUS_LOCK(udev->bus);
usb_proc_mwait(USB_BUS_NON_GIANT_PROC(udev->bus),
usb_proc_mwait(USB_BUS_CS_PROC(udev->bus),
&udev->cs_msg[0], &udev->cs_msg[1]);
USB_BUS_UNLOCK(udev->bus);
......
......@@ -346,7 +346,7 @@ uhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint
}
up->req_reset_tt = req;
/* get reset transfer started */
usb_proc_msignal(USB_BUS_NON_GIANT_PROC(udev->bus),
usb_proc_msignal(USB_BUS_TT_PROC(udev->bus),
&hub->tt_msg[0], &hub->tt_msg[1]);
}
#endif
......@@ -1579,7 +1579,7 @@ uhub_detach(device_t dev)
#if USB_HAVE_TT_SUPPORT
/* Make sure our TT messages are not queued anywhere */
USB_BUS_LOCK(bus);
usb_proc_mwait(USB_BUS_NON_GIANT_PROC(bus),
usb_proc_mwait(USB_BUS_TT_PROC(bus),
&hub->tt_msg[0], &hub->tt_msg[1]);
USB_BUS_UNLOCK(bus);
#endif
......
......@@ -34,6 +34,7 @@
#endif
/* defines */
#define USB_PRI_HIGHEST PI_SWI(SWI_TTY)
#define USB_PRI_HIGH PI_SWI(SWI_NET)
#define USB_PRI_MED PI_SWI(SWI_CAMBIO)
......
......@@ -872,6 +872,19 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
}
}
static uint8_t
usbd_transfer_setup_has_bulk(const struct usb_config *setup_start,
uint16_t n_setup)
{
while (n_setup--) {
uint8_t type = setup_start[n_setup].type;
if (type == UE_BULK || type == UE_BULK_INTR ||
type == UE_TYPE_ANY)
return (1);
}
return (0);
}
/*------------------------------------------------------------------------*
* usbd_transfer_setup - setup an array of USB transfers
*
......@@ -1013,9 +1026,12 @@ usbd_transfer_setup(struct usb_device *udev,
else if (xfer_mtx == &Giant)
info->done_p =
USB_BUS_GIANT_PROC(udev->bus);
else if (usbd_transfer_setup_has_bulk(setup_start, n_setup))
info->done_p =
USB_BUS_NON_GIANT_BULK_PROC(udev->bus);
else
info->done_p =
USB_BUS_NON_GIANT_PROC(udev->bus);
USB_BUS_NON_GIANT_ISOC_PROC(udev->bus);
}
/* reset sizes */
......@@ -2280,10 +2296,8 @@ usbd_callback_ss_done_defer(struct usb_xfer *xfer)
* will have a Lock Order Reversal, LOR, if we try to
* proceed !
*/
if (usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1])) {
/* ignore */
}
(void) usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1]);
} else {
/* clear second recurse flag */
pq->recurse_2 = 0;
......@@ -2307,23 +2321,26 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq)
struct usb_xfer_root *info = xfer->xroot;
USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
if (!mtx_owned(info->xfer_mtx) && !SCHEDULER_STOPPED()) {
if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) &&
SCHEDULER_STOPPED() == 0) {
/*
* Cases that end up here:
*
* 5) HW interrupt done callback or other source.
* 6) HW completed transfer during callback
*/
DPRINTFN(3, "case 5\n");
DPRINTFN(3, "case 5 and 6\n");
/*
* We have to postpone the callback due to the fact we
* will have a Lock Order Reversal, LOR, if we try to
* proceed !
* proceed!
*
* Postponing the callback also ensures that other USB
* transfer queues get a chance.
*/
if (usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1])) {
/* ignore */
}
(void) usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1]);
return;
}
/*
......@@ -2694,7 +2711,7 @@ usbd_pipe_start(struct usb_xfer_queue *pq)
} else if (udev->ctrl_xfer[1]) {
info = udev->ctrl_xfer[1]->xroot;
usb_proc_msignal(
USB_BUS_NON_GIANT_PROC(info->bus),
USB_BUS_CS_PROC(info->bus),
&udev->cs_msg[0], &udev->cs_msg[1]);
} else {
/* should not happen */
......@@ -3019,9 +3036,11 @@ usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer)
if (!pq->recurse_1) {
do {
/* clear third recurse flag */
pq->recurse_3 = 0;
/* set both recurse flags */
do {
/* set two first recurse flags */
pq->recurse_1 = 1;
pq->recurse_2 = 1;
......@@ -3040,6 +3059,12 @@ usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer)
(pq->command) (pq);
DPRINTFN(6, "cb %p (leave)\n", pq->curr);
/*
* Set third recurse flag to indicate
* recursion happened:
*/
pq->recurse_3 = 1;
} while (!pq->recurse_2);
/* clear first recurse flag */
......@@ -3315,7 +3340,8 @@ usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max)
USB_BUS_CONTROL_XFER_PROC(udev->bus)->up_msleep = 0;
USB_BUS_EXPLORE_PROC(udev->bus)->up_msleep = 0;
USB_BUS_GIANT_PROC(udev->bus)->up_msleep = 0;
USB_BUS_NON_GIANT_PROC(udev->bus)->up_msleep = 0;
USB_BUS_NON_GIANT_ISOC_PROC(udev->bus)->up_msleep = 0;
USB_BUS_NON_GIANT_BULK_PROC(udev->bus)->up_msleep = 0;
/* poll USB hardware */
(udev->bus->methods->xfer_poll) (udev->bus);
......
......@@ -128,6 +128,8 @@ struct usb_xfer_queue {
void (*command) (struct usb_xfer_queue *pq);
uint8_t recurse_1:1;
uint8_t recurse_2:1;
uint8_t recurse_3:1;
uint8_t reserved:5;
};
/*
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment