Commit 33d61f24 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: UHCI: Get rid of excessive spinlocks

This patch introduces a major simplification into the UHCI driver by
replacing its multiple spinlocks with a single one.  The protected area of
code is slightly larger and there's more possibilities for contention on
an SMP system, but I think that shouldn't be a problem.  Stephen Hemminger
has been kind enough to test this on his SMP computer and he hasn't
encountered any difficulties.
parent 2212df84
...@@ -27,7 +27,7 @@ static inline void lprintk(char *buf) ...@@ -27,7 +27,7 @@ static inline void lprintk(char *buf)
p = strchr(buf, '\n'); p = strchr(buf, '\n');
if (p) if (p)
*p = 0; *p = 0;
printk("%s\n", buf); printk(KERN_DEBUG "%s\n", buf);
buf = p; buf = p;
if (buf) if (buf)
buf++; buf++;
...@@ -328,21 +328,17 @@ static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *bu ...@@ -328,21 +328,17 @@ static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *bu
//out += sprintf(out, "Inserttime=%lx ",urbp->inserttime); //out += sprintf(out, "Inserttime=%lx ",urbp->inserttime);
//out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime); //out += sprintf(out, "FSBRtime=%lx ",urbp->fsbrtime);
spin_lock(&urbp->urb->lock);
count = 0; count = 0;
list_for_each(tmp, &urbp->td_list) list_for_each(tmp, &urbp->td_list)
count++; count++;
spin_unlock(&urbp->urb->lock);
out += sprintf(out, "TDs=%d ",count); out += sprintf(out, "TDs=%d ",count);
if (urbp->queued) if (urbp->queued)
out += sprintf(out, "queued\n"); out += sprintf(out, "queued\n");
else { else {
spin_lock(&uhci->frame_list_lock);
count = 0; count = 0;
list_for_each(tmp, &urbp->queue_list) list_for_each(tmp, &urbp->queue_list)
count++; count++;
spin_unlock(&uhci->frame_list_lock);
out += sprintf(out, "queued URBs=%d\n", count); out += sprintf(out, "queued URBs=%d\n", count);
} }
...@@ -352,12 +348,10 @@ static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *bu ...@@ -352,12 +348,10 @@ static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, char *bu
static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len) static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
{ {
char *out = buf; char *out = buf;
unsigned long flags;
struct list_head *head, *tmp; struct list_head *head, *tmp;
int count; int count;
out += sprintf(out, "Main list URBs:"); out += sprintf(out, "Main list URBs:");
spin_lock_irqsave(&uhci->urb_list_lock, flags);
if (list_empty(&uhci->urb_list)) if (list_empty(&uhci->urb_list))
out += sprintf(out, " Empty\n"); out += sprintf(out, " Empty\n");
else { else {
...@@ -373,10 +367,8 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len) ...@@ -373,10 +367,8 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
tmp = tmp->next; tmp = tmp->next;
} }
} }
spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
out += sprintf(out, "Remove list URBs:"); out += sprintf(out, "Remove list URBs:");
spin_lock_irqsave(&uhci->urb_remove_list_lock, flags);
if (list_empty(&uhci->urb_remove_list)) if (list_empty(&uhci->urb_remove_list))
out += sprintf(out, " Empty\n"); out += sprintf(out, " Empty\n");
else { else {
...@@ -392,10 +384,8 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len) ...@@ -392,10 +384,8 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
tmp = tmp->next; tmp = tmp->next;
} }
} }
spin_unlock_irqrestore(&uhci->urb_remove_list_lock, flags);
out += sprintf(out, "Complete list URBs:"); out += sprintf(out, "Complete list URBs:");
spin_lock_irqsave(&uhci->complete_list_lock, flags);
if (list_empty(&uhci->complete_list)) if (list_empty(&uhci->complete_list))
out += sprintf(out, " Empty\n"); out += sprintf(out, " Empty\n");
else { else {
...@@ -411,7 +401,6 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len) ...@@ -411,7 +401,6 @@ static int uhci_show_lists(struct uhci_hcd *uhci, char *buf, int len)
tmp = tmp->next; tmp = tmp->next;
} }
} }
spin_unlock_irqrestore(&uhci->complete_list_lock, flags);
return out - buf; return out - buf;
} }
...@@ -425,7 +414,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ...@@ -425,7 +414,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
struct uhci_td *td; struct uhci_td *td;
struct list_head *tmp, *head; struct list_head *tmp, *head;
spin_lock_irqsave(&uhci->frame_list_lock, flags); spin_lock_irqsave(&uhci->schedule_lock, flags);
out += sprintf(out, "HC status\n"); out += sprintf(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf)); out += uhci_show_status(uhci, out, len - (out - buf));
...@@ -508,11 +497,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ...@@ -508,11 +497,11 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
} }
} }
spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
if (debug > 2) if (debug > 2)
out += uhci_show_lists(uhci, out, len - (out - buf)); out += uhci_show_lists(uhci, out, len - (out - buf));
spin_unlock_irqrestore(&uhci->schedule_lock, flags);
return out - buf; return out - buf;
} }
...@@ -623,4 +612,3 @@ static struct file_operations uhci_proc_operations = { ...@@ -623,4 +612,3 @@ static struct file_operations uhci_proc_operations = {
.release = uhci_proc_release, .release = uhci_proc_release,
}; };
#endif #endif
This diff is collapsed.
...@@ -342,8 +342,8 @@ struct uhci_hcd { ...@@ -342,8 +342,8 @@ struct uhci_hcd {
struct uhci_td *term_td; /* Terminating TD, see UHCI bug */ struct uhci_td *term_td; /* Terminating TD, see UHCI bug */
struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */
spinlock_t frame_list_lock; spinlock_t schedule_lock;
struct uhci_frame_list *fl; /* P: uhci->frame_list_lock */ struct uhci_frame_list *fl; /* P: uhci->schedule_lock */
int fsbr; /* Full-speed bandwidth reclamation */ int fsbr; /* Full-speed bandwidth reclamation */
unsigned long fsbrtimeout; /* FSBR delay */ unsigned long fsbrtimeout; /* FSBR delay */
...@@ -353,24 +353,19 @@ struct uhci_hcd { ...@@ -353,24 +353,19 @@ struct uhci_hcd {
unsigned int saved_framenumber; /* Save during PM suspend */ unsigned int saved_framenumber; /* Save during PM suspend */
/* Main list of URB's currently controlled by this HC */ /* Main list of URB's currently controlled by this HC */
spinlock_t urb_list_lock; struct list_head urb_list; /* P: uhci->schedule_lock */
struct list_head urb_list; /* P: uhci->urb_list_lock */
/* List of QH's that are done, but waiting to be unlinked (race) */ /* 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; /* P: uhci->schedule_lock */
struct list_head qh_remove_list; /* P: uhci->qh_remove_list_lock */
/* List of TD's that are done, but waiting to be freed (race) */ /* List of TD's that are done, but waiting to be freed (race) */
spinlock_t td_remove_list_lock; struct list_head td_remove_list; /* P: uhci->schedule_lock */
struct list_head td_remove_list; /* P: uhci->td_remove_list_lock */
/* List of asynchronously unlinked URB's */ /* List of asynchronously unlinked URB's */
spinlock_t urb_remove_list_lock; struct list_head urb_remove_list; /* P: uhci->schedule_lock */
struct list_head urb_remove_list; /* P: uhci->urb_remove_list_lock */
/* List of URB's awaiting completion callback */ /* List of URB's awaiting completion callback */
spinlock_t complete_list_lock; struct list_head complete_list; /* P: uhci->schedule_lock */
struct list_head complete_list; /* P: uhci->complete_list_lock */
int rh_numports; int rh_numports;
...@@ -401,26 +396,15 @@ struct urb_priv { ...@@ -401,26 +396,15 @@ struct urb_priv {
/* /*
* Locking in uhci.c * Locking in uhci.c
* *
* spinlocks are used extensively to protect the many lists and data * Almost everything relating to the hardware schedule and processing
* structures we have. It's not that pretty, but it's necessary. We * of URBs is protected by uhci->schedule_lock. urb->status is protected
* need to be done with all of the locks (except complete_list_lock) when * by urb->lock; that's the one exception.
* 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: * To prevent deadlocks, never lock uhci->schedule_lock while holding
* urb->lock. The safe order of locking is:
* *
* #1 uhci->urb_list_lock * #1 uhci->schedule_lock
* #2 urb->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
*/ */
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment