mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: David Brownell <david-b@pacbell.net>
To: linux-usb-devel@lists.sourceforge.net,
	lkml <linux-kernel@vger.kernel.org>
Subject: PATCHES:  usb with CONFIG_SLAB_DEBUG (using new pci_pool API)
Date: Fri, 23 Mar 2001 11:57:31 -0800	[thread overview]
Message-ID: <189101c0b3d3$7f732da0$6800000a@brownell.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 1267 bytes --]

Hi,

Here are updated patches getting usb-ohci and usb-uhci to
behave on an ac20 kernel with slab debugging enabled.
It uses the pci_pool API, discussed earlier.

- pcipool-0323.patch ... adds pci_pool apis to <linux/pci.h>;
    bugfixes vs what I sent to linux-usb-devel yesterday

- ohci-0323.patch ... basically what I sent yesterday

- uhci-0323.patch ... bugfixes vs what I sent yesterday;
    adds a fix for an unrelated oops (referencing an urb
    after the completion function freed it)

I don't know about any problems with these, but that doesn't
mean they're not lurking.  Converting usb-ohci to use the rest
of the pci dma mapping APIs is being done separately, and
I think Johannes has an update pending to "uhci.c".  The EHCI
host controller driver (latest) is also using "pci_pool".

Does anyone want particularly want to see further discussion
about the pci_pool API?  This version dropped the explicit
address mapping primitive that was contentious last time,
and nothing else appeared to be troublesome.  (Shouldn't be!)
This version does add another pci_pool_create() parameter,
as needed to handle a 4KB-crossing restriction for ehci.  That
verges on too many arguments, any more and I'd want them
wrapped into a (readonly) structure.

- Dave




[-- Attachment #2: pcipool-0323.patch --]
[-- Type: application/octet-stream, Size: 10816 bytes --]

--- include/linux/pci-orig.h	Wed Mar 14 12:40:44 2001
+++ include/linux/pci.h	Thu Mar 22 14:06:46 2001
@@ -553,6 +553,14 @@
 struct pci_driver *pci_dev_driver(const struct pci_dev *);
 const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev);
 
+/* kmem_cache style wrapper around pci_alloc_consistent() */
+struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
+		size_t size, size_t align, size_t allocation, int flags);
+void pci_pool_destroy (struct pci_pool *pool);
+
+void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
+void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr);
+
 #endif /* CONFIG_PCI */
 
 /* Include architecture-dependent settings and functions */
--- drivers/pci/pci-orig.c	Wed Mar 14 12:40:27 2001
+++ drivers/pci/pci.c	Fri Mar 23 10:12:46 2001
@@ -1339,6 +1339,344 @@
 }
 #endif
 
+/*
+ * Pool allocator ... useful for many drivers.  This wraps the core
+ * pci_alloc_consistent allocator, so small blocks can easily be used
+ * by drivers for bus mastering controllers.  This should probably be
+ * sharing the guts of the slab allocator.
+ */
+
+struct pci_pool {	/* the pool */
+	struct list_head	page_list;
+	spinlock_t		lock;
+	size_t			blocks_per_page;
+	size_t			size;
+	int			flags;
+	struct pci_dev		*dev;
+	size_t			allocation;
+	char			name [32];
+	wait_queue_head_t	waitq;
+};
+
+struct pci_page {	/* cacheable header for 'allocation' bytes */
+	struct list_head	page_list;
+	void			*vaddr;
+	dma_addr_t		dma;
+	unsigned long		bitmap [0];
+};
+
+#define	POOL_TIMEOUT_JIFFIES	((100 /* msec */ * HZ) / 1000)
+#define	POOL_POISON_BYTE	0x97
+
+// #define CONFIG_PCIPOOL_DEBUG
+
+
+/**
+ * pci_pool_create - Creates a pool of pci consistent memory blocks, for dma.
+ * @name: name of pool, for diagnostics
+ * @pdev: pci device that will be doing the DMA
+ * @size: size of the blocks in this pool.
+ * @align: alignment requirement for blocks; must be a power of two
+ * @allocation: returned blocks won't cross this boundary (or zero)
+ * @flags: SLAB_* (or GFP_*) flags (not all are supported).
+ *
+ * Returns a pci allocation pool with the requested characteristics, or
+ * null if one can't be created.  Given one of these pools, pci_pool_alloc()
+ * may be used to allocate memory.  Such memory will all have "consistent"
+ * DMA mappings, accessible by the device and its driver without using
+ * cache flushing primitives.  The actual size of blocks allocated may be
+ * larger than requested because of alignment.
+ *
+ * If allocation is nonzero, objects returned from pci_pool_alloc() won't
+ * cross that size boundary.  This may be useful for devices which have
+ * addressing restrictions on individual DMA transfers, such as not crossing
+ * boundaries of 4KBytes.
+ */
+struct pci_pool *
+pci_pool_create (const char *name, struct pci_dev *pdev,
+	size_t size, size_t align, size_t allocation, int flags)
+{
+	struct pci_pool		*retval;
+
+	if (size == 0)
+		return 0;
+	else if (size < align)
+		size = align;
+	else if ((size % align) != 0) {
+		size += align + 1;
+		size &= ~(align - 1);
+	}
+
+	if (allocation == 0) {
+		if (PAGE_SIZE < size)
+			allocation = size;
+		else
+			allocation = PAGE_SIZE;
+		// FIXME: round up for less fragmentation
+	} else if (allocation < size)
+		return 0;
+
+	if (!(retval = kmalloc (sizeof *retval, flags)))
+		return retval;
+
+#ifdef	CONFIG_PCIPOOL_DEBUG
+	flags |= SLAB_POISON;
+#endif
+
+	strncpy (retval->name, name, sizeof retval->name);
+	retval->name [sizeof retval->name - 1] = 0;
+
+	retval->dev = pdev;
+	INIT_LIST_HEAD (&retval->page_list);
+	spin_lock_init (&retval->lock);
+	retval->size = size;
+	retval->flags = flags;
+	retval->allocation = allocation;
+	retval->blocks_per_page = allocation / size;
+	init_waitqueue_head (&retval->waitq);
+
+#ifdef CONFIG_PCIPOOL_DEBUG
+	printk (KERN_DEBUG "pcipool create %s/%s size %d, %d/page (%d alloc)\n",
+		pdev->slot_name, retval->name, size,
+		retval->blocks_per_page, allocation);
+#endif
+
+	return retval;
+}
+
+
+static struct pci_page *
+pool_alloc_page (struct pci_pool *pool, int mem_flags)
+{
+	struct pci_page	*page;
+	int		mapsize;
+
+	mapsize = pool->blocks_per_page;
+	mapsize = (mapsize + BITS_PER_LONG - 1) / BITS_PER_LONG;
+	mapsize *= sizeof (long);
+
+	page = (struct pci_page *) kmalloc (mapsize + sizeof *page, mem_flags);
+	if (!page)
+		return 0;
+	page->vaddr = pci_alloc_consistent (pool->dev,
+				pool->allocation, &page->dma);
+	if (page->vaddr) {
+		memset (page->bitmap, ~0, mapsize);	// bit set == free
+		if (pool->flags & SLAB_POISON)
+			memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
+		list_add (&page->page_list, &pool->page_list);
+	}
+	return page;
+}
+
+
+static inline int
+is_page_busy (int blocks, unsigned long *bitmap)
+{
+	while (blocks > 0) {
+		if (*bitmap++ != ~0)
+			return 1;
+		blocks -= BITS_PER_LONG;
+	}
+	return 0;
+}
+
+static void
+pool_free_page (struct pci_pool *pool, struct pci_page *page)
+{
+	dma_addr_t	dma = page->dma;
+
+#ifdef CONFIG_PCIPOOL_DEBUG
+	if (is_page_busy (pool->blocks_per_page, page->bitmap)) {
+		printk (KERN_ERR "pcipool %s/%s, free_page %p busy; leaked\n",
+			pool->dev->slot_name, pool->name, page->vaddr);
+		goto done;
+	}
+#endif
+
+	if (pool->flags & SLAB_POISON)
+		memset (page->vaddr, POOL_POISON_BYTE, pool->allocation);
+	pci_free_consistent (pool->dev, pool->allocation, page->vaddr, dma);
+done:
+	list_del (&page->page_list);
+	kfree (page);
+}
+
+
+/**
+ * pci_pool_destroy - destroys a pool of pci memory blocks.
+ * @pool: pci pool that will be destroyed
+ *
+ * Caller guarantees that no more memory from the pool is in use,
+ * and that nothing will try to use the pool after this call.
+ */
+void
+pci_pool_destroy (struct pci_pool *pool)
+{
+	unsigned long		flags;
+
+#ifdef CONFIG_PCIPOOL_DEBUG
+	printk (KERN_DEBUG "pcipool destroy %s/%s\n",
+		pool->dev->slot_name, pool->name);
+#endif
+
+	spin_lock_irqsave (&pool->lock, flags);
+	while (!list_empty (&pool->page_list)) {
+		struct pci_page		*page;
+		page = list_entry (pool->page_list.next,
+				struct pci_page, page_list);
+		pool_free_page (pool, page);
+	}
+	spin_unlock_irqrestore (&pool->lock, flags);
+	kfree (pool);
+}
+
+
+/**
+ * pci_pool_alloc - get a block of consistent memory
+ * @pool: pci pool that will produce the block
+ * @mem_flags: GFP_KERNEL or GFP_ATOMIC
+ * @handle: pointer to dma address of block
+ *
+ * This returns the kernel virtual address of a currently unused block,
+ * and reports its dma address through the handle.
+ */
+void *
+pci_pool_alloc (struct pci_pool *pool, int mem_flags, dma_addr_t *handle)
+{
+	unsigned long		flags;
+	struct list_head	*entry;
+	struct pci_page		*page;
+	int			map, block;
+	size_t			offset;
+	void			*retval;
+
+restart:
+	spin_lock_irqsave (&pool->lock, flags);
+	list_for_each (entry, &pool->page_list) {
+		int		i;
+		page = list_entry (entry, struct pci_page, page_list);
+		/* only cachable accesses here ... */
+		for (map = 0, i = 0;
+				i < pool->blocks_per_page;
+				i += BITS_PER_LONG, map++) {
+			if (page->bitmap [map] == 0)
+				continue;
+			block = ffs (page->bitmap [map]);
+			if ((i + block) <= pool->blocks_per_page) {
+				block--;
+				clear_bit (block, &page->bitmap [map]);
+				offset = (BITS_PER_LONG * map) + block;
+				offset *= pool->size;
+				goto ready;
+			}
+		}
+	}
+	if (!(page = pool_alloc_page (pool, mem_flags))) {
+		if (mem_flags == GFP_KERNEL) {
+			DECLARE_WAITQUEUE (wait, current);
+
+			current->state = TASK_INTERRUPTIBLE;
+			add_wait_queue (&pool->waitq, &wait);
+			spin_unlock_irqrestore (&pool->lock, flags);
+
+			schedule_timeout (POOL_TIMEOUT_JIFFIES);
+
+			current->state = TASK_RUNNING;
+			remove_wait_queue (&pool->waitq, &wait);
+			goto restart;
+		}
+		retval = 0;
+		goto done;
+	}
+
+	clear_bit (0, &page->bitmap [0]);
+	offset = 0;
+ready:
+	retval = offset + page->vaddr;
+	*handle = offset + page->dma;
+done:
+	spin_unlock_irqrestore (&pool->lock, flags);
+	return retval;
+}
+
+
+static struct pci_page *
+pool_find_page (struct pci_pool *pool, dma_addr_t dma)
+{
+	unsigned long		flags;
+	struct list_head	*entry;
+	struct pci_page		*page;
+
+	spin_lock_irqsave (&pool->lock, flags);
+	list_for_each (entry, &pool->page_list) {
+		page = list_entry (entry, struct pci_page, page_list);
+		if (dma < page->dma)
+			continue;
+		if (dma < (page->dma + pool->allocation))
+			goto done;
+	}
+	page = 0;
+done:
+	spin_unlock_irqrestore (&pool->lock, flags);
+	return page;
+}
+
+
+/**
+ * pci_pool_free - put block back into pci pool
+ * @pool: the pci pool holding the block
+ * @vaddr: virtual address of block
+ * @dma: dma address of block
+ *
+ * Caller promises neither device nor driver will again touch this block
+ * unless it is first re-allocated.
+ */
+void
+pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t dma)
+{
+	struct pci_page		*page;
+	unsigned long		flags;
+	int			map, block;
+
+	if ((page = pool_find_page (pool, dma)) == 0) {
+		printk (KERN_ERR "pci_pool_free %s/%s, %p/%x (bad dma)\n",
+			pool->dev->slot_name, pool->name, vaddr, dma);
+		return;
+	}
+#ifdef	CONFIG_PCIPOOL_DEBUG
+	if (((dma - page->dma) + (void *)page->vaddr) != vaddr) {
+		printk (KERN_ERR "pci_pool_free %s/%s, %p (bad vaddr)/%x\n",
+			pool->dev->slot_name, pool->name, vaddr, dma);
+		return;
+	}
+#endif
+
+	block = dma - page->dma;
+	block /= pool->size;
+	map = block / BITS_PER_LONG;
+	block %= BITS_PER_LONG;
+
+#ifdef	CONFIG_PCIPOOL_DEBUG
+	if (page->bitmap [map] & (1 << block)) {
+		printk (KERN_ERR "pci_pool_free %s/%s, dma %x already free\n",
+			pool->dev->slot_name, pool->name, dma);
+		return;
+	}
+#endif
+	if (pool->flags & SLAB_POISON)
+		memset (vaddr, POOL_POISON_BYTE, pool->size);
+
+	spin_lock_irqsave (&pool->lock, flags);
+	set_bit (block, &page->bitmap [map]);
+	if (waitqueue_active (&pool->waitq))
+		wake_up (&pool->waitq);
+	else if (!is_page_busy (pool->blocks_per_page, page->bitmap))
+		pool_free_page (pool, page);
+	spin_unlock_irqrestore (&pool->lock, flags);
+}
+
+
 void __init pci_init(void)
 {
 	struct pci_dev *dev;
@@ -1420,4 +1758,11 @@
 
 EXPORT_SYMBOL(isa_dma_bridge_buggy);
 EXPORT_SYMBOL(pci_pci_problems);
+
+/* Pool allocator (layer over pci_alloc_consistent) */
+
+EXPORT_SYMBOL (pci_pool_create);
+EXPORT_SYMBOL (pci_pool_destroy);
+EXPORT_SYMBOL (pci_pool_alloc);
+EXPORT_SYMBOL (pci_pool_free);
 

[-- Attachment #3: ohci-0323.patch --]
[-- Type: application/octet-stream, Size: 14879 bytes --]

--- drivers/usb/usb-ohci-orig.h	Wed Mar 14 12:40:36 2001
+++ drivers/usb/usb-ohci.h	Fri Mar 23 10:13:17 2001
@@ -39,7 +39,7 @@
 #define ED_URB_DEL  	0x08
 
 /* usb_ohci_ed */
-typedef struct ed {
+struct ed {
 	__u32 hwINFO;       
 	__u32 hwTailP;
 	__u32 hwHeadP;
@@ -53,9 +53,12 @@
 	__u8 state;
 	__u8 type; 
 	__u16 last_iso;
-    struct ed * ed_rm_list;
-   
-} ed_t;
+	struct ed * ed_rm_list;
+
+	dma_addr_t dma;
+	__u32 unused[3];
+} __attribute((aligned(16)));
+typedef struct ed ed_t;
 
  
 /* TD info field */
@@ -96,19 +99,23 @@
 
 #define MAXPSW 1
 
-typedef struct td { 
+struct td {
 	__u32 hwINFO;
   	__u32 hwCBP;		/* Current Buffer Pointer */
   	__u32 hwNextTD;		/* Next TD Pointer */
   	__u32 hwBE;		/* Memory Buffer End Pointer */
-  	__u16 hwPSW[MAXPSW];
 
+  	__u16 hwPSW[MAXPSW];
   	__u8 unused;
   	__u8 index;
   	struct ed * ed;
   	struct td * next_dl_td;
   	urb_t * urb;
-} td_t;
+
+	dma_addr_t td_dma;
+	__u32 unused2[3];
+} __attribute((aligned(16)));
+typedef struct td td_t;
 
 
 #define OHCI_ED_SKIP	(1 << 14)
@@ -121,7 +128,7 @@
  
 #define NUM_INTS 32	/* part of the OHCI standard */
 struct ohci_hcca {
-    __u32	int_table[NUM_INTS];	/* Interrupt ED table */
+	__u32	int_table[NUM_INTS];	/* Interrupt ED table */
 	__u16	frame_no;		/* current frame number */
 	__u16	pad1;			/* set to 0 on each frame_no change */
 	__u32	done_head;		/* info returned for an interrupt */
@@ -356,7 +363,7 @@
 	struct ohci_regs * regs;	/* OHCI controller's memory */
 	struct list_head ohci_hcd_list;	/* list of all ohci_hcd */
 
-	struct ohci * next; 		// chain of uhci device contexts
+	struct ohci * next; 		// chain of ohci device contexts
 	// struct list_head urb_list; 	// list of all pending urbs
 	// spinlock_t urb_list_lock; 	// lock to keep consistency 
   
@@ -371,17 +378,18 @@
 	struct usb_device * dev[128];
 	struct virt_root_hub rh;
 
-	/* PCI device handle and settings */
+	/* PCI device handle, settings, ... */
 	struct pci_dev	*ohci_dev;
 	u8		pci_latency;
+	struct pci_pool	*td_cache;
+	struct pci_pool	*dev_cache;
 } ohci_t;
 
-
-#define NUM_TDS	0		/* num of preallocated transfer descriptors */
 #define NUM_EDS 32		/* num of preallocated endpoint descriptors */
 
 struct ohci_device {
 	ed_t 	ed[NUM_EDS];
+	dma_addr_t dma;
 	int ed_cnt;
 	wait_queue_head_t * wait;
 };
@@ -393,7 +401,7 @@
 /* endpoint */
 static int ep_link(ohci_t * ohci, ed_t * ed);
 static int ep_unlink(ohci_t * ohci, ed_t * ed);
-static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load, int mem_flags);
 static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed);
 /* td */
 static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index);
@@ -406,97 +414,91 @@
 /*-------------------------------------------------------------------------*/
 
 #define ALLOC_FLAGS (in_interrupt () ? GFP_ATOMIC : GFP_KERNEL)
- 
-#ifdef OHCI_MEM_SLAB
-#define	__alloc(t,c) kmem_cache_alloc(c,ALLOC_FLAGS)
-#define	__free(c,x) kmem_cache_free(c,x)
-static kmem_cache_t *td_cache, *ed_cache;
 
-/*
- * WARNING:  do NOT use this with "forced slab debug"; it won't respect
- * our hardware alignment requirement.
- */
-#ifndef OHCI_MEM_FLAGS
-#define	OHCI_MEM_FLAGS 0
+#ifdef DEBUG
+#	define OHCI_MEM_FLAGS	SLAB_POISON
+#else
+#	define OHCI_MEM_FLAGS	0
+#endif
+ 
+#ifndef CONFIG_PCI
+#	error "usb-ohci currently requires PCI-based controllers"
+	/* to support non-PCI OHCIs, you need custom bus/mem/... glue */
 #endif
 
-static int ohci_mem_init (void)
+static int ohci_mem_init (struct ohci *ohci)
 {
-	/* redzoning (or forced debug!) breaks alignment */
-	int	flags = (OHCI_MEM_FLAGS) & ~SLAB_RED_ZONE;
-
-	/* TDs accessed by controllers and host */
-	td_cache = kmem_cache_create ("ohci_td", sizeof (struct td), 0,
-		flags | SLAB_HWCACHE_ALIGN, NULL, NULL);
-	if (!td_cache) {
-		dbg ("no TD cache?");
+	ohci->td_cache = pci_pool_create ("ohci_td", ohci->ohci_dev,
+		sizeof (struct td),
+		16 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->td_cache)
 		return -ENOMEM;
-	}
-
-	/* EDs are accessed by controllers and host;  dev part is host-only */
-	ed_cache = kmem_cache_create ("ohci_ed", sizeof (struct ohci_device), 0,
-		flags | SLAB_HWCACHE_ALIGN, NULL, NULL);
-	if (!ed_cache) {
-		dbg ("no ED cache?");
-		kmem_cache_destroy (td_cache);
-		td_cache = 0;
+	ohci->dev_cache = pci_pool_create ("ohci_dev", ohci->ohci_dev,
+		sizeof (struct ohci_device),
+		16 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		GFP_KERNEL | OHCI_MEM_FLAGS);
+	if (!ohci->dev_cache)
 		return -ENOMEM;
-	}
-	dbg ("slab flags 0x%x", flags);
 	return 0;
 }
 
-static void ohci_mem_cleanup (void)
+static void ohci_mem_cleanup (struct ohci *ohci)
 {
-	if (ed_cache && kmem_cache_destroy (ed_cache))
-		err ("ed_cache remained");
-	ed_cache = 0;
-
-	if (td_cache && kmem_cache_destroy (td_cache))
-		err ("td_cache remained");
-	td_cache = 0;
+	if (ohci->td_cache) {
+		pci_pool_destroy (ohci->td_cache);
+		ohci->td_cache = 0;
+	}
+	if (ohci->dev_cache) {
+		pci_pool_destroy (ohci->dev_cache);
+		ohci->dev_cache = 0;
+	}
 }
 
-#else
-#define	__alloc(t,c) kmalloc(sizeof(t),ALLOC_FLAGS)
-#define	__free(dev,x) kfree(x)
-#define td_cache 0
-#define ed_cache 0
-
-static inline int ohci_mem_init (void) { return 0; }
-static inline void ohci_mem_cleanup (void) { return; }
-
-/* FIXME: pci_consistent version */
-
-#endif
-
-
 /* TDs ... */
 static inline struct td *
-td_alloc (struct ohci *hc)
+td_alloc (struct ohci *hc, int mem_flags)
 {
-	struct td *td = (struct td *) __alloc (struct td, td_cache);
+	dma_addr_t	dma;
+	struct td	*td;
+
+	td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
+	if (td)
+		td->td_dma = dma;
 	return td;
 }
 
 static inline void
 td_free (struct ohci *hc, struct td *td)
 {
-	__free (td_cache, td);
+	pci_pool_free (hc->td_cache, td, td->td_dma);
 }
 
 
 /* DEV + EDs ... only the EDs need to be consistent */
 static inline struct ohci_device *
-dev_alloc (struct ohci *hc)
+dev_alloc (struct ohci *hc, int mem_flags)
 {
-	struct ohci_device *dev = (struct ohci_device *)
-		__alloc (struct ohci_device, ed_cache);
+	dma_addr_t		dma;
+	struct ohci_device	*dev;
+	int			i, offset;
+
+	dev = pci_pool_alloc (hc->dev_cache, mem_flags, &dma);
+	if (dev) {
+		memset (dev, 0, sizeof (*dev));
+		dev->dma = dma;
+		offset = ((char *)&dev->ed) - ((char *)dev);
+		for (i = 0; i < NUM_EDS; i++, offset += sizeof dev->ed [0])
+			dev->ed [i].dma = dma + offset;
+	}
 	return dev;
 }
 
 static inline void
-dev_free (struct ohci_device *dev)
+dev_free (struct ohci *hc, struct ohci_device *dev)
 {
-	__free (ed_cache, dev);
+	pci_pool_free (hc->dev_cache, dev, dev->dma);
 }
+
--- drivers/usb/usb-ohci-orig.c	Wed Mar 14 12:40:36 2001
+++ drivers/usb/usb-ohci.c	Wed Mar 21 17:33:06 2001
@@ -12,11 +12,12 @@
  * 
  * History:
  * 
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
  * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
  * 2000/09/26 fixed races in removing the private portion of the urb
  * 2000/09/07 disable bulk and control lists when unlinking the last
  *	endpoint descriptor in order to avoid unrecoverable errors on
- *	the Lucent chips.
+ *	the Lucent chips. (rwc@sgi)
  * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
  *	urb unlink probs, indentation fixes
  * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
@@ -65,8 +66,6 @@
 
 #define OHCI_USE_NPS		// force NoPowerSwitching mode
 // #define OHCI_VERBOSE_DEBUG	/* not always helpful */
-// #define OHCI_MEM_SLAB
-// #define OHCI_MEM_FLAGS	SLAB_POISON	/* no redzones; see mm/slab.c */
 
 #include "usb-ohci.h"
 
@@ -132,7 +131,7 @@
 			}
 		}
 
-		urb_free_priv ((struct ohci *)urb->dev->bus, urb_priv);
+		urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
 		usb_dec_dev_use (urb->dev);
 		urb->dev = NULL;
 	}
@@ -460,6 +459,7 @@
 	int i, size = 0;
 	unsigned long flags;
 	int bustime = 0;
+	int mem_flags = ALLOC_FLAGS;
 	
 	if (!urb->dev || !urb->dev->bus)
 		return -ENODEV;
@@ -489,7 +489,7 @@
 	}
 
 	/* every endpoint has a ed, locate and fill it */
-	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) {
+	if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
 		usb_dec_dev_use (urb->dev);	
 		return -ENOMEM;
 	}
@@ -534,8 +534,9 @@
 
 	/* allocate the TDs */
 	for (i = 0; i < size; i++) { 
-		urb_priv->td[i] = td_alloc (ohci);
+		urb_priv->td[i] = td_alloc (ohci, mem_flags);
 		if (!urb_priv->td[i]) {
+			urb_priv->length = i;
 			urb_free_priv (ohci, urb_priv);
 			usb_dec_dev_use (urb->dev);	
 			return -ENOMEM;
@@ -687,18 +688,11 @@
 {
 	struct ohci_device * dev;
 
-	/* FIXME:  ED allocation with pci_consistent memory
-	 * must know the controller ... either pass it in here,
-	 * or decouple ED allocation from dev allocation.
-	 */
-	dev = dev_alloc (NULL);
+	dev = dev_alloc ((struct ohci *) usb_dev->bus->hcpriv, ALLOC_FLAGS);
 	if (!dev)
 		return -ENOMEM;
-		
-	memset (dev, 0, sizeof (*dev));
 
 	usb_dev->hcpriv = dev;
-
 	return 0;
 }
 
@@ -783,7 +777,7 @@
 	}
 
 	/* free device, and associated EDs */
-	dev_free (dev);
+	dev_free (ohci, dev);
 
 	return 0;
 }
@@ -877,9 +871,9 @@
 	case PIPE_CONTROL:
 		ed->hwNextED = 0;
 		if (ohci->ed_controltail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_controlhead);
+			writel (ed->dma, &ohci->regs->ed_controlhead);
 		} else {
-			ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_controltail;
 		if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
@@ -893,9 +887,9 @@
 	case PIPE_BULK:
 		ed->hwNextED = 0;
 		if (ohci->ed_bulktail == NULL) {
-			writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead);
+			writel (ed->dma, &ohci->regs->ed_bulkhead);
 		} else {
-			ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
 		}
 		ed->ed_prev = ohci->ed_bulktail;
 		if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
@@ -920,7 +914,7 @@
 				ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
 					inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
 			ed->hwNextED = *ed_p; 
-			*ed_p = cpu_to_le32 (virt_to_bus (ed));
+			*ed_p = cpu_to_le32 (ed->dma);
 		}
 #ifdef DEBUG
 		ep_print_int_eds (ohci, "LINK_INT");
@@ -931,7 +925,7 @@
 		ed->hwNextED = 0;
 		ed->int_interval = 1;
 		if (ohci->ed_isotail != NULL) {
-			ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed));
+			ohci->ed_isotail->hwNextED = cpu_to_le32 (ed->dma);
 			ed->ed_prev = ohci->ed_isotail;
 		} else {
 			for ( i = 0; i < 32; i += inter) {
@@ -940,7 +934,7 @@
 					*ed_p != 0; 
 					ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) 
 						inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval);
-				*ed_p = cpu_to_le32 (virt_to_bus (ed));	
+				*ed_p = cpu_to_le32 (ed->dma);	
 			}	
 			ed->ed_prev = NULL;
 		}	
@@ -1066,7 +1060,13 @@
  * in all other cases the state is left unchanged
  * the ed info fields are setted anyway even though most of them should not change */
  
-static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load)
+static ed_t * ep_add_ed (
+	struct usb_device * usb_dev,
+	unsigned int pipe,
+	int interval,
+	int load,
+	int mem_flags
+)
 {
    	ohci_t * ohci = usb_dev->bus->hcpriv;
 	td_t * td;
@@ -1089,13 +1089,13 @@
 	if (ed->state == ED_NEW) {
 		ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */
   		/* dummy td; end of td list for ed */
-		td = td_alloc (ohci);
+		td = td_alloc (ohci, mem_flags);
   		if (!td) {
 			/* out of memory */
 			spin_unlock_irqrestore (&usb_ed_lock, flags);
 			return NULL;
 		}
-		ed->hwTailP = cpu_to_le32 (virt_to_bus (td));
+		ed->hwTailP = cpu_to_le32 (td->td_dma);
 		ed->hwHeadP = ed->hwTailP;	
 		ed->state = ED_UNLINK;
 		ed->type = usb_pipetype (pipe);
@@ -1164,7 +1164,7 @@
  * TD handling functions
  *-------------------------------------------------------------------------*/
 
-/* prepare a TD */
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
 
 static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index)
 {
@@ -1176,7 +1176,10 @@
 		return;
 	}
 	
+	/* use this td as the next dummy */
 	td_pt = urb_priv->td [index];
+	td_pt->hwNextTD = 0;
+
 	/* fill the old dummy TD */
 	td = urb_priv->td [index] = (td_t *)
 		bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0);
@@ -1198,9 +1201,11 @@
 	td->hwBE = cpu_to_le32 ((!data || !len )
 		? 0
 		: virt_to_bus (data + len - 1));
-	td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt));
+	td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+
 	td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000);
-	td_pt->hwNextTD = 0;
+
+	/* append to queue */
 	td->ed->hwTailP = td->hwNextTD;
 }
 
@@ -1765,9 +1770,6 @@
 	wIndex        = le16_to_cpu (cmd->index);
 	wLength       = le16_to_cpu (cmd->length);
 
-	dbg ("rh_submit_urb, req = %d(%x) len=%d", bmRType_bReq,
-		bmRType_bReq, wLength);
-
 	switch (bmRType_bReq) {
 	/* Request Destination:
 	   without flags: Device, 
@@ -2199,6 +2201,8 @@
 
 	list_del (&ohci->ohci_hcd_list);
 	INIT_LIST_HEAD (&ohci->ohci_hcd_list);
+
+	ohci_mem_cleanup (ohci);
     
 	/* unmap the IO address space */
 	iounmap (ohci->regs);
@@ -2221,6 +2225,7 @@
 	ohci_t * ohci;
 	u8 latency, limit;
 	char buf[8], *bufp = buf;
+	int ret;
 
 #ifndef __sparc__
 	sprintf(buf, "%d", irq);
@@ -2235,6 +2240,10 @@
 	if (!ohci) {
 		return -ENOMEM;
 	}
+	if ((ret = ohci_mem_init (ohci)) < 0) {
+		hc_release_ohci (ohci);
+		return ret;
+	}
 
 	/* bad pci latencies can contribute to overruns */ 
 	pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
@@ -2566,11 +2575,7 @@
 {
 	int ret;
 
-	if ((ret = ohci_mem_init ()) < 0)
-		return ret;
-
 	if ((ret = pci_module_init (&ohci_pci_driver)) < 0) {
-		ohci_mem_cleanup ();
 		return ret;
 	}
 
@@ -2588,7 +2593,6 @@
 	pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
 #endif  
 	pci_unregister_driver (&ohci_pci_driver);
-	ohci_mem_cleanup ();
 }
 
 module_init (ohci_hcd_init);

[-- Attachment #4: uhci-0323.patch --]
[-- Type: application/octet-stream, Size: 14112 bytes --]

--- drivers/usb/usb-uhci-orig.h	Mon May 15 12:05:15 2000
+++ drivers/usb/usb-uhci.h	Thu Mar 22 21:39:06 2001
@@ -212,7 +212,10 @@
 	struct list_head urb_unlinked;	// list of all unlinked  urbs
 	long timeout_check;
 	int timeout_urbs;
+#ifdef	CONFIG_PCI
 	struct pci_dev *uhci_pci;
+	struct pci_pool *desc_pool;
+#endif
 } uhci_t, *puhci_t;
 
 
--- drivers/usb/usb-uhci-orig.c	Wed Mar 14 12:40:36 2001
+++ drivers/usb/usb-uhci.c	Fri Mar 23 10:12:27 2001
@@ -7,7 +7,7 @@
  *               Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
  * (c) 2000      Yggdrasil Computing, Inc. (port of new PCI interface support
  *               from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
- * (C) 2000      David Brownell, david-b@pacbell.net (usb-ohci.c)
+ * (C) 2000-2001 David Brownell, david-b@pacbell.net (pci from usb-ohci.c, pci_pool)
  *          
  * HW-initalization based on material of
  *
@@ -44,7 +44,7 @@
 //#define ISO_SANITY_CHECK
 
 /* This enables debug printks */
-#define DEBUG
+#undef DEBUG
 
 /* This enables all symbols to be exported, to ease debugging oopses */
 //#define DEBUG_SYMBOLS
@@ -58,10 +58,6 @@
 #include "usb-uhci.h"
 #include "usb-uhci-debug.h"
 
-#undef DEBUG
-#undef dbg
-#define dbg(format, arg...) do {} while (0)
-#define DEBUG_SYMBOLS
 #ifdef DEBUG_SYMBOLS
 	#define _static
 	#ifndef EXPORT_SYMTAB
@@ -75,7 +71,6 @@
 #define async_dbg dbg //err
 
 #ifdef DEBUG_SLAB
-	static kmem_cache_t *uhci_desc_kmem;
 	static kmem_cache_t *urb_priv_kmem;
 #endif
 
@@ -226,10 +221,11 @@
 
 }
 /*-------------------------------------------------------------------*/
-_static int alloc_td (uhci_desc_t ** new, int flags)
+_static int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags)
 {
-#ifdef DEBUG_SLAB
-	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
+#ifdef CONFIG_PCI
+	dma_addr_t	dma;
+	*new= pci_pool_alloc (s->desc_pool, SLAB_FLAG, &dma);
 #else
 	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif
@@ -340,10 +336,10 @@
 }
 
 /*-------------------------------------------------------------------*/
-_static int delete_desc (uhci_desc_t *element)
+_static int delete_desc (uhci_t *s, uhci_desc_t *element)
 {
-#ifdef DEBUG_SLAB
-	kmem_cache_free(uhci_desc_kmem, element);
+#ifdef CONFIG_PCI
+	pci_pool_free(s->desc_pool, element, virt_to_bus (element));
 #else
 	kfree (element);
 #endif
@@ -351,10 +347,11 @@
 }
 /*-------------------------------------------------------------------*/
 // Allocates qh element
-_static int alloc_qh (uhci_desc_t ** new)
+_static int alloc_qh (uhci_t *s, uhci_desc_t ** new)
 {
-#ifdef DEBUG_SLAB
-	*new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
+#ifdef CONFIG_PCI
+	dma_addr_t	dma;
+	*new= pci_pool_alloc (s->desc_pool, SLAB_FLAG, &dma);
 #else
 	*new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif	
@@ -439,15 +436,15 @@
 		td = list_entry (p, uhci_desc_t, vertical);
 		dbg("unlink td @ %p",td);
 		unlink_td (s, td, 0); // no physical unlink
-		delete_desc (td);
+		delete_desc (s, td);
 	}
 
-	delete_desc (qh);
+	delete_desc (s, qh);
 	
 	return 0;
 }
 /*-------------------------------------------------------------------*/
-_static void clean_td_chain (uhci_desc_t *td)
+_static void clean_td_chain (uhci_t *s, uhci_desc_t *td)
 {
 	struct list_head *p;
 	uhci_desc_t *td1;
@@ -457,10 +454,10 @@
 	
 	while ((p = td->horizontal.next) != &td->horizontal) {
 		td1 = list_entry (p, uhci_desc_t, horizontal);
-		delete_desc (td1);
+		delete_desc (s, td1);
 	}
 	
-	delete_desc (td);
+	delete_desc (s, td);
 }
 
 /*-------------------------------------------------------------------*/
@@ -485,18 +482,18 @@
 	if (s->td32ms) {
 	
 		unlink_td(s,s->td32ms,1);
-		delete_desc(s->td32ms);
+		delete_desc(s, s->td32ms);
 	}
 
 	for (n = 0; n < 8; n++) {
 		td = s->int_chain[n];
-		clean_td_chain (td);
+		clean_td_chain (s, td);
 	}
 
 	if (s->iso_td) {
 		for (n = 0; n < 1024; n++) {
 			td = s->iso_td[n];
-			clean_td_chain (td);
+			clean_td_chain (s, td);
 		}
 		kfree (s->iso_td);
 	}
@@ -519,13 +516,13 @@
 	}
 	else {
 		if (s->ls_control_chain)
-			delete_desc (s->ls_control_chain);
+			delete_desc (s, s->ls_control_chain);
 		if (s->control_chain)
-			 delete_desc(s->control_chain);
+			 delete_desc(s, s->control_chain);
 		if (s->bulk_chain)
-			delete_desc (s->bulk_chain);
+			delete_desc (s, s->bulk_chain);
 		if (s->chain_end)
-			delete_desc (s->chain_end);
+			delete_desc (s, s->chain_end);
 	}
 	dbg("cleanup_skel finished");	
 }
@@ -560,7 +557,7 @@
 	dbg("allocating iso descs");
 	for (n = 0; n < 1024; n++) {
 	 	// allocate skeleton iso/irq-tds
-		ret = alloc_td (&td, 0);
+		ret = alloc_td (s, &td, 0);
 		if (ret)
 			goto init_skel_cleanup;
 		s->iso_td[n] = td;
@@ -568,14 +565,14 @@
 	}
 
 	dbg("allocating qh: chain_end");
-	ret = alloc_qh (&qh);
+	ret = alloc_qh (s, &qh);
 	
 	if (ret)
 		goto init_skel_cleanup;
 				
 	s->chain_end = qh;
 
-	ret = alloc_td (&td, 0);
+	ret = alloc_td (s, &td, 0);
 
 	if (ret)
 		goto init_skel_cleanup;
@@ -586,7 +583,7 @@
 	s->td1ms=td;
 
 	dbg("allocating qh: bulk_chain");
-	ret = alloc_qh (&qh);
+	ret = alloc_qh (s, &qh);
 	if (ret)
 		goto init_skel_cleanup;
 	
@@ -594,7 +591,7 @@
 	s->bulk_chain = qh;
 
 	dbg("allocating qh: control_chain");
-	ret = alloc_qh (&qh);
+	ret = alloc_qh (s, &qh);
 	if (ret)
 		goto init_skel_cleanup;
 	
@@ -607,7 +604,7 @@
 #endif
 
 	dbg("allocating qh: ls_control_chain");
-	ret = alloc_qh (&qh);
+	ret = alloc_qh (s, &qh);
 	if (ret)
 		goto init_skel_cleanup;
 	
@@ -622,7 +619,7 @@
 	for (n = 0; n < 8; n++) {
 		uhci_desc_t *td;
 
-		alloc_td (&td, 0);
+		alloc_td (s, &td, 0);
 		if (!td)
 			goto init_skel_cleanup;
 		s->int_chain[n] = td;
@@ -639,7 +636,7 @@
 	for (n = 0; n < 1024; n++) {
 		// link all iso-tds to the interrupt chains
 		int m, o;
-		dbg("framelist[%i]=%x",n,s->framelist[n]);
+		// dbg("framelist[%i]=%x",n,s->framelist[n]);
 		if ((n&127)==127) 
 			((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
 		else 
@@ -648,7 +645,7 @@
 					((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
 	}
 
-	ret = alloc_td (&td, 0);
+	ret = alloc_td (s, &td, 0);
 
 	if (ret)
 		goto init_skel_cleanup;
@@ -689,12 +686,12 @@
 	}
 
 	dbg("uhci_submit_control start");
-	alloc_qh (&qh);		// alloc qh for this request
+	alloc_qh (s, &qh);		// alloc qh for this request
 
 	if (!qh)
 		return -ENOMEM;
 
-	alloc_td (&td, UHCI_PTR_DEPTH * depth_first);		// get td for setup stage
+	alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first);		// get td for setup stage
 
 	if (!td) {
 		delete_qh (s, qh);
@@ -732,7 +729,7 @@
 	while (len > 0) {
 		int pktsze = len;
 
-		alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
+		alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first);
 		if (!td) {
 			delete_qh (s, qh);
 			return -ENOMEM;
@@ -764,7 +761,7 @@
 
 	destination |= 1 << TD_TOKEN_TOGGLE;	/* End in Data1 */
 
-	alloc_td (&td, UHCI_PTR_DEPTH);
+	alloc_td (s, &td, UHCI_PTR_DEPTH);
 	
 	if (!td) {
 		delete_qh (s, qh);
@@ -829,15 +826,15 @@
 	upriv = (urb_priv_t*)urb->hcpriv;
 
 	if (!bulk_urb) {
-		alloc_qh (&qh);		// get qh for this request
+		alloc_qh (s, &qh);		// get qh for this request
 		
 		if (!qh)
 			return -ENOMEM;
 
 		if (urb->transfer_flags & USB_QUEUE_BULK) {
-			alloc_qh(&nqh); // placeholder for clean unlink
+			alloc_qh(s, &nqh); // placeholder for clean unlink
 			if (!nqh) {
-				delete_desc (qh);
+				delete_desc (s, qh);
 				return -ENOMEM;
 			}
 			upriv->next_qh = nqh;
@@ -853,12 +850,12 @@
 	}
 
 	if (urb->transfer_flags & USB_QUEUE_BULK) {
-		alloc_qh (&bqh); // "bottom" QH,
+		alloc_qh (s, &bqh); // "bottom" QH,
 		
 		if (!bqh) {
 			if (!bulk_urb) { 
-				delete_desc(qh);
-				delete_desc(nqh);
+				delete_desc(s, qh);
+				delete_desc(s, nqh);
 			}
 			return -ENOMEM;
 		}
@@ -882,7 +879,7 @@
 	do {					// TBD: Really allow zero-length packets?
 		int pktsze = len;
 
-		alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
+		alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first);
 
 		if (!td) {
 			delete_qh (s, qh);
@@ -962,9 +959,9 @@
 	uhci_desc_t *td;
 
 	while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
-				td = list_entry (p, uhci_desc_t, desc_list);
-				list_del (p);
-				delete_desc (td);
+		td = list_entry (p, uhci_desc_t, desc_list);
+		list_del (p);
+		delete_desc (s, td);
 	}
 }
 /*-------------------------------------------------------------------*/
@@ -1449,7 +1446,7 @@
 	if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))
 		return -EINVAL;
 
-	ret = alloc_td (&td, UHCI_PTR_DEPTH);
+	ret = alloc_td (s, &td, UHCI_PTR_DEPTH);
 
 	if (ret)
 		return -ENOMEM;
@@ -1521,14 +1518,14 @@
 		}
 		else
 #endif
-		ret = alloc_td (&td, UHCI_PTR_DEPTH);
+		ret = alloc_td (s, &td, UHCI_PTR_DEPTH);
 
 		if (ret) {
 			int i;	// Cleanup allocated TDs
 
 			for (i = 0; i < n; n++)
 				if (tdm[i])
-					 delete_desc(tdm[i]);
+					 delete_desc(s, tdm[i]);
 			kfree (tdm);
 			goto err;
 		}
@@ -2511,7 +2508,7 @@
 
 		list_del (p);
 		p = p->next;
-		delete_desc (desc);
+		delete_desc (s, desc);
 	}
 	
 	dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
@@ -2566,6 +2563,7 @@
 #else
 		kfree (urb->hcpriv);
 #endif
+		urb->hcpriv = 0;
 
 		if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) {  // process_interrupt does completion on its own		
 			urb_t *next_urb = urb->next;
@@ -2625,19 +2623,22 @@
 
 			// Completion
 			if (urb->complete) {
+				int was_unlinked = (urb->status == -ENOENT);
 				urb->dev = NULL;
 				spin_unlock(&s->urb_list_lock);
 				urb->complete ((struct urb *) urb);
 				// Re-submit the URB if ring-linked
-				if (is_ring && (urb->status != -ENOENT) && !contains_killed) {
+				if (is_ring && !was_unlinked && !contains_killed) {
 					urb->dev=usb_dev;
 					uhci_submit_urb (urb);
-				}
+				} else
+					urb = 0;
 				spin_lock(&s->urb_list_lock);
 			}
 			
 			usb_dec_dev_use (usb_dev);
-			spin_unlock(&urb->lock);		
+			if (urb)
+				spin_unlock(&urb->lock);		
 		}
 	}
 
@@ -2791,6 +2792,11 @@
 	free_irq (s->irq, s);
 	usb_free_bus (s->bus);
 	cleanup_skel (s);
+
+#ifdef CONFIG_PCI
+	pci_pool_destroy(s->desc_pool);
+#endif
+
 	kfree (s);
 }
 
@@ -2840,6 +2846,7 @@
 #endif
 	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
 		io_addr, bufp);
+	printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
 
 	s = kmalloc (sizeof (uhci_t), GFP_KERNEL);
 	if (!s)
@@ -2860,8 +2867,33 @@
 	s->timeout_check = 0;
 	s->uhci_pci=dev;
 
+#ifdef CONFIG_PCI
+
+	s->desc_pool = pci_pool_create("uhci_desc", dev,
+		sizeof(uhci_desc_t),
+		16 /* byte alignment */,
+		0 /* no page-crossing issues */,
+		SLAB_KERNEL);
+	
+	if(!s->desc_pool) {
+		err("pci_pool_create for uhci_desc failed (out of memory)");
+		goto pci1;
+	}
+
+#endif	
+	info(VERSTR);
+
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+	info("High bandwidth mode enabled");	
+#endif
+
 	bus = usb_alloc_bus (&uhci_device_operations);
 	if (!bus) {
+exit3:
+#ifdef CONFIG_PCI
+		pci_pool_destroy(s->desc_pool);
+pci1:
+#endif
 		kfree (s);
 		return -1;
 	}
@@ -2896,9 +2928,9 @@
 	s->rh.numports = s->maxports;
 	s->loop_usage=0;
 	if (init_skel (s)) {
+exit4:
 		usb_free_bus (bus);
-		kfree(s);
-		return -1;
+		goto exit3;
 	}
 
 	request_region (s->io_addr, io_size, MODNAME);
@@ -2913,8 +2945,7 @@
 		reset_hc (s);
 		release_region (s->io_addr, s->io_size);
 		cleanup_skel(s);
-		kfree(s);
-		return -1;
+		goto exit4;
 	}
 
 	/* Enable PIRQ */
@@ -2924,7 +2955,7 @@
 
 	if(uhci_start_usb (s) < 0) {
 		uhci_pci_remove(dev);
-		return -1;
+		return -ENODEV;
 	}
 
 	//chain new uhci device into global list
@@ -3001,53 +3032,27 @@
 {
 	int retval;
 
-#ifdef DEBUG_SLAB
-
-	uhci_desc_kmem = kmem_cache_create("uhci_desc", sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-	
-	if(!uhci_desc_kmem) {
-		err("kmem_cache_create for uhci_desc failed (out of memory)");
-		return -ENOMEM;
-	}
-
-	urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
-	
-	if(!urb_priv_kmem) {
-		err("kmem_cache_create for urb_priv_t failed (out of memory)");
-		kmem_cache_destroy(uhci_desc_kmem);
+#ifdef	DEBUG_SLAB
+	urb_priv_kmem = kmem_cache_create ("uhci_urb_priv",
+		sizeof (urb_priv_t), 0,
+		SLAB_HWCACHE_ALIGN, NULL, NULL);
+	if (!urb_priv_kmem)
 		return -ENOMEM;
-	}
-#endif	
-	info(VERSTR);
-
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-	info("High bandwidth mode enabled");	
 #endif
-
 	retval = pci_module_init (&uhci_pci_driver);
-
-#ifdef DEBUG_SLAB
-	if (retval < 0 ) {
-		if (kmem_cache_destroy(urb_priv_kmem))
-			err("urb_priv_kmem remained");
-		if (kmem_cache_destroy(uhci_desc_kmem))
-			err("uhci_desc_kmem remained");
-	}
+#ifdef	DEBUG_SLAB
+	if (retval)
+		kmem_cache_destroy (urb_priv_kmem);
 #endif
-	
 	return retval;
 }
 
 static void __exit uhci_hcd_cleanup (void) 
 {      
 	pci_unregister_driver (&uhci_pci_driver);
-	
-#ifdef DEBUG_SLAB
-	if(kmem_cache_destroy(uhci_desc_kmem))
-		err("uhci_desc_kmem remained");
-
-	if(kmem_cache_destroy(urb_priv_kmem))
-		err("urb_priv_kmem remained");
+#ifdef	DEBUG_SLAB
+	if (urb_priv_kmem && kmem_cache_destroy (urb_priv_kmem))
+		err ("urb_priv cache not empty");
 #endif
 }
 
--- drivers/usb/usb-uhci-debug-orig.h	Sat Jul  8 19:38:16 2000
+++ drivers/usb/usb-uhci-debug.h	Thu Mar 22 21:39:06 2001
@@ -23,7 +23,7 @@
 }
 #endif
 
-static void uhci_show_td (puhci_desc_t td)
+static void __attribute__((__unused__)) uhci_show_td (puhci_desc_t td)
 {
 	char *spid;
 	

                 reply	other threads:[~2001-03-23 20:03 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='189101c0b3d3$7f732da0$6800000a@brownell.org' \
    --to=david-b@pacbell.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®