diff options
| author | Russell King <rmk@flint.arm.linux.org.uk> | 2002-11-24 23:39:57 +0000 |
|---|---|---|
| committer | Russell King <rmk@flint.arm.linux.org.uk> | 2002-11-24 23:39:57 +0000 |
| commit | 0763cb7973848ccd0d556650765e522a050552dc (patch) | |
| tree | 19466a4391c9f6e4bd9eb96f860c1c83647f545f | |
| parent | 897ed78950b8d76b77e5b9d414b7cd79c7fac072 (diff) | |
[ARM PATCH] 1332/1: DMA Scatter-Gather Primitives for SA-1111 Bounce Buffer Layer
Patch from Christopher Hoover
- implements the dma scatter-gather primitives for the SA-1111 bounce
buffer layer. these are required by usb-storage.
- adds (optionally) a tiny bit of statistics
- adjust the large buffer pool down to 4KiB (suggested by the stats)
| -rw-r--r-- | arch/arm/mach-sa1100/sa1111-pcibuf.c | 293 |
1 files changed, 225 insertions, 68 deletions
diff --git a/arch/arm/mach-sa1100/sa1111-pcibuf.c b/arch/arm/mach-sa1100/sa1111-pcibuf.c index b39048ca37b7..ebc2f4051c81 100644 --- a/arch/arm/mach-sa1100/sa1111-pcibuf.c +++ b/arch/arm/mach-sa1100/sa1111-pcibuf.c @@ -31,6 +31,14 @@ #define DPRINTK(...) do { } while (0) #endif +//#define STATS +#ifdef STATS +#define DO_STATS(X) do { X ; } while (0) +#else +#define DO_STATS(X) do { } while (0) +#endif + +/* ************************************************** */ struct safe_buffer { struct list_head node; @@ -50,10 +58,24 @@ LIST_HEAD(safe_buffers); #define SIZE_SMALL 1024 -#define SIZE_LARGE (16*1024) +#define SIZE_LARGE (4*1024) static struct pci_pool *small_buffer_pool, *large_buffer_pool; +#ifdef STATS +static unsigned long sbp_allocs __initdata = 0; +static unsigned long lbp_allocs __initdata = 0; +static unsigned long total_allocs __initdata= 0; + +static void print_alloc_stats(void) +{ + printk(KERN_INFO + "sa1111_pcibuf: sbp: %lu, lbp: %lu, other: %lu, total: %lu\n", + sbp_allocs, lbp_allocs, + total_allocs - sbp_allocs - lbp_allocs, total_allocs); +} +#endif + static int __init create_safe_buffer_pools(void) { @@ -81,6 +103,10 @@ create_safe_buffer_pools(void) return -1; } + printk(KERN_INFO + "sa1111_pcibuf: buffer sizes: small=%u, large=%u\n", + SIZE_SMALL, SIZE_LARGE); + return 0; } @@ -108,6 +134,8 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction) DPRINTK("%s(ptr=%p, size=%d, direction=%d)\n", __func__, ptr, size, direction); + DO_STATS ( total_allocs++ ); + buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC); if (buf == 0) { printk(KERN_WARNING "%s: kmalloc failed\n", __func__); @@ -117,12 +145,14 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction) if (size <= SIZE_SMALL) { pool = small_buffer_pool; safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); + + DO_STATS ( sbp_allocs++ ); } else if (size <= SIZE_LARGE) { pool = large_buffer_pool; safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); + + DO_STATS ( lbp_allocs++ ); } else { - printk(KERN_DEBUG - "sa111_pcibuf: resorting to pci_alloc_consistent\n"); pool = 0; safe = pci_alloc_consistent(SA1111_FAKE_PCIDEV, size, &safe_dma_addr); @@ -136,6 +166,11 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction) return 0; } +#ifdef STATS + if (total_allocs % 1000 == 0) + print_alloc_stats(); +#endif + BUG_ON(sa1111_check_dma_bug(safe_dma_addr)); // paranoia buf->ptr = ptr; @@ -204,37 +239,39 @@ dma_range_is_safe(dma_addr_t addr, size_t size) return ((physaddr + size - 1) < (1<<20)); } -/* - * see if a buffer address is in an 'unsafe' range. if it is - * allocate a 'safe' buffer and copy the unsafe buffer into it. - * substitute the safe buffer for the unsafe one. - * (basically move the buffer from an unsafe area to a safe one) - */ -dma_addr_t -sa1111_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) -{ - unsigned long flags; - dma_addr_t dma_addr; +/* ************************************************** */ - DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", - __func__, hwdev, ptr, size, direction); +#ifdef STATS +static unsigned long map_op_count __initdata = 0; +static unsigned long bounce_count __initdata = 0; - BUG_ON(hwdev != SA1111_FAKE_PCIDEV); - BUG_ON(direction == PCI_DMA_NONE); +static void print_map_stats(void) +{ + printk(KERN_INFO + "sa1111_pcibuf: map_op_count=%lu, bounce_count=%lu\n", + map_op_count, bounce_count); +} +#endif - local_irq_save(flags); +static dma_addr_t +map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) +{ + dma_addr_t dma_addr; + + DO_STATS ( map_op_count++ ); dma_addr = virt_to_bus(ptr); if (!dma_range_is_safe(dma_addr, size)) { struct safe_buffer *buf; + DO_STATS ( bounce_count++ ) ; + buf = alloc_safe_buffer(hwdev, ptr, size, direction); if (buf == 0) { printk(KERN_ERR "%s: unable to map unsafe buffer %p!\n", __func__, ptr); - local_irq_restore(flags); return 0; } @@ -256,33 +293,22 @@ sa1111_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) consistent_sync(ptr, size, direction); } - local_irq_restore(flags); +#ifdef STATS + if (map_op_count % 1000 == 0) + print_map_stats(); +#endif + return dma_addr; } -/* - * see if a mapped address was really a "safe" buffer and if so, copy - * the data from the safe buffer back to the unsafe buffer and free up - * the safe buffer. (basically return things back to the way they - * should be) - */ - -void -sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, - size_t size, int direction) +static void +unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) { - unsigned long flags; struct safe_buffer *buf; - DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", - __func__, hwdev, (void *) dma_addr, size, direction); - - BUG_ON(hwdev != SA1111_FAKE_PCIDEV); - BUG_ON(direction == PCI_DMA_NONE); - - local_irq_save(flags); - buf = find_safe_buffer(dma_addr); + if (buf) { BUG_ON(buf->size != size); BUG_ON(buf->direction != direction); @@ -292,6 +318,9 @@ sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, buf->ptr, (void *) virt_to_bus(buf->ptr), buf->safe, (void *) buf->safe_dma_addr); + + DO_STATS ( bounce_count++ ); + if ((direction == PCI_DMA_FROMDEVICE) || (direction == PCI_DMA_BIDIRECTIONAL)) { DPRINTK("%s: copy back from safe %p, to unsafe %p size %d\n", @@ -300,39 +329,16 @@ sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, } free_safe_buffer(buf); } - - local_irq_restore(flags); } -int -sa1111_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction) -{ - BUG(); /* Not implemented. */ -} - -void -sa1111_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, - int direction) -{ - BUG(); /* Not implemented. */ -} - -void -sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, +static void +sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction) { - unsigned long flags; struct safe_buffer *buf; - DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", - __func__, hwdev, (void *) dma_addr, size, direction); - - BUG_ON(hwdev != SA1111_FAKE_PCIDEV); - - local_irq_save(flags); - buf = find_safe_buffer(dma_addr); + if (buf) { BUG_ON(buf->size != size); BUG_ON(buf->direction != direction); @@ -342,6 +348,8 @@ sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, buf->ptr, (void *) virt_to_bus(buf->ptr), buf->safe, (void *) buf->safe_dma_addr); + DO_STATS ( bounce_count++ ); + switch (direction) { case PCI_DMA_FROMDEVICE: DPRINTK("%s: copy back from safe %p, to unsafe %p size %d\n", @@ -362,15 +370,159 @@ sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, } else { consistent_sync(bus_to_virt(dma_addr), size, direction); } +} + +/* ************************************************** */ + +/* + * see if a buffer address is in an 'unsafe' range. if it is + * allocate a 'safe' buffer and copy the unsafe buffer into it. + * substitute the safe buffer for the unsafe one. + * (basically move the buffer from an unsafe area to a safe one) + */ +dma_addr_t +sa1111_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) +{ + unsigned long flags; + dma_addr_t dma_addr; + + DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", + __func__, hwdev, ptr, size, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + BUG_ON(direction == PCI_DMA_NONE); + + local_irq_save(flags); + + dma_addr = map_single(hwdev, ptr, size, direction); + + local_irq_restore(flags); + + return dma_addr; +} + +/* + * see if a mapped address was really a "safe" buffer and if so, copy + * the data from the safe buffer back to the unsafe buffer and free up + * the safe buffer. (basically return things back to the way they + * should be) + */ + +void +sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + unsigned long flags; + + DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", + __func__, hwdev, (void *) dma_addr, size, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + BUG_ON(direction == PCI_DMA_NONE); + + local_irq_save(flags); + + unmap_single(hwdev, dma_addr, size, direction); + + local_irq_restore(flags); +} + +int +sa1111_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + unsigned long flags; + int i; + + DPRINTK("%s(hwdev=%p,sg=%p,nents=%d,dir=%x)\n", + __func__, hwdev, sg, nents, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + BUG_ON(direction == PCI_DMA_NONE); + + local_irq_save(flags); + + for (i = 0; i < nents; i++, sg++) { + struct page *page = sg->page; + unsigned int offset = sg->offset; + unsigned int length = sg->length; + void *ptr = page_address(page) + offset; + + sg->dma_address = + map_single(hwdev, ptr, length, direction); + } + + local_irq_restore(flags); + + return nents; +} + +void +sa1111_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents, + int direction) +{ + unsigned long flags; + int i; + + DPRINTK("%s(hwdev=%p,sg=%p,nents=%d,dir=%x)\n", + __func__, hwdev, sg, nents, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + BUG_ON(direction == PCI_DMA_NONE); + + local_irq_save(flags); + + for (i = 0; i < nents; i++, sg++) { + dma_addr_t dma_addr = sg->dma_address; + unsigned int length = sg->length; + + unmap_single(hwdev, dma_addr, length, direction); + } + + local_irq_restore(flags); +} + +void +sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + unsigned long flags; + + DPRINTK("%s(hwdev=%p,ptr=%p,size=%d,dir=%x)\n", + __func__, hwdev, (void *) dma_addr, size, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + + local_irq_save(flags); + + sync_single(hwdev, dma_addr, size, direction); local_irq_restore(flags); } void sa1111_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nelems, int direction) + int nents, int direction) { - BUG(); /* Not implemented. */ + unsigned long flags; + int i; + + DPRINTK("%s(hwdev=%p,sg=%p,nents=%d,dir=%x)\n", + __func__, hwdev, sg, nents, direction); + + BUG_ON(hwdev != SA1111_FAKE_PCIDEV); + BUG_ON(direction == PCI_DMA_NONE); + + local_irq_save(flags); + + for (i = 0; i < nents; i++, sg++) { + dma_addr_t dma_addr = sg->dma_address; + unsigned int length = sg->length; + + sync_single(hwdev, dma_addr, length, direction); + } + + local_irq_restore(flags); } EXPORT_SYMBOL(sa1111_map_single); @@ -399,6 +551,11 @@ static void __exit sa1111_pcibuf_exit(void) { BUG_ON(!list_empty(&safe_buffers)); +#ifdef STATS + print_alloc_stats(); + print_map_stats(); +#endif + destroy_safe_buffer_pools(); } module_exit(sa1111_pcibuf_exit); |
