Skip to content

Commit c4564d4

Browse files
P33MP33M
P33M
authored and
P33M
committed
dwc_otg: implement tasklet for returning URBs to usbcore hcd layer
The dwc_otg driver interrupt handler for transfer completion will spend a very long time with interrupts disabled when a URB is completed - this is because usb_hcd_giveback_urb is called from within the handler which for a USB device driver with complicated processing (e.g. webcam) will take an exorbitant amount of time to complete. This results in missed completion interrupts for other USB packets which lead to them being dropped due to microframe overruns. This patch splits returning the URB to the usb hcd layer into a high-priority tasklet. This will have most benefit for isochronous IN transfers but will also have incidental benefit where multiple periodic devices are active at once.
1 parent e959a8e commit c4564d4

File tree

6 files changed

+73
-17
lines changed

6 files changed

+73
-17
lines changed

drivers/usb/host/dwc_common_port/dwc_common_linux.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task)
991991
tasklet_schedule(&task->t);
992992
}
993993

994+
void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task)
995+
{
996+
tasklet_hi_schedule(&task->t);
997+
}
998+
994999

9951000
/* workqueues
9961001
- run in process context (can sleep)

drivers/usb/host/dwc_common_port/dwc_list.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,17 @@ struct { \
384384
#define DWC_TAILQ_PREV(elm, headname, field) \
385385
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
386386
#define DWC_TAILQ_EMPTY(head) \
387-
(TAILQ_FIRST(head) == TAILQ_END(head))
387+
(DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head))
388388

389389
#define DWC_TAILQ_FOREACH(var, head, field) \
390-
for((var) = TAILQ_FIRST(head); \
391-
(var) != TAILQ_END(head); \
392-
(var) = TAILQ_NEXT(var, field))
390+
for ((var) = DWC_TAILQ_FIRST(head); \
391+
(var) != DWC_TAILQ_END(head); \
392+
(var) = DWC_TAILQ_NEXT(var, field))
393393

394394
#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
395-
for((var) = TAILQ_LAST(head, headname); \
396-
(var) != TAILQ_END(head); \
397-
(var) = TAILQ_PREV(var, headname, field))
395+
for ((var) = DWC_TAILQ_LAST(head, headname); \
396+
(var) != DWC_TAILQ_END(head); \
397+
(var) = DWC_TAILQ_PREV(var, headname, field))
398398

399399
/*
400400
* Tail queue functions.

drivers/usb/host/dwc_common_port/dwc_os.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task);
981981
extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task);
982982
#define dwc_task_schedule DWC_TASK_SCHEDULE
983983

984+
extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task);
985+
#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE
984986

985987
/** @name Timer
986988
*

drivers/usb/host/dwc_otg/dwc_otg_hcd.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
* header file.
4141
*/
4242

43+
#include <linux/usb.h>
44+
#include <linux/usb/hcd.h>
45+
4346
#include "dwc_otg_hcd.h"
4447
#include "dwc_otg_regs.h"
4548

@@ -694,6 +697,31 @@ static void reset_tasklet_func(void *data)
694697
dwc_otg_hcd->flags.b.port_reset_change = 1;
695698
}
696699

700+
static void completion_tasklet_func(void *ptr)
701+
{
702+
dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr;
703+
struct urb *urb;
704+
urb_tq_entry_t *item;
705+
dwc_irqflags_t flags;
706+
707+
DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
708+
while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) {
709+
item = DWC_TAILQ_FIRST(&hcd->completed_urb_list);
710+
urb = item->urb;
711+
DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item,
712+
urb_tq_entries);
713+
DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
714+
DWC_FREE(item);
715+
716+
usb_hcd_unlink_urb_from_ep(hcd->priv, urb);
717+
usb_hcd_giveback_urb(hcd->priv, urb, urb->status);
718+
719+
DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
720+
}
721+
DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
722+
return;
723+
}
724+
697725
static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list)
698726
{
699727
dwc_list_link_t *item;
@@ -833,6 +861,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd)
833861

834862
DWC_TIMER_FREE(dwc_otg_hcd->conn_timer);
835863
DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet);
864+
DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet);
836865

837866
#ifdef DWC_DEV_SRPCAP
838867
if (dwc_otg_hcd->core_if->power_down == 2 &&
@@ -877,7 +906,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
877906
DWC_LIST_INIT(&hcd->periodic_sched_ready);
878907
DWC_LIST_INIT(&hcd->periodic_sched_assigned);
879908
DWC_LIST_INIT(&hcd->periodic_sched_queued);
880-
909+
DWC_TAILQ_INIT(&hcd->completed_urb_list);
881910
/*
882911
* Create a host channel descriptor for each host channel implemented
883912
* in the controller. Initialize the channel descriptor array.
@@ -915,6 +944,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
915944

916945
/* Initialize reset tasklet. */
917946
hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd);
947+
948+
hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet",
949+
completion_tasklet_func, hcd);
918950
#ifdef DWC_DEV_SRPCAP
919951
if (hcd->core_if->power_down == 2) {
920952
/* Initialize Power on timer for Host power up in case hibernation */

drivers/usb/host/dwc_otg/dwc_otg_hcd.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,13 @@ typedef struct dwc_otg_qh {
374374

375375
DWC_CIRCLEQ_HEAD(hc_list, dwc_hc);
376376

377+
typedef struct urb_tq_entry {
378+
struct urb *urb;
379+
DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries;
380+
} urb_tq_entry_t;
381+
382+
DWC_TAILQ_HEAD(urb_list, urb_tq_entry);
383+
377384
/**
378385
* This structure holds the state of the HCD, including the non-periodic and
379386
* periodic schedules.
@@ -551,6 +558,9 @@ struct dwc_otg_hcd {
551558
/* Tasket to do a reset */
552559
dwc_tasklet_t *reset_tasklet;
553560

561+
dwc_tasklet_t *completion_tasklet;
562+
struct urb_list completed_urb_list;
563+
554564
/* */
555565
dwc_spinlock_t *lock;
556566
dwc_spinlock_t *channel_lock;

drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
271271
dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status)
272272
{
273273
struct urb *urb = (struct urb *)urb_handle;
274-
274+
urb_tq_entry_t *new_entry;
275275
if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) {
276276
DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n",
277277
__func__, urb, usb_pipedevice(urb->pipe),
@@ -285,7 +285,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
285285
}
286286
}
287287
}
288-
288+
new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t));
289289
urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb);
290290
/* Convert status value. */
291291
switch (status) {
@@ -348,18 +348,25 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle,
348348
}
349349

350350
DWC_FREE(dwc_otg_urb);
351-
351+
if (!new_entry) {
352+
DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n");
353+
urb->status = -EPROTO;
354+
/* don't schedule the tasklet -
355+
* directly return the packet here with error. */
352356
#if USB_URB_EP_LINKING
353-
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
357+
usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb);
354358
#endif
355-
DWC_SPINUNLOCK(hcd->lock);
356359
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
357-
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
360+
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb);
358361
#else
359-
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status);
362+
usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status);
360363
#endif
361-
DWC_SPINLOCK(hcd->lock);
362-
364+
} else {
365+
new_entry->urb = urb;
366+
DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry,
367+
urb_tq_entries);
368+
DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet);
369+
}
363370
return 0;
364371
}
365372

0 commit comments

Comments
 (0)