Messages in this thread Patch in this message | | | Date | Sat, 10 May 2003 00:40:12 -0400 | From | Pete Zaitcev <> | Subject | Patch for usb-ohci in 2.4 needs review & testing |
| |
Hi, All:
The attached patch appears to fix a bug which was really making my life difficult: dd if=/dev/scd0 produces corrupted ISOs when reading off a USB CD-ROM on a box with no less than 2 P4 CPUs, HyperThreading and ServerWorks chipset. However, I'm not completely sure that I didn't break something.
If someone will be so kind to review and test, here are some notes to keep in mind:
- Review with looking at the code, not just the patch, because if I miss a place to change, it won't be apparent from the patch. The lock coverage expands, so it is important not to encroach on a call which can sleep.
- Test with CONFIG_DEBUG_SPINLOCK if you fancy it, it's interesting. If the thing locks up, an NMI oopser trace would be helpul (nmi_watchdog=1).
- Test with something other than a storage, in particular WEBCAMS, because they use isochronous transfers, which do completes from under a lock now.
- The whole idea of checking jiffy-sized timeouts from an interrupt is revolting, but I am wary of unknown here, so I didn't kill it. If anyone knows how that abomination came to life, I'll be glad to know too.
- Frode's one-shot interrupt fixes are stuck to the tail of it, because they conflict. It should not be a problem, except when you run -ac, which partially incorporates Frode's fixes already, and this patch produces one rejected segment. It is easy to merge.
Thanks in advance, -- Pete
diff -urN -X dontdiff linux-2.4.21-rc1/drivers/usb/host/usb-ohci.c linux-2.4.21-rc1-nip/drivers/usb/host/usb-ohci.c --- linux-2.4.21-rc1/drivers/usb/host/usb-ohci.c 2003-04-24 10:52:56.000000000 -0700 +++ linux-2.4.21-rc1-nip/drivers/usb/host/usb-ohci.c 2003-05-09 16:02:59.000000000 -0700 @@ -104,10 +104,6 @@ #define OHCI_UNLINK_TIMEOUT (HZ / 10) -static LIST_HEAD (ohci_hcd_list); -static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; - - /*-------------------------------------------------------------------------*/ /* AMD-756 (D2 rev) reports corrupt register contents in some cases. @@ -134,6 +130,53 @@ /*-------------------------------------------------------------------------* * URB support functions *-------------------------------------------------------------------------*/ + +static void ohci_complete_add(struct ohci *ohci, struct urb *urb) +{ + + if (urb->hcpriv != NULL) { + printk("completing with non-null priv!\n"); + return; + } + + if (ohci->complete_tail == NULL) { + ohci->complete_head = urb; + ohci->complete_tail = urb; + } else { + ohci->complete_head->hcpriv = urb; + ohci->complete_tail = urb; + } +} + +static inline struct urb *ohci_complete_get(struct ohci *ohci) +{ + struct urb *urb; + + if ((urb = ohci->complete_head) == NULL) + return NULL; + if (urb == ohci->complete_tail) { + ohci->complete_tail = NULL; + ohci->complete_head = NULL; + } else { + ohci->complete_head = urb->hcpriv; + } + urb->hcpriv = NULL; + return urb; +} + +static inline void ohci_complete(struct ohci *ohci) +{ + struct urb *urb; + + spin_lock(&ohci->ohci_lock); + while ((urb = ohci_complete_get(ohci)) != NULL) { + spin_unlock(&ohci->ohci_lock); + if (urb->complete) + (*urb->complete)(urb); + spin_lock(&ohci->ohci_lock); + } + spin_unlock(&ohci->ohci_lock); +} /* free HCD-private data associated with this URB */ @@ -214,15 +257,6 @@ } } -static void urb_rm_priv (struct urb * urb) -{ - unsigned long flags; - - spin_lock_irqsave (&usb_ed_lock, flags); - urb_rm_priv_locked (urb); - spin_unlock_irqrestore (&usb_ed_lock, flags); -} - /*-------------------------------------------------------------------------*/ #ifdef DEBUG @@ -466,7 +500,6 @@ { urb_priv_t * urb_priv = urb->hcpriv; struct urb * urbt; - unsigned long flags; int i; if (!urb_priv) @@ -474,7 +507,7 @@ /* just to be sure */ if (!urb->complete) { - urb_rm_priv (urb); + urb_rm_priv_locked (urb); return -1; } @@ -490,12 +523,17 @@ usb_pipeout (urb->pipe) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - urb->complete (urb); + if (urb->interval) { + urb->complete (urb); - /* implicitly requeued */ - urb->actual_length = 0; - urb->status = -EINPROGRESS; - td_submit_urb (urb); + /* implicitly requeued */ + urb->actual_length = 0; + urb->status = -EINPROGRESS; + td_submit_urb (urb); + } else { + urb_rm_priv_locked(urb); + ohci_complete_add(hc, urb); + } break; case PIPE_ISOCHRONOUS: @@ -508,7 +546,6 @@ ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); urb->complete (urb); - spin_lock_irqsave (&usb_ed_lock, flags); urb->actual_length = 0; urb->status = USB_ST_URB_PENDING; urb->start_frame = urb_priv->ed->last_iso + 1; @@ -519,18 +556,17 @@ } td_submit_urb (urb); } - spin_unlock_irqrestore (&usb_ed_lock, flags); - + } else { /* unlink URB, call complete */ - urb_rm_priv (urb); - urb->complete (urb); + urb_rm_priv_locked (urb); + ohci_complete_add(hc, urb); } break; case PIPE_BULK: case PIPE_CONTROL: /* unlink URB, call complete */ - urb_rm_priv (urb); - urb->complete (urb); + urb_rm_priv_locked (urb); + ohci_complete_add(hc, urb); break; } return 0; @@ -567,20 +603,24 @@ #ifdef DEBUG urb_print (urb, "SUB", usb_pipein (pipe)); #endif - + /* handle a request to the virtual root hub */ if (usb_pipedevice (pipe) == ohci->rh.devnum) return rh_submit_urb (urb); - + + spin_lock_irqsave(&ohci->ohci_lock, flags); + /* when controller's hung, permit only roothub cleanup attempts * such as powering down ports */ if (ohci->disabled) { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ESHUTDOWN; } /* every endpoint has a ed, locate and fill it */ if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -602,6 +642,7 @@ case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; if (size <= 0) { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } @@ -621,8 +662,9 @@ /* allocate the private part of the URB */ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + GFP_ATOMIC); if (!urb_priv) { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -633,13 +675,12 @@ urb_priv->ed = ed; /* allocate the TDs (updating hash chains) */ - spin_lock_irqsave (&usb_ed_lock, flags); for (i = 0; i < size; i++) { urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC); if (!urb_priv->td[i]) { urb_priv->length = i; urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -ENOMEM; } @@ -647,7 +688,7 @@ if (ed->state == ED_NEW || (ed->state & ED_DEL)) { urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return -EINVAL; } @@ -669,7 +710,7 @@ } if (bustime < 0) { urb_free_priv (ohci, urb_priv); - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (urb->dev); return bustime; } @@ -712,7 +753,7 @@ } #endif - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); return 0; } @@ -743,6 +784,7 @@ if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); + spin_lock_irqsave(&ohci->ohci_lock, flags); if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { if (!ohci->disabled) { urb_priv_t * urb_priv; @@ -752,6 +794,7 @@ */ if (!(urb->transfer_flags & USB_ASYNC_UNLINK) && in_interrupt ()) { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); err ("bug in call from %p; use async!", __builtin_return_address(0)); return -EWOULDBLOCK; @@ -760,11 +803,10 @@ /* flag the urb and its TDs for deletion in some * upcoming SF interrupt delete list processing */ - spin_lock_irqsave (&usb_ed_lock, flags); urb_priv = urb->hcpriv; if (!urb_priv || (urb_priv->state == URB_DEL)) { - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); return 0; } @@ -779,7 +821,7 @@ add_wait_queue (&unlink_wakeup, &wait); urb_priv->wait = &unlink_wakeup; - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); /* wait until all TDs are deleted */ set_current_state(TASK_UNINTERRUPTIBLE); @@ -796,11 +838,12 @@ } else { /* usb_dec_dev_use done in dl_del_list() */ urb->status = -EINPROGRESS; - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); return -EINPROGRESS; } } else { - urb_rm_priv (urb); + urb_rm_priv_locked (urb); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; if (urb->complete) @@ -850,7 +893,7 @@ * (freeing all the TDs, unlinking EDs) but we need * to defend against bugs that prevent that. */ - spin_lock_irqsave (&usb_ed_lock, flags); + spin_lock_irqsave(&ohci->ohci_lock, flags); for(i = 0; i < NUM_EDS; i++) { ed = &(dev->ed[i]); if (ed->state != ED_NEW) { @@ -865,7 +908,7 @@ cnt++; } } - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore(&ohci->ohci_lock, flags); /* if the controller is running, tds for those unlinked * urbs get freed by dl_del_list at the next SF interrupt @@ -1199,17 +1242,12 @@ td_t * td; ed_t * ed_ret; volatile ed_t * ed; - unsigned long flags; - - - spin_lock_irqsave (&usb_ed_lock, flags); ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { /* pending delete request */ - spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } @@ -1222,7 +1260,6 @@ /* out of memory */ if (td) td_free(ohci, td); - spin_unlock_irqrestore (&usb_ed_lock, flags); return NULL; } ed->hwTailP = cpu_to_le32 (td->td_dma); @@ -1245,8 +1282,7 @@ ed->int_period = interval; ed->int_load = load; } - - spin_unlock_irqrestore (&usb_ed_lock, flags); + return ed_ret; } @@ -1505,7 +1541,7 @@ /* handle an urb that is being unlinked */ -static void dl_del_urb (struct urb * urb) +static void dl_del_urb (struct ohci *ohci, struct urb * urb) { wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait; @@ -1513,8 +1549,7 @@ if (urb->transfer_flags & USB_ASYNC_UNLINK) { urb->status = -ECONNRESET; - if (urb->complete) - urb->complete (urb); + ohci_complete_add(ohci, urb); } else { urb->status = -ENOENT; if (urb->complete) @@ -1537,10 +1572,7 @@ td_t * td_rev = NULL; td_t * td_list = NULL; urb_priv_t * urb_priv = NULL; - unsigned long flags; - - spin_lock_irqsave (&usb_ed_lock, flags); - + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; ohci->hcca->done_head = 0; @@ -1566,7 +1598,6 @@ td_rev = td_list; td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; } - spin_unlock_irqrestore (&usb_ed_lock, flags); return td_list; } @@ -1578,7 +1609,6 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) { - unsigned long flags; ed_t * ed; __u32 edINFO; __u32 tdINFO; @@ -1586,8 +1616,6 @@ __u32 * td_p; int ctrl = 0, bulk = 0; - spin_lock_irqsave (&usb_ed_lock, flags); - for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0); @@ -1608,7 +1636,7 @@ /* URB is done; clean up */ if (++(urb_priv->td_cnt) == urb_priv->length) - dl_del_urb (urb); + dl_del_urb (ohci, urb); } else { td_p = &td->hwNextTD; } @@ -1665,7 +1693,6 @@ } ohci->ed_rm_list[frame] = NULL; - spin_unlock_irqrestore (&usb_ed_lock, flags); } @@ -1682,9 +1709,7 @@ struct urb * urb; urb_priv_t * urb_priv; __u32 tdINFO, edHeadP, edTailP; - - unsigned long flags; - + while (td_list) { td_list_next = td_list->next_dl_td; @@ -1713,13 +1738,10 @@ urb->status = cc_to_error[cc]; sohci_return_urb (ohci, urb); } else { - spin_lock_irqsave (&usb_ed_lock, flags); - dl_del_urb (urb); - spin_unlock_irqrestore (&usb_ed_lock, flags); + dl_del_urb (ohci, urb); } } - spin_lock_irqsave (&usb_ed_lock, flags); if (ed->state != ED_NEW) { edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; edTailP = le32_to_cpup (&ed->hwTailP); @@ -1728,7 +1750,6 @@ if ((edHeadP == edTailP) && (ed->state == ED_OPER)) ep_unlink (ohci, ed); } - spin_unlock_irqrestore (&usb_ed_lock, flags); td_list = td_list_next; } @@ -1919,7 +1940,8 @@ int leni = urb->transfer_buffer_length; int len = 0; int status = TD_CC_NOERROR; - + unsigned long flags; + __u32 datab[4]; __u8 * data_buf = (__u8 *) datab; @@ -1928,13 +1950,16 @@ __u16 wIndex; __u16 wLength; + spin_lock_irqsave(&ohci->ohci_lock, flags); + if (usb_pipeint(pipe)) { ohci->rh.urb = urb; ohci->rh.send = 1; ohci->rh.interval = urb->interval; rh_init_int_timer(urb); urb->status = cc_to_error [TD_CC_NOERROR]; - + + spin_unlock_irqrestore(&ohci->ohci_lock, flags); return 0; } @@ -2106,6 +2131,7 @@ #endif urb->hcpriv = NULL; + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use (usb_dev); urb->dev = NULL; if (urb->complete) @@ -2118,13 +2144,16 @@ static int rh_unlink_urb (struct urb * urb) { ohci_t * ohci = urb->dev->bus->hcpriv; + unsigned int flags; + spin_lock_irqsave(&ohci->ohci_lock, flags); if (ohci->rh.urb == urb) { ohci->rh.send = 0; del_timer (&ohci->rh.rh_int_timer); ohci->rh.urb = NULL; urb->hcpriv = NULL; + spin_unlock_irqrestore(&ohci->ohci_lock, flags); usb_dec_dev_use(urb->dev); urb->dev = NULL; if (urb->transfer_flags & USB_ASYNC_UNLINK) { @@ -2133,6 +2162,8 @@ urb->complete (urb); } else urb->status = -ENOENT; + } else { + spin_unlock_irqrestore(&ohci->ohci_lock, flags); } return 0; } @@ -2276,7 +2307,7 @@ static void check_timeouts (struct ohci *ohci) { - spin_lock (&usb_ed_lock); + spin_lock (&ohci->ohci_lock); while (!list_empty (&ohci->timeout_list)) { struct urb *urb; @@ -2289,15 +2320,15 @@ continue; urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; - spin_unlock (&usb_ed_lock); + spin_unlock (&ohci->ohci_lock); // outside the interrupt handler (in a timer...) // this reference would race interrupts sohci_unlink_urb (urb); - spin_lock (&usb_ed_lock); + spin_lock (&ohci->ohci_lock); } - spin_unlock (&usb_ed_lock); + spin_unlock (&ohci->ohci_lock); } @@ -2311,6 +2342,8 @@ struct ohci_regs * regs = ohci->regs; int ints; + spin_lock (&ohci->ohci_lock); + /* avoid (slow) readl if only WDH happened */ if ((ohci->hcca->done_head != 0) && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { @@ -2319,11 +2352,13 @@ /* cardbus/... hardware gone before remove() */ } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) { ohci->disabled++; + spin_unlock (&ohci->ohci_lock); err ("%s device removed!", ohci->ohci_dev->slot_name); return; /* interrupt for some other device? */ } else if ((ints &= readl (®s->intrenable)) == 0) { + spin_unlock (&ohci->ohci_lock); return; } @@ -2374,6 +2409,13 @@ } } + /* + * Finally, we are done with trashing about our hardware lists + * and other CPUs are allowed in. The festive flipping of the lock + * ensues as we struggle with the check_timeouts disaster. + */ + spin_unlock (&ohci->ohci_lock); + if (!list_empty (&ohci->timeout_list)) { check_timeouts (ohci); // FIXME: enable SF as needed in a timer; @@ -2386,6 +2428,8 @@ writel (ints, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->intrenable); (void)readl (®s->intrdisable); /* PCI posting flush */ + + ohci_complete(ohci); } /*-------------------------------------------------------------------------*/ @@ -2418,10 +2462,8 @@ ohci->ohci_dev = dev; pci_set_drvdata(dev, ohci); - INIT_LIST_HEAD (&ohci->ohci_hcd_list); - list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); - INIT_LIST_HEAD (&ohci->timeout_list); + spin_lock_init(&ohci->ohci_lock); ohci->bus = usb_alloc_bus (&sohci_device_operations); if (!ohci->bus) { @@ -2465,9 +2507,6 @@ usb_free_bus (ohci->bus); } - list_del (&ohci->ohci_hcd_list); - INIT_LIST_HEAD (&ohci->ohci_hcd_list); - ohci_mem_cleanup (ohci); /* unmap the IO address space */ @@ -2713,12 +2752,12 @@ ohci->sleeping = 1; /* First stop processing */ - spin_lock_irqsave (&usb_ed_lock, flags); + spin_lock_irqsave (&ohci->ohci_lock, flags); ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); writel (ohci->hc_control, &ohci->regs->control); writel (OHCI_INTR_SF, &ohci->regs->intrstatus); (void) readl (&ohci->regs->intrstatus); - spin_unlock_irqrestore (&usb_ed_lock, flags); + spin_unlock_irqrestore (&ohci->ohci_lock, flags); /* Wait a frame or two */ mdelay(1); @@ -2844,7 +2883,7 @@ mdelay (3); /* Then re-enable operations */ - spin_lock_irqsave (&usb_ed_lock, flags); + spin_lock_irqsave (&ohci->ohci_lock, flags); ohci->disabled = 0; ohci->sleeping = 0; ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; @@ -2860,7 +2899,6 @@ /* Check for a pending done list */ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); (void) readl (&ohci->regs->intrdisable); - spin_unlock_irqrestore (&usb_ed_lock, flags); #ifdef CONFIG_PMAC_PBOOK if (_machine == _MACH_Pmac) enable_irq (ohci->irq); @@ -2870,6 +2908,7 @@ writel (OHCI_INTR_WDH, &ohci->regs->intrenable); writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + spin_unlock_irqrestore (&ohci->ohci_lock, flags); break; default: diff -urN -X dontdiff linux-2.4.21-rc1/drivers/usb/host/usb-ohci.h linux-2.4.21-rc1-nip/drivers/usb/host/usb-ohci.h --- linux-2.4.21-rc1/drivers/usb/host/usb-ohci.h 2003-04-24 11:43:35.000000000 -0700 +++ linux-2.4.21-rc1-nip/drivers/usb/host/usb-ohci.h 2003-05-09 13:47:25.000000000 -0700 @@ -384,12 +384,12 @@ #define OHCI_QUIRK_SUCKYIO 0x02 /* NSC superio */ struct ohci_regs * regs; /* OHCI controller's memory */ - struct list_head ohci_hcd_list; /* list of all ohci_hcd */ struct list_head timeout_list; // struct list_head urb_list; // list of all pending urbs - // spinlock_t urb_list_lock; // lock to keep consistency - + spinlock_t ohci_lock; /* Covers all fields up & down */ + struct urb *complete_head, *complete_tail; + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ ed_t * ed_bulktail; /* last endpoint of bulk list */ diff -urN -X dontdiff linux-2.4.21-rc1/drivers/usb/host/usb-uhci.c linux-2.4.21-rc1-nip/drivers/usb/host/usb-uhci.c --- linux-2.4.21-rc1/drivers/usb/host/usb-uhci.c 2003-04-24 10:52:56.000000000 -0700 +++ linux-2.4.21-rc1-nip/drivers/usb/host/usb-uhci.c 2003-04-29 12:43:28.000000000 -0700 @@ -2430,9 +2430,9 @@ _static int process_interrupt (uhci_t *s, struct urb *urb) { - int i, ret = -EINPROGRESS; + int ret = -EINPROGRESS; urb_priv_t *urb_priv = urb->hcpriv; - struct list_head *p = urb_priv->desc_list.next; + struct list_head *p; uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); int actual_length; @@ -2440,8 +2440,8 @@ //dbg("urb contains interrupt request"); - for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) - { + // Maybe we allow more than one TD later ;-) + while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { desc = list_entry (p, uhci_desc_t, desc_list); if (is_td_active(desc)) { - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |