Hi Johannes and list,

I was still seeing very slow scanning from 2.4.18-pre9 and and Epson.
I was wondering whether all the fixes to the 2.5 uhci.c settle this
issue? (Still can't quite get 2.5.5 vanilla to compile yet... :-((()

Cheers!
Richard Chan


On Wed, 2002-02-20 at 14:23, Johannes Erdfelt wrote:
> uhci.c didn't work well with USB storage. It would tend to stall
> relatively quickly and sometimes locked up the system. It usually only
> took me a couple of tries ripping a CD to reproduce the problem.
> 
> I took a long hard look at the locking in uhci.c and decided to clean
> it up, fixing a couple of bugs along the way as well as documenting the
> locking strategy.
> 
> With this patch applies, where I could only rip a CD a couple of times
> before causing problems, I was able to rip a CD 12,000 times in a row
> successfully, before I stopped it. Not a single error :)
> 
> This is the last patch for today.
> 
> JE
> 
> diff -ur linux-2.4.18-pre9/drivers/usb/uhci.c linux-2.4.18-pre9.je/drivers/usb/uhci.c
> --- linux-2.4.18-pre9/drivers/usb/uhci.c      Tue Feb 19 22:10:21 2002
> +++ linux-2.4.18-pre9.je/drivers/usb/uhci.c   Tue Feb 19 21:59:14 2002
> @@ -4,7 +4,7 @@
>   * Maintainer: Johannes Erdfelt <[EMAIL PROTECTED]>
>   *
>   * (C) Copyright 1999 Linus Torvalds
> - * (C) Copyright 1999-2001 Johannes Erdfelt, [EMAIL PROTECTED]
> + * (C) Copyright 1999-2002 Johannes Erdfelt, [EMAIL PROTECTED]
>   * (C) Copyright 1999 Randy Dunlap
>   * (C) Copyright 1999 Georg Acher, [EMAIL PROTECTED]
>   * (C) Copyright 1999 Deti Fliegl, [EMAIL PROTECTED]
> @@ -240,10 +240,10 @@
>       unsigned long flags;
>  
>       /* If it's not inserted, don't remove it */
> +     spin_lock_irqsave(&uhci->frame_list_lock, flags);
>       if (td->frame == -1 && list_empty(&td->fl_list))
> -             return;
> +             goto out;
>  
> -     spin_lock_irqsave(&uhci->frame_list_lock, flags);
>       if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
>               if (list_empty(&td->fl_list)) {
>                       uhci->fl->frame[td->frame] = td->link;
> @@ -268,6 +268,7 @@
>       list_del_init(&td->fl_list);
>       td->frame = -1;
>  
> +out:
>       spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
>  }
>  
> @@ -358,6 +359,9 @@
>       pci_pool_free(uhci->qh_pool, qh, qh->dma_handle);
>  }
>  
> +/*
> + * MUST be called with uhci->frame_list_lock acquired
> + */
>  static void _uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct urb 
>*urb)
>  {
>       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
> @@ -417,11 +421,10 @@
>               return;
>  
>       /* Only go through the hoops if it's actually linked in */
> +     spin_lock_irqsave(&uhci->frame_list_lock, flags);
>       if (!list_empty(&qh->list)) {
>               qh->urbp = NULL;
>  
> -             spin_lock_irqsave(&uhci->frame_list_lock, flags);
> -
>               pqh = list_entry(qh->list.prev, struct uhci_qh, list);
>  
>               if (pqh->urbp) {
> @@ -444,9 +447,8 @@
>               qh->element = qh->link = UHCI_PTR_TERM;
>  
>               list_del_init(&qh->list);
> -
> -             spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
>       }
> +     spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
>  
>       spin_lock_irqsave(&uhci->qh_remove_list_lock, flags);
>  
> @@ -658,6 +660,9 @@
>       return urbp;
>  }
>  
> +/*
> + * MUST be called with urb->lock acquired
> + */
>  static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
>  {
>       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
> @@ -667,6 +672,9 @@
>       list_add_tail(&td->list, &urbp->td_list);
>  }
>  
> +/*
> + * MUST be called with urb->lock acquired
> + */
>  static void uhci_remove_td_from_urb(struct uhci_td *td)
>  {
>       if (list_empty(&td->list))
> @@ -677,22 +685,22 @@
>       td->urb = NULL;
>  }
>  
> +/*
> + * MUST be called with urb->lock acquired
> + */
>  static void uhci_destroy_urb_priv(struct urb *urb)
>  {
>       struct list_head *head, *tmp;
>       struct urb_priv *urbp;
>       struct uhci *uhci;
> -     unsigned long flags;
> -
> -     spin_lock_irqsave(&urb->lock, flags);
>  
>       urbp = (struct urb_priv *)urb->hcpriv;
>       if (!urbp)
> -             goto out;
> +             return;
>  
>       if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) {
>               warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or 
>bus?", urb);
> -             goto out;
> +             return;
>       }
>  
>       if (!list_empty(&urb->urb_list))
> @@ -730,9 +738,6 @@
>  
>       urb->hcpriv = NULL;
>       kmem_cache_free(uhci_up_cachep, urbp);
> -
> -out:
> -     spin_unlock_irqrestore(&urb->lock, flags);
>  }
>  
>  static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb)
> @@ -874,7 +879,7 @@
>        * It's IN if the pipe is an output pipe or we're not expecting
>        * data back.
>        */
> -     destination &= ~TD_PID;
> +     destination &= ~TD_TOKEN_PID_MASK;
>       if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
>               destination |= USB_PID_IN;
>       else
> @@ -1312,9 +1317,7 @@
>       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
>       struct list_head *tmp, *head;
>       int ret = 0;
> -     unsigned long flags;
>  
> -     spin_lock_irqsave(&uhci->urb_list_lock, flags);
>       head = &uhci->urb_list;
>       tmp = head->next;
>       while (tmp != head) {
> @@ -1337,8 +1340,6 @@
>       } else
>               ret = -1;       /* no previous urb found */
>  
> -     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> -
>       return ret;
>  }
>  
> @@ -1446,34 +1447,30 @@
>       return ret;
>  }
>  
> +/*
> + * MUST be called with uhci->urb_list_lock acquired
> + */
>  static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb)
>  {
>       struct list_head *tmp, *head;
> -     unsigned long flags;
> -     struct urb *u = NULL;
>  
>       /* We don't match Isoc transfers since they are special */
>       if (usb_pipeisoc(urb->pipe))
>               return NULL;
>  
> -     spin_lock_irqsave(&uhci->urb_list_lock, flags);
>       head = &uhci->urb_list;
>       tmp = head->next;
>       while (tmp != head) {
> -             u = list_entry(tmp, struct urb, urb_list);
> +             struct urb *u = list_entry(tmp, struct urb, urb_list);
>  
>               tmp = tmp->next;
>  
>               if (u->dev == urb->dev && u->pipe == urb->pipe &&
>                   u->status == -EINPROGRESS)
> -                     goto out;
> +                     return u;
>       }
> -     u = NULL;
> -
> -out:
> -     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>  
> -     return u;
> +     return NULL;
>  }
>  
>  static int uhci_submit_urb(struct urb *urb)
> @@ -1497,13 +1494,15 @@
>       INIT_LIST_HEAD(&urb->urb_list);
>       usb_inc_dev_use(urb->dev);
>  
> -     spin_lock_irqsave(&urb->lock, flags);
> +     spin_lock_irqsave(&uhci->urb_list_lock, flags);
> +     spin_lock(&urb->lock);
>  
>       if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
>           urb->status == -ECONNABORTED) {
>               dbg("uhci_submit_urb: urb not available to submit (status = %d)", 
>urb->status);
>               /* Since we can have problems on the out path */
> -             spin_unlock_irqrestore(&urb->lock, flags);
> +             spin_unlock(&urb->lock);
> +             spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>               usb_dec_dev_use(urb->dev);
>  
>               return ret;
> @@ -1572,18 +1571,21 @@
>  out:
>       urb->status = ret;
>  
> -     spin_unlock_irqrestore(&urb->lock, flags);
> -
>       if (ret == -EINPROGRESS) {
> -             spin_lock_irqsave(&uhci->urb_list_lock, flags);
>               /* We use _tail to make find_urb_ep more efficient */
>               list_add_tail(&urb->urb_list, &uhci->urb_list);
> +
> +             spin_unlock(&urb->lock);
>               spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>  
>               return 0;
>       }
>  
>       uhci_unlink_generic(uhci, urb);
> +
> +     spin_unlock(&urb->lock);
> +     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> +
>       uhci_call_completion(urb);
>  
>       return ret;
> @@ -1592,7 +1594,7 @@
>  /*
>   * Return the result of a transfer
>   *
> - * Must be called with urb_list_lock acquired
> + * MUST be called with urb_list_lock acquired
>   */
>  static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
>  {
> @@ -1631,10 +1633,10 @@
>  
>       urbp->status = ret;
>  
> -     spin_unlock_irqrestore(&urb->lock, flags);
> -
> -     if (ret == -EINPROGRESS)
> +     if (ret == -EINPROGRESS) {
> +             spin_unlock_irqrestore(&urb->lock, flags);
>               return;
> +     }
>  
>       switch (usb_pipetype(urb->pipe)) {
>       case PIPE_CONTROL:
> @@ -1664,11 +1666,17 @@
>                       usb_pipetype(urb->pipe), urb);
>       }
>  
> +     /* Remove it from uhci->urb_list */
>       list_del_init(&urb->urb_list);
>  
>       uhci_add_complete(urb);
> +
> +     spin_unlock_irqrestore(&urb->lock, flags);
>  }
>  
> +/*
> + * MUST be called with urb->lock acquired
> + */
>  static void uhci_unlink_generic(struct uhci *uhci, struct urb *urb)
>  {
>       struct list_head *head, *tmp;
> @@ -1735,6 +1743,9 @@
>  
>       uhci = (struct uhci *)urb->dev->bus->hcpriv;
>  
> +     spin_lock_irqsave(&uhci->urb_list_lock, flags);
> +     spin_lock(&urb->lock);
> +
>       /* Release bandwidth for Interrupt or Isoc. transfers */
>       /* Spinlock needed ? */
>       if (urb->bandwidth) {
> @@ -1750,35 +1761,41 @@
>               }
>       }
>  
> -     if (urb->status != -EINPROGRESS)
> +     if (urb->status != -EINPROGRESS) {
> +             spin_unlock(&urb->lock);
> +             spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>               return 0;
> +     }
>  
> -     spin_lock_irqsave(&uhci->urb_list_lock, flags);
>       list_del_init(&urb->urb_list);
> -     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>  
>       uhci_unlink_generic(uhci, urb);
>  
>       /* Short circuit the virtual root hub */
>       if (urb->dev == uhci->rh.dev) {
>               rh_unlink_urb(urb);
> +
> +             spin_unlock(&urb->lock);
> +             spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> +
>               uhci_call_completion(urb);
>       } else {
>               if (urb->transfer_flags & USB_ASYNC_UNLINK) {
> -                     /* urb_list is available now since we called */
> -                     /*  uhci_unlink_generic already */
> -
>                       urbp->status = urb->status = -ECONNABORTED;
>  
> -                     spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
> +                     spin_lock(&uhci->urb_remove_list_lock);
>  
> -                     /* Check to see if the remove list is empty */
> +                     /* If we're the first, set the next interrupt bit */
>                       if (list_empty(&uhci->urb_remove_list))
>                               uhci_set_next_interrupt(uhci);
>                       
>                       list_add(&urb->urb_list, &uhci->urb_remove_list);
>  
> -                     spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
> +                     spin_unlock(&uhci->urb_remove_list_lock);
> +
> +                     spin_unlock(&urb->lock);
> +                     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> +
>               } else {
>                       urb->status = -ENOENT;
>  
> @@ -1791,6 +1808,9 @@
>                       } else
>                               schedule_timeout(1+1*HZ/1000); 
>  
> +                     spin_unlock(&urb->lock);
> +                     spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
> +
>                       uhci_call_completion(urb);
>               }
>       }
> @@ -1924,12 +1944,14 @@
>  /* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
>  static int rh_send_irq(struct urb *urb)
>  {
> -     int i, len = 1;
>       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
> +     struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
>       unsigned int io_addr = uhci->io_addr;
> +     unsigned long flags;
> +     int i, len = 1;
>       __u16 data = 0;
> -     struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
>  
> +     spin_lock_irqsave(&urb->lock, flags);
>       for (i = 0; i < uhci->rh.numports; i++) {
>               data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 
>1)) : 0);
>               len = (i + 1) / 8 + 1;
> @@ -1939,6 +1961,8 @@
>       urb->actual_length = len;
>       urbp->status = 0;
>  
> +     spin_unlock_irqrestore(&urb->lock, flags);
> +
>       if ((data > 0) && (uhci->rh.send != 0)) {
>               dbg("root-hub INT complete: port1: %x port2: %x data: %x",
>                       inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data);
> @@ -1965,7 +1989,6 @@
>  
>       spin_lock_irqsave(&uhci->urb_list_lock, flags);
>       head = &uhci->urb_list;
> -
>       tmp = head->next;
>       while (tmp != head) {
>               struct urb *u = list_entry(tmp, struct urb, urb_list);
> @@ -1973,6 +1996,8 @@
>  
>               tmp = tmp->next;
>  
> +             spin_lock(&urb->lock);
> +
>               /* Check if the FSBR timed out */
>               if (urbp->fsbr && !urbp->fsbr_timeout && time_after_eq(jiffies, 
>urbp->fsbrtime + IDLE_TIMEOUT))
>                       uhci_fsbr_timeout(uhci, u);
> @@ -1982,6 +2007,8 @@
>                       list_del(&u->urb_list);
>                       list_add_tail(&u->urb_list, &list);
>               }
> +
> +             spin_unlock(&urb->lock);
>       }
>       spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
>  
> @@ -2215,6 +2242,9 @@
>       return stat;
>  }
>  
> +/*
> + * MUST be called with urb->lock acquired
> + */
>  static int rh_unlink_urb(struct urb *urb)
>  {
>       struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv;
> @@ -2250,11 +2280,20 @@
>  
>  static void uhci_call_completion(struct urb *urb)
>  {
> -     struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
> +     struct urb_priv *urbp;
>       struct usb_device *dev = urb->dev;
>       struct uhci *uhci = (struct uhci *)dev->bus->hcpriv;
>       int is_ring = 0, killed, resubmit_interrupt, status;
>       struct urb *nurb;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&urb->lock, flags);
> +
> +     urbp = (struct urb_priv *)urb->hcpriv;
> +     if (!urbp || !urb->dev) {
> +             spin_unlock_irqrestore(&urb->lock, flags);
> +             return;
> +     }
>  
>       killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
>                       urb->status == -ECONNRESET);
> @@ -2302,6 +2341,8 @@
>               urb->status = status;
>  
>       urb->dev = NULL;
> +     spin_unlock_irqrestore(&urb->lock, flags);
> +
>       if (urb->complete) {
>               urb->complete(urb);
>  
> @@ -2339,11 +2380,14 @@
>               struct urb_priv *urbp = list_entry(tmp, struct urb_priv, 
>complete_list);
>               struct urb *urb = urbp->urb;
>  
> -             tmp = tmp->next;
> -
>               list_del_init(&urbp->complete_list);
> +             spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
>  
>               uhci_call_completion(urb);
> +
> +             spin_lock_irqsave(&uhci->complete_list_lock, flags);
> +             head = &uhci->complete_list;
> +             tmp = head->next;
>       }
>       spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
>  }
> @@ -2365,7 +2409,8 @@
>               list_del_init(&urb->urb_list);
>  
>               urbp->status = urb->status = -ECONNRESET;
> -             uhci_call_completion(urb);
> +
> +             uhci_add_complete(urb);
>       }
>       spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
>  }
> @@ -3098,3 +3143,4 @@
>  MODULE_AUTHOR(DRIVER_AUTHOR);
>  MODULE_DESCRIPTION(DRIVER_DESC);
>  MODULE_LICENSE("GPL");
> +
> diff -ur linux-2.4.18-pre9/drivers/usb/uhci.h linux-2.4.18-pre9.je/drivers/usb/uhci.h
> --- linux-2.4.18-pre9/drivers/usb/uhci.h      Thu Nov 22 11:49:52 2001
> +++ linux-2.4.18-pre9.je/drivers/usb/uhci.h   Sun Feb 17 14:17:13 2002
> @@ -121,15 +121,16 @@
>   * for TD <info>: (a.k.a. Token)
>   */
>  #define TD_TOKEN_TOGGLE              19
> -#define TD_PID                       0xFF
> +#define TD_TOKEN_PID_MASK    0xFF
> +#define TD_TOKEN_EXPLEN_MASK 0x7FF           /* expected length, encoded as n - 1 */
>  
>  #define uhci_maxlen(token)   ((token) >> 21)
> -#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 
>1-based */
> +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 
>1-based */
>  #define uhci_toggle(token)   (((token) >> TD_TOKEN_TOGGLE) & 1)
>  #define uhci_endpoint(token) (((token) >> 15) & 0xf)
>  #define uhci_devaddr(token)  (((token) >> 8) & 0x7f)
>  #define uhci_devep(token)    (((token) >> 8) & 0x7ff)
> -#define uhci_packetid(token) ((token) & 0xff)
> +#define uhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
>  #define uhci_packetout(token)        (uhci_packetid(token) != USB_PID_IN)
>  #define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN)
>  
> @@ -163,7 +164,7 @@
>       struct list_head list;          /* P: urb->lock */
>  
>       int frame;
> -     struct list_head fl_list;       /* P: frame_list_lock */
> +     struct list_head fl_list;       /* P: uhci->frame_list_lock */
>  } __attribute__((aligned(16)));
>  
>  /*
> @@ -306,21 +307,25 @@
>       struct uhci_qh *skelqh[UHCI_NUM_SKELQH];        /* Skeleton QH's */
>  
>       spinlock_t frame_list_lock;
> -     struct uhci_frame_list *fl;             /* Frame list */
> +     struct uhci_frame_list *fl;             /* P: uhci->frame_list_lock */
>       int fsbr;                               /* Full speed bandwidth reclamation */
>       int is_suspended;
>  
> +     /* Main list of URB's currently controlled by this HC */
> +     spinlock_t urb_list_lock;
> +     struct list_head urb_list;              /* P: uhci->urb_list_lock */
> +
> +     /* List of QH's that are done, but waiting to be unlinked (race) */
>       spinlock_t qh_remove_list_lock;
> -     struct list_head qh_remove_list;
> +     struct list_head qh_remove_list;        /* P: uhci->qh_remove_list_lock */
>  
> +     /* List of asynchronously unlinked URB's */
>       spinlock_t urb_remove_list_lock;
> -     struct list_head urb_remove_list;
> -
> -     spinlock_t urb_list_lock;
> -     struct list_head urb_list;
> +     struct list_head urb_remove_list;       /* P: uhci->urb_remove_list_lock */
>  
> +     /* List of URB's awaiting completion callback */
>       spinlock_t complete_list_lock;
> -     struct list_head complete_list;
> +     struct list_head complete_list;         /* P: uhci->complete_list_lock */
>  
>       struct virt_root_hub rh;        /* private data of the virtual root hub */
>  };
> @@ -333,7 +338,7 @@
>       dma_addr_t transfer_buffer_dma_handle;
>  
>       struct uhci_qh *qh;             /* QH for this URB */
> -     struct list_head td_list;
> +     struct list_head td_list;       /* P: urb->lock */
>  
>       int fsbr : 1;                   /* URB turned on FSBR */
>       int fsbr_timeout : 1;           /* URB timed out on FSBR */
> @@ -345,11 +350,36 @@
>       int status;                     /* Final status */
>  
>       unsigned long inserttime;       /* In jiffies */
> -     unsigned long fsbrtime; /* In jiffies */
> +     unsigned long fsbrtime;         /* In jiffies */
>  
> -     struct list_head queue_list;
> -     struct list_head complete_list;
> +     struct list_head queue_list;    /* P: uhci->frame_list_lock */
> +     struct list_head complete_list; /* P: uhci->complete_list_lock */
>  };
> +
> +/*
> + * Locking in uhci.c
> + *
> + * spinlocks are used extensively to protect the many lists and data
> + * structures we have. It's not that pretty, but it's necessary. We
> + * need to be done with all of the locks (except complete_list_lock) when
> + * we call urb->complete. I've tried to make it simple enough so I don't
> + * have to spend hours racking my brain trying to figure out if the
> + * locking is safe.
> + *
> + * Here's the safe locking order to prevent deadlocks:
> + *
> + * #1 uhci->urb_list_lock
> + * #2 urb->lock
> + * #3 uhci->urb_remove_list_lock, uhci->frame_list_lock, 
> + *   uhci->qh_remove_list_lock
> + * #4 uhci->complete_list_lock
> + *
> + * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
> + * at the lowest level FIRST and NEVER grab locks at the same level at the
> + * same time.
> + * 
> + * So, if you need uhci->urb_list_lock, grab it before you grab urb->lock
> + */
>  
>  /* -------------------------------------------------------------------------
>     Virtual Root HUB
> 
> _______________________________________________
> [EMAIL PROTECTED]
> To unsubscribe, use the last form field at:
> https://lists.sourceforge.net/lists/listinfo/linux-usb-devel



_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to