diff options
Diffstat (limited to 'drivers/staging/octeon/ethernet-mem.c')
-rw-r--r-- | drivers/staging/octeon/ethernet-mem.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c new file mode 100644 index 000000000000..532594957ebc --- /dev/null +++ b/drivers/staging/octeon/ethernet-mem.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is based on code from OCTEON SDK by Cavium Networks. + * + * Copyright (c) 2003-2010 Cavium Networks + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/slab.h> + +#include "octeon-ethernet.h" +#include "ethernet-mem.h" +#include "ethernet-defines.h" + +/** + * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs + * @pool: Pool to allocate an skbuff for + * @size: Size of the buffer needed for the pool + * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. + */ +static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) +{ + int freed = elements; + + while (freed) { + struct sk_buff *skb = dev_alloc_skb(size + 256); + + if (unlikely(!skb)) + break; + skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f)); + *(struct sk_buff **)(skb->data - sizeof(void *)) = skb; + cvmx_fpa_free(skb->data, pool, size / 128); + freed--; + } + return elements - freed; +} + +/** + * cvm_oct_free_hw_skbuff- free hardware pool skbuffs + * @pool: Pool to allocate an skbuff for + * @size: Size of the buffer needed for the pool + * @elements: Number of buffers to allocate + */ +static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) +{ + char *memory; + + do { + memory = cvmx_fpa_alloc(pool); + if (memory) { + struct sk_buff *skb = + *(struct sk_buff **)(memory - sizeof(void *)); + elements--; + dev_kfree_skb(skb); + } + } while (memory); + + if (elements < 0) + pr_warn("Freeing of pool %u had too many skbuffs (%d)\n", + pool, elements); + else if (elements > 0) + pr_warn("Freeing of pool %u is missing %d skbuffs\n", + pool, elements); +} + +/** + * cvm_oct_fill_hw_memory - fill a hardware pool with memory. + * @pool: Pool to populate + * @size: Size of each buffer in the pool + * @elements: Number of buffers to allocate + * + * Returns the actual number of buffers allocated. + */ +static int cvm_oct_fill_hw_memory(int pool, int size, int elements) +{ + char *memory; + char *fpa; + int freed = elements; + + while (freed) { + /* + * FPA memory must be 128 byte aligned. Since we are + * aligning we need to save the original pointer so we + * can feed it to kfree when the memory is returned to + * the kernel. + * + * We allocate an extra 256 bytes to allow for + * alignment and space for the original pointer saved + * just before the block. + */ + memory = kmalloc(size + 256, GFP_ATOMIC); + if (unlikely(!memory)) { + pr_warn("Unable to allocate %u bytes for FPA pool %d\n", + elements * size, pool); + break; + } + fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL); + *((char **)fpa - 1) = memory; + cvmx_fpa_free(fpa, pool, 0); + freed--; + } + return elements - freed; +} + +/** + * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory + * @pool: FPA pool to free + * @size: Size of each buffer in the pool + * @elements: Number of buffers that should be in the pool + */ +static void cvm_oct_free_hw_memory(int pool, int size, int elements) +{ + char *memory; + char *fpa; + + do { + fpa = cvmx_fpa_alloc(pool); + if (fpa) { + elements--; + fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa)); + memory = *((char **)fpa - 1); + kfree(memory); + } + } while (fpa); + + if (elements < 0) + pr_warn("Freeing of pool %u had too many buffers (%d)\n", + pool, elements); + else if (elements > 0) + pr_warn("Warning: Freeing of pool %u is missing %d buffers\n", + pool, elements); +} + +int cvm_oct_mem_fill_fpa(int pool, int size, int elements) +{ + int freed; + + if (pool == CVMX_FPA_PACKET_POOL) + freed = cvm_oct_fill_hw_skbuff(pool, size, elements); + else + freed = cvm_oct_fill_hw_memory(pool, size, elements); + return freed; +} + +void cvm_oct_mem_empty_fpa(int pool, int size, int elements) +{ + if (pool == CVMX_FPA_PACKET_POOL) + cvm_oct_free_hw_skbuff(pool, size, elements); + else + cvm_oct_free_hw_memory(pool, size, elements); +} |