ChangeSet 1.883.3.2, 2002/12/16 10:31:24-08:00, david-b@pacbell.net [PATCH] ehci-hcd (2/2): rest of tasklet remove This is the rest of the work to remove the tasklet: the non-syntax portions which affect work scheduling. It's not quite davem's version; it's got locking updates, which among other things prevent a hang when the timer kicks in. This scheduling change is split out from the other parts in case more problems like that unlink race (fixed in my previous patch) show up. It doesn't fix (or help fix) any ehci bugs, but simpler code is fine. diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Wed Dec 18 00:35:24 2002 +++ b/drivers/usb/host/ehci-hcd.c Wed Dec 18 00:35:24 2002 @@ -244,9 +244,7 @@ /*-------------------------------------------------------------------------*/ -static void ehci_tasklet (unsigned long param); - -static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs); +static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); static void ehci_watchdog (unsigned long param) { @@ -254,10 +252,25 @@ unsigned long flags; spin_lock_irqsave (&ehci->lock, flags); - /* guard against lost IAA, which wedges everything */ - ehci_irq (&ehci->hcd, NULL); + + /* lost IAA irqs wedge things badly; seen with a vt8235 */ + if (ehci->reclaim) { + u32 status = readl (&ehci->regs->status); + + if (status & STS_IAA) { + ehci_vdbg (ehci, "lost IAA\n"); + writel (STS_IAA, &ehci->regs->status); + ehci->reclaim_ready = 1; + } + } + + ehci_work (ehci, NULL); + if (ehci->reclaim && !timer_pending (&ehci->watchdog)) + mod_timer (&ehci->watchdog, + jiffies + EHCI_WATCHDOG_JIFFIES); + /* stop async processing after it's idled a while */ - if (ehci->async_idle) { + else if (ehci->async_idle) { start_unlink_async (ehci, ehci->async); ehci->async_idle = 0; } @@ -418,9 +431,6 @@ /* set async sleep time = 10 us ... ? */ - ehci->tasklet.func = ehci_tasklet; - ehci->tasklet.data = (unsigned long) ehci; - init_timer (&ehci->watchdog); ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; @@ -499,8 +509,9 @@ remove_debug_files (ehci); /* root hub is shut down separately (first, when possible) */ - tasklet_disable (&ehci->tasklet); - ehci_tasklet ((unsigned long) ehci); + spin_lock_irq (&ehci->lock); + ehci_work (ehci, NULL); + spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); #ifdef EHCI_STATS @@ -608,23 +619,16 @@ /*-------------------------------------------------------------------------*/ /* - * tasklet scheduled by some interrupts and other events - * calls driver completion functions ... but not in_irq() + * ehci_work is called from some interrupts, timers, and so on. + * it calls driver completion functions, after dropping ehci->lock. */ -static void ehci_tasklet (unsigned long param) +static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) { - struct ehci_hcd *ehci = (struct ehci_hcd *) param; - struct pt_regs *regs = NULL; - - spin_lock_irq (&ehci->lock); - if (ehci->reclaim_ready) end_unlink_async (ehci, regs); scan_async (ehci, regs); if (ehci->next_uframe != -1) scan_periodic (ehci, regs); - - spin_unlock_irq (&ehci->lock); } /*-------------------------------------------------------------------------*/ @@ -645,6 +649,8 @@ if (!status) /* irq sharing? */ return; + spin_lock (&ehci->lock); + /* clear (just) interrupts */ writel (status, &ehci->regs->status); readl (&ehci->regs->command); /* unblock posted write */ @@ -684,9 +690,9 @@ bh = 1; } - /* most work doesn't need to be in_irq() */ - if (likely (bh == 1)) - tasklet_schedule (&ehci->tasklet); + if (bh) + ehci_work (ehci, regs); + spin_unlock (&ehci->lock); } /*-------------------------------------------------------------------------*/ @@ -882,7 +888,7 @@ ) { spin_unlock_irqrestore (&ehci->lock, flags); /* wait_ms() won't spin, we're a thread; - * and we know IRQ+tasklet can progress + * and we know IRQ/timer/... can progress */ wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Wed Dec 18 00:35:24 2002 +++ b/drivers/usb/host/ehci-sched.c Wed Dec 18 00:35:24 2002 @@ -190,7 +190,7 @@ /* posted write ... PSS happens later */ ehci->hcd.state = USB_STATE_RUNNING; - /* make sure tasklet scans these */ + /* make sure ehci_work scans these */ ehci->next_uframe = readl (&ehci->regs->frame_index) % (ehci->periodic_size << 3); return 0; diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Wed Dec 18 00:35:24 2002 +++ b/drivers/usb/host/ehci.h Wed Dec 18 00:35:24 2002 @@ -65,9 +65,6 @@ int next_uframe; /* scan periodic, start here */ unsigned periodic_sched; /* periodic activity count */ - /* deferred work from IRQ, etc */ - struct tasklet_struct tasklet; - /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS];