| // 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 <asm/octeon/octeon.h> |
| |
| #include "ethernet-mem.h" |
| #include "ethernet-defines.h" |
| |
| #include <asm/octeon/cvmx-fpa.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); |
| } |