Commit 0763cb79 authored by Russell King's avatar Russell King

[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)
parent 897ed789
...@@ -31,6 +31,14 @@ ...@@ -31,6 +31,14 @@
#define DPRINTK(...) do { } while (0) #define DPRINTK(...) do { } while (0)
#endif #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 safe_buffer {
struct list_head node; struct list_head node;
...@@ -50,10 +58,24 @@ LIST_HEAD(safe_buffers); ...@@ -50,10 +58,24 @@ LIST_HEAD(safe_buffers);
#define SIZE_SMALL 1024 #define SIZE_SMALL 1024
#define SIZE_LARGE (16*1024) #define SIZE_LARGE (4*1024)
static struct pci_pool *small_buffer_pool, *large_buffer_pool; 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 static int __init
create_safe_buffer_pools(void) create_safe_buffer_pools(void)
{ {
...@@ -81,6 +103,10 @@ create_safe_buffer_pools(void) ...@@ -81,6 +103,10 @@ create_safe_buffer_pools(void)
return -1; return -1;
} }
printk(KERN_INFO
"sa1111_pcibuf: buffer sizes: small=%u, large=%u\n",
SIZE_SMALL, SIZE_LARGE);
return 0; return 0;
} }
...@@ -108,6 +134,8 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction) ...@@ -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", DPRINTK("%s(ptr=%p, size=%d, direction=%d)\n",
__func__, ptr, size, direction); __func__, ptr, size, direction);
DO_STATS ( total_allocs++ );
buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC); buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
if (buf == 0) { if (buf == 0) {
printk(KERN_WARNING "%s: kmalloc failed\n", __func__); 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) ...@@ -117,12 +145,14 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
if (size <= SIZE_SMALL) { if (size <= SIZE_SMALL) {
pool = small_buffer_pool; pool = small_buffer_pool;
safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
DO_STATS ( sbp_allocs++ );
} else if (size <= SIZE_LARGE) { } else if (size <= SIZE_LARGE) {
pool = large_buffer_pool; pool = large_buffer_pool;
safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); safe = pci_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
DO_STATS ( lbp_allocs++ );
} else { } else {
printk(KERN_DEBUG
"sa111_pcibuf: resorting to pci_alloc_consistent\n");
pool = 0; pool = 0;
safe = pci_alloc_consistent(SA1111_FAKE_PCIDEV, size, safe = pci_alloc_consistent(SA1111_FAKE_PCIDEV, size,
&safe_dma_addr); &safe_dma_addr);
...@@ -136,6 +166,11 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction) ...@@ -136,6 +166,11 @@ alloc_safe_buffer(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
return 0; return 0;
} }
#ifdef STATS
if (total_allocs % 1000 == 0)
print_alloc_stats();
#endif
BUG_ON(sa1111_check_dma_bug(safe_dma_addr)); // paranoia BUG_ON(sa1111_check_dma_bug(safe_dma_addr)); // paranoia
buf->ptr = ptr; buf->ptr = ptr;
...@@ -204,37 +239,39 @@ dma_range_is_safe(dma_addr_t addr, size_t size) ...@@ -204,37 +239,39 @@ dma_range_is_safe(dma_addr_t addr, size_t size)
return ((physaddr + size - 1) < (1<<20)); 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", #ifdef STATS
__func__, hwdev, ptr, size, direction); static unsigned long map_op_count __initdata = 0;
static unsigned long bounce_count __initdata = 0;
BUG_ON(hwdev != SA1111_FAKE_PCIDEV); static void print_map_stats(void)
BUG_ON(direction == PCI_DMA_NONE); {
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); dma_addr = virt_to_bus(ptr);
if (!dma_range_is_safe(dma_addr, size)) { if (!dma_range_is_safe(dma_addr, size)) {
struct safe_buffer *buf; struct safe_buffer *buf;
DO_STATS ( bounce_count++ ) ;
buf = alloc_safe_buffer(hwdev, ptr, size, direction); buf = alloc_safe_buffer(hwdev, ptr, size, direction);
if (buf == 0) { if (buf == 0) {
printk(KERN_ERR printk(KERN_ERR
"%s: unable to map unsafe buffer %p!\n", "%s: unable to map unsafe buffer %p!\n",
__func__, ptr); __func__, ptr);
local_irq_restore(flags);
return 0; return 0;
} }
...@@ -256,33 +293,22 @@ sa1111_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction) ...@@ -256,33 +293,22 @@ sa1111_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
consistent_sync(ptr, size, 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; return dma_addr;
} }
/* static void
* see if a mapped address was really a "safe" buffer and if so, copy unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
* the data from the safe buffer back to the unsafe buffer and free up size_t size, int direction)
* 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;
struct safe_buffer *buf; 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); buf = find_safe_buffer(dma_addr);
if (buf) { if (buf) {
BUG_ON(buf->size != size); BUG_ON(buf->size != size);
BUG_ON(buf->direction != direction); BUG_ON(buf->direction != direction);
...@@ -292,6 +318,9 @@ sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, ...@@ -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->ptr, (void *) virt_to_bus(buf->ptr),
buf->safe, (void *) buf->safe_dma_addr); buf->safe, (void *) buf->safe_dma_addr);
DO_STATS ( bounce_count++ );
if ((direction == PCI_DMA_FROMDEVICE) || if ((direction == PCI_DMA_FROMDEVICE) ||
(direction == PCI_DMA_BIDIRECTIONAL)) { (direction == PCI_DMA_BIDIRECTIONAL)) {
DPRINTK("%s: copy back from safe %p, to unsafe %p size %d\n", 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, ...@@ -300,39 +329,16 @@ sa1111_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
} }
free_safe_buffer(buf); free_safe_buffer(buf);
} }
local_irq_restore(flags);
} }
int static void
sa1111_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
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,
size_t size, int direction) size_t size, int direction)
{ {
unsigned long flags;
struct safe_buffer *buf; 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); buf = find_safe_buffer(dma_addr);
if (buf) { if (buf) {
BUG_ON(buf->size != size); BUG_ON(buf->size != size);
BUG_ON(buf->direction != direction); BUG_ON(buf->direction != direction);
...@@ -342,6 +348,8 @@ sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr, ...@@ -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->ptr, (void *) virt_to_bus(buf->ptr),
buf->safe, (void *) buf->safe_dma_addr); buf->safe, (void *) buf->safe_dma_addr);
DO_STATS ( bounce_count++ );
switch (direction) { switch (direction) {
case PCI_DMA_FROMDEVICE: case PCI_DMA_FROMDEVICE:
DPRINTK("%s: copy back from safe %p, to unsafe %p size %d\n", 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, ...@@ -362,15 +370,159 @@ sa1111_dma_sync_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
} else { } else {
consistent_sync(bus_to_virt(dma_addr), size, direction); 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); local_irq_restore(flags);
} }
void void
sa1111_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg, 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); EXPORT_SYMBOL(sa1111_map_single);
...@@ -399,6 +551,11 @@ static void __exit sa1111_pcibuf_exit(void) ...@@ -399,6 +551,11 @@ static void __exit sa1111_pcibuf_exit(void)
{ {
BUG_ON(!list_empty(&safe_buffers)); BUG_ON(!list_empty(&safe_buffers));
#ifdef STATS
print_alloc_stats();
print_map_stats();
#endif
destroy_safe_buffer_pools(); destroy_safe_buffer_pools();
} }
module_exit(sa1111_pcibuf_exit); module_exit(sa1111_pcibuf_exit);
......
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