1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This file is based on code from OCTEON SDK by Cavium Networks. |
4 | * |
5 | * Copyright (c) 2003-2010 Cavium Networks |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/netdevice.h> |
10 | #include <linux/slab.h> |
11 | |
12 | #include "octeon-ethernet.h" |
13 | #include "ethernet-mem.h" |
14 | #include "ethernet-defines.h" |
15 | |
16 | /** |
17 | * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs |
18 | * @pool: Pool to allocate an skbuff for |
19 | * @size: Size of the buffer needed for the pool |
20 | * @elements: Number of buffers to allocate |
21 | * |
22 | * Returns the actual number of buffers allocated. |
23 | */ |
24 | static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements) |
25 | { |
26 | int freed = elements; |
27 | |
28 | while (freed) { |
29 | struct sk_buff *skb = dev_alloc_skb(length: size + 256); |
30 | |
31 | if (unlikely(!skb)) |
32 | break; |
33 | skb_reserve(skb, len: 256 - (((unsigned long)skb->data) & 0x7f)); |
34 | *(struct sk_buff **)(skb->data - sizeof(void *)) = skb; |
35 | cvmx_fpa_free(ptr: skb->data, pool, num_cache_lines: size / 128); |
36 | freed--; |
37 | } |
38 | return elements - freed; |
39 | } |
40 | |
41 | /** |
42 | * cvm_oct_free_hw_skbuff- free hardware pool skbuffs |
43 | * @pool: Pool to allocate an skbuff for |
44 | * @size: Size of the buffer needed for the pool |
45 | * @elements: Number of buffers to allocate |
46 | */ |
47 | static void cvm_oct_free_hw_skbuff(int pool, int size, int elements) |
48 | { |
49 | char *memory; |
50 | |
51 | do { |
52 | memory = cvmx_fpa_alloc(pool); |
53 | if (memory) { |
54 | struct sk_buff *skb = |
55 | *(struct sk_buff **)(memory - sizeof(void *)); |
56 | elements--; |
57 | dev_kfree_skb(skb); |
58 | } |
59 | } while (memory); |
60 | |
61 | if (elements < 0) |
62 | pr_warn("Freeing of pool %u had too many skbuffs (%d)\n" , |
63 | pool, elements); |
64 | else if (elements > 0) |
65 | pr_warn("Freeing of pool %u is missing %d skbuffs\n" , |
66 | pool, elements); |
67 | } |
68 | |
69 | /** |
70 | * cvm_oct_fill_hw_memory - fill a hardware pool with memory. |
71 | * @pool: Pool to populate |
72 | * @size: Size of each buffer in the pool |
73 | * @elements: Number of buffers to allocate |
74 | * |
75 | * Returns the actual number of buffers allocated. |
76 | */ |
77 | static int cvm_oct_fill_hw_memory(int pool, int size, int elements) |
78 | { |
79 | char *memory; |
80 | char *fpa; |
81 | int freed = elements; |
82 | |
83 | while (freed) { |
84 | /* |
85 | * FPA memory must be 128 byte aligned. Since we are |
86 | * aligning we need to save the original pointer so we |
87 | * can feed it to kfree when the memory is returned to |
88 | * the kernel. |
89 | * |
90 | * We allocate an extra 256 bytes to allow for |
91 | * alignment and space for the original pointer saved |
92 | * just before the block. |
93 | */ |
94 | memory = kmalloc(size: size + 256, GFP_ATOMIC); |
95 | if (unlikely(!memory)) { |
96 | pr_warn("Unable to allocate %u bytes for FPA pool %d\n" , |
97 | elements * size, pool); |
98 | break; |
99 | } |
100 | fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL); |
101 | *((char **)fpa - 1) = memory; |
102 | cvmx_fpa_free(ptr: fpa, pool, num_cache_lines: 0); |
103 | freed--; |
104 | } |
105 | return elements - freed; |
106 | } |
107 | |
108 | /** |
109 | * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory |
110 | * @pool: FPA pool to free |
111 | * @size: Size of each buffer in the pool |
112 | * @elements: Number of buffers that should be in the pool |
113 | */ |
114 | static void cvm_oct_free_hw_memory(int pool, int size, int elements) |
115 | { |
116 | char *memory; |
117 | char *fpa; |
118 | |
119 | do { |
120 | fpa = cvmx_fpa_alloc(pool); |
121 | if (fpa) { |
122 | elements--; |
123 | fpa = (char *)phys_to_virt(address: cvmx_ptr_to_phys(ptr: fpa)); |
124 | memory = *((char **)fpa - 1); |
125 | kfree(objp: memory); |
126 | } |
127 | } while (fpa); |
128 | |
129 | if (elements < 0) |
130 | pr_warn("Freeing of pool %u had too many buffers (%d)\n" , |
131 | pool, elements); |
132 | else if (elements > 0) |
133 | pr_warn("Warning: Freeing of pool %u is missing %d buffers\n" , |
134 | pool, elements); |
135 | } |
136 | |
137 | int cvm_oct_mem_fill_fpa(int pool, int size, int elements) |
138 | { |
139 | int freed; |
140 | |
141 | if (pool == CVMX_FPA_PACKET_POOL) |
142 | freed = cvm_oct_fill_hw_skbuff(pool, size, elements); |
143 | else |
144 | freed = cvm_oct_fill_hw_memory(pool, size, elements); |
145 | return freed; |
146 | } |
147 | |
148 | void cvm_oct_mem_empty_fpa(int pool, int size, int elements) |
149 | { |
150 | if (pool == CVMX_FPA_PACKET_POOL) |
151 | cvm_oct_free_hw_skbuff(pool, size, elements); |
152 | else |
153 | cvm_oct_free_hw_memory(pool, size, elements); |
154 | } |
155 | |