ChangeSet 1.1234, 2003/06/18 16:48:52-07:00, [EMAIL PROTECTED]

[PATCH] USB: ehci i/o watchdog

This patch adds a new "I/O watchdog" role to the existing
timer code, and cleans it up a bit.  If you want to run
EHCI without IRQs, it's now simple:  disable them, and
tweak the timer appropriately.

The patch should help with these reported problems.

    (a) Bulk I/O sometimes seems to stop progressing.  Not
        trouble in itself, but usb-storage and scsi could
        wedge deeply because of bugs in their fault recovery;
        and then the problems could break khubd and rmmod...

    (b) Some periodic transfers need to be "jumpstarted".
        Usually seen with a high speed hub.


 drivers/usb/host/ehci-hcd.c |   24 ++++++++++++--------
 drivers/usb/host/ehci-q.c   |   35 +++++++++++-------------------
 drivers/usb/host/ehci.h     |   51 ++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 77 insertions(+), 33 deletions(-)


diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c       Wed Jun 18 17:35:37 2003
+++ b/drivers/usb/host/ehci-hcd.c       Wed Jun 18 17:35:37 2003
@@ -118,8 +118,10 @@
 #define        EHCI_TUNE_MULT_TT       1
 #define        EHCI_TUNE_FLS           2       /* (small) 256 frame schedule */
 
-#define EHCI_WATCHDOG_JIFFIES  (HZ/100)        /* arbitrary; ~10 msec */
+#define EHCI_IAA_JIFFIES       (HZ/100)        /* arbitrary; ~10 msec */
+#define EHCI_IO_JIFFIES                (HZ/10)         /* io watchdog > irq_thresh */
 #define EHCI_ASYNC_JIFFIES     (HZ/20)         /* async idle timeout */
+#define EHCI_SHRINK_JIFFIES    (HZ/200)        /* async qh unlink delay */
 
 /* Initial IRQ latency:  lower than default */
 static int log2_irq_thresh = 0;                // 0 to 6
@@ -268,16 +270,13 @@
                }
        }
 
+       /* stop async processing after it's idled a bit */
+       if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
+               start_unlink_async (ehci, ehci->async);
+
+       /* ehci could run by timer, without IRQs ... */
        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 */
-       else if (ehci->async_idle) {
-               start_unlink_async (ehci, ehci->async);
-               ehci->async_idle = 0;
-       }
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
@@ -660,11 +659,18 @@
  */
 static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
+       timer_action_done (ehci, TIMER_IO_WATCHDOG);
        if (ehci->reclaim_ready)
                end_unlink_async (ehci, regs);
        scan_async (ehci, regs);
        if (ehci->next_uframe != -1)
                scan_periodic (ehci, regs);
+
+       /* the IO watchdog guards against hardware or driver bugs that
+        * misplace IRQs, and should let us run completely without IRQs.
+        */
+       if ((ehci->async->qh_next.ptr != 0) || (ehci->periodic_sched != 0))
+               timer_action (ehci, TIMER_IO_WATCHDOG);
 }
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c Wed Jun 18 17:35:37 2003
+++ b/drivers/usb/host/ehci-q.c Wed Jun 18 17:35:37 2003
@@ -741,8 +741,7 @@
 
        /* (re)start the async schedule? */
        head = ehci->async;
-       if (ehci->async_idle)
-               del_timer (&ehci->watchdog);
+       timer_action_done (ehci, TIMER_ASYNC_OFF);
        if (!head->qh_next.qh) {
                u32     cmd = readl (&ehci->regs->command);
 
@@ -773,8 +772,6 @@
 
        qh->qh_state = QH_STATE_LINKED;
        /* qtd completions reported later by interrupt */
-
-       ehci->async_idle = 0;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -955,7 +952,7 @@
        struct ehci_qh          *qh = ehci->reclaim;
        struct ehci_qh          *next;
 
-       del_timer (&ehci->watchdog);
+       timer_action_done (ehci, TIMER_IAA_WATCHDOG);
 
        // qh->hw_next = cpu_to_le32 (qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
@@ -980,12 +977,8 @@
                 * active but idle for a while once it empties.
                 */
                if (HCD_IS_RUNNING (ehci->hcd.state)
-                               && ehci->async->qh_next.qh == 0
-                               && !timer_pending (&ehci->watchdog)) {
-                       ehci->async_idle = 1;
-                       mod_timer (&ehci->watchdog,
-                                       jiffies + EHCI_ASYNC_JIFFIES);
-               }
+                               && ehci->async->qh_next.qh == 0)
+                       timer_action (ehci, TIMER_ASYNC_OFF);
        }
 
        if (next)
@@ -1020,6 +1013,7 @@
                        wmb ();
                        // handshake later, if we need to
                }
+               timer_action_done (ehci, TIMER_ASYNC_OFF);
                return;
        } 
 
@@ -1045,9 +1039,8 @@
        ehci->reclaim_ready = 0;
        cmd |= CMD_IAAD;
        writel (cmd, &ehci->regs->command);
-       /* posted write need not be known to HC yet ... */
-
-       mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES);
+       (void) readl (&ehci->regs->command);
+       timer_action (ehci, TIMER_IAA_WATCHDOG);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1056,10 +1049,11 @@
 scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        struct ehci_qh          *qh;
-       int                     unlink_delay = 0;
+       enum ehci_timer_action  action = TIMER_IO_WATCHDOG;
 
        if (!++(ehci->stamp))
                ehci->stamp++;
+       timer_action_done (ehci, TIMER_ASYNC_SHRINK);
 rescan:
        qh = ehci->async->qh_next.qh;
        if (likely (qh != 0)) {
@@ -1091,17 +1085,14 @@
                         */
                        if (list_empty (&qh->qtd_list)) {
                                if (qh->stamp == ehci->stamp)
-                                       unlink_delay = 1;
-                               else if (!ehci->reclaim) {
+                                       action = TIMER_ASYNC_SHRINK;
+                               else if (!ehci->reclaim)
                                        start_unlink_async (ehci, qh);
-                                       unlink_delay = 0;
-                               }
                        }
 
                        qh = qh->qh_next.qh;
                } while (qh);
        }
-
-       if (unlink_delay && !timer_pending (&ehci->watchdog))
-               mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES/2);
+       if (action == TIMER_ASYNC_SHRINK)
+               timer_action (ehci, TIMER_ASYNC_SHRINK);
 }
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h   Wed Jun 18 17:35:37 2003
+++ b/drivers/usb/host/ehci.h   Wed Jun 18 17:35:37 2003
@@ -52,8 +52,7 @@
        /* async schedule support */
        struct ehci_qh          *async;
        struct ehci_qh          *reclaim;
-       int                     reclaim_ready : 1,
-                               async_idle : 1;
+       int                     reclaim_ready : 1;
 
        /* periodic schedule support */
 #define        DEFAULT_I_TDPS          1024            /* some HCs can do less */
@@ -83,6 +82,7 @@
 
        struct timer_list       watchdog;
        struct notifier_block   reboot_notifier;
+       unsigned long           actions;
        unsigned                stamp;
 
        /* irq statistics */
@@ -99,6 +99,53 @@
 
 /* NOTE:  urb->transfer_flags expected to not use this bit !!! */
 #define EHCI_STATE_UNLINK      0x8000          /* urb being unlinked */
+
+enum ehci_timer_action {
+       TIMER_IO_WATCHDOG,
+       TIMER_IAA_WATCHDOG,
+       TIMER_ASYNC_SHRINK,
+       TIMER_ASYNC_OFF,
+};
+
+static inline void
+timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action)
+{
+       clear_bit (action, &ehci->actions);
+}
+
+static inline void
+timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
+{
+       if (!test_and_set_bit (action, &ehci->actions)) {
+               unsigned long t;
+
+               switch (action) {
+               case TIMER_IAA_WATCHDOG:
+                       t = EHCI_IAA_JIFFIES;
+                       break;
+               case TIMER_IO_WATCHDOG:
+                       t = EHCI_IO_JIFFIES;
+                       break;
+               case TIMER_ASYNC_OFF:
+                       t = EHCI_ASYNC_JIFFIES;
+                       break;
+               // case TIMER_ASYNC_SHRINK:
+               default:
+                       t = EHCI_SHRINK_JIFFIES;
+                       break;
+               }
+               t += jiffies;
+               // all timings except IAA watchdog can be overridden.
+               // async queue SHRINK often precedes IAA.  while it's ready
+               // to go OFF neither can matter, and afterwards the IO
+               // watchdog stops unless there's still periodic traffic.
+               if (action != TIMER_IAA_WATCHDOG
+                               && t > ehci->watchdog.expires
+                               && timer_pending (&ehci->watchdog))
+                       return;
+               mod_timer (&ehci->watchdog, t);
+       }
+}
 
 /*-------------------------------------------------------------------------*/
 



-------------------------------------------------------
This SF.Net email is sponsored by: INetU
Attention Web Developers & Consultants: Become An INetU Hosting Partner.
Refer Dedicated Servers. We Manage Them. You Get 10% Monthly Commission!
INetU Dedicated Managed Hosting http://www.inetu.net/partner/index.php
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to