linux-2.5.40 and .41 got a kernel invalid page request
when using a USB keyboard.  linux-2.5.34 worked fine.

        The bug is an incompatibility between uhci_alloc_urb_priv and
uhci_destroy_urb_priv, at least as they are used by uhci_urb_enqueue.
uhci_alloc_urb_priv adds the urb_priv to uhci->urb_list, but
uhci_destroy_urb_priv does not remove it.  However, uhci_urb_enqueue
ASSUMES that uhci_destroy_urb_priv will completeley undo the
uhci_alloc_urb_priv.

        In particular, this occurs uhci_urb_enqeueue when is called on
a control pipe and uhci_find_urb_ep() returns non-NULL.  This occurs
when I plug in a USB keyboard or boot with a USB keyboard plugged in,
presumably from some of the activity generated by the "hotplug"
scripts.

        The following patch changes uhci_destroy_urb_priv to
remove the urb_priv from whatever list it is on if it has been
added.  It works for me, although I am not sure do not think
this patch is completly optimal:

        1. If it really is OK for uhci_destroy_urb_priv to be called
           when urbp->urb_list is linked into a list, then the warning
           message should be deleted.

        2. If queuing of control and interrupt requests is not going
           to be implemented, the check for eurb != NULL can be
           made earlier and uhci_urb_enqueue can just abort before
           uhci_alloc_urb_priv is ever called.

        In the meantime, however, this patch fixes a real bug, so
I would encourage you to integrate and forward it until there is a
better fix.


-- 
Adam J. Richter     __     ______________   575 Oroville Road
[EMAIL PROTECTED]     \ /                  Milpitas, California 95035
+1 408 309-6081         | g g d r a s i l   United States of America
                         "Free Software For The Rest Of Us."
--- linux-2.5.41/drivers/usb/host/uhci-hcd.c    2002-10-07 11:24:51.000000000 -0700
+++ linux/drivers/usb/host/uhci-hcd.c   2002-10-09 07:16:43.000000000 -0700
@@ -691,20 +691,22 @@
 static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
 {
        struct list_head *head, *tmp;
        struct urb_priv *urbp;
 
        urbp = (struct urb_priv *)urb->hcpriv;
        if (!urbp)
                return;
 
-       if (!list_empty(&urbp->urb_list))
+       if (!list_empty(&urbp->urb_list)) {
                warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or 
uhci->remove_list", urb);
+               list_del(&urbp->urb_list);
+       }
 
        if (!list_empty(&urbp->complete_list))
                warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", 
urb);
 
        head = &urbp->td_list;
        tmp = head->next;
        while (tmp != head) {
                struct uhci_td *td = list_entry(tmp, struct uhci_td, list);
 

Reply via email to