1 /*-
2 * Copyright (c) 2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * Author: Hartmut Brandt <harti@freebsd.org>
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: releng/11.2/sys/kern/subr_mbpool.c 298819 2016-04-29 22:15:33Z pfg $");
32
33 #include <sys/param.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/kernel.h>
37 #include <sys/systm.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40
41 #include <machine/bus.h>
42
43 #include <sys/mbuf.h>
44 #include <sys/mbpool.h>
45
46 MODULE_VERSION(libmbpool, 1);
47
48 /*
49 * Memory is allocated as DMA-able pages. Each page is divided into a number
50 * of equal chunks where the last 4 bytes of each chunk are occupied by
51 * the page number and the chunk number. The caller must take these four
52 * bytes into account when specifying the chunk size. Each page is mapped by
53 * its own DMA map using the user specified DMA tag.
54 *
55 * Each chunk has a used and a card bit in the high bits of its page number.
56 * 0 0 chunk is free and may be allocated
57 * 1 1 chunk has been given to the interface
58 * 0 1 chunk is traveling through the system
59 * 1 0 illegal
60 */
61 struct mbtrail {
62 uint16_t chunk;
63 uint16_t page;
64 };
65 #define MBP_CARD 0x8000
66 #define MBP_USED 0x4000
67 #define MBP_PMSK 0x3fff /* page number mask */
68 #define MBP_CMSK 0x01ff /* chunk number mask */
69
70 struct mbfree {
71 SLIST_ENTRY(mbfree) link; /* link on free list */
72 };
73
74 struct mbpage {
75 bus_dmamap_t map; /* map for this page */
76 bus_addr_t phy; /* physical address */
77 void *va; /* the memory */
78 };
79
80 struct mbpool {
81 const char *name; /* a name for this pool */
82 bus_dma_tag_t dmat; /* tag for mapping */
83 u_int max_pages; /* maximum number of pages */
84 size_t page_size; /* size of each allocation */
85 size_t chunk_size; /* size of each external mbuf */
86
87 struct mtx free_lock; /* lock of free list */
88 SLIST_HEAD(, mbfree) free_list; /* free list */
89 u_int npages; /* current number of pages */
90 u_int nchunks; /* chunks per page */
91 struct mbpage pages[]; /* pages */
92 };
93
94 static MALLOC_DEFINE(M_MBPOOL, "mbpools", "mbuf pools");
95
96 /*
97 * Make a trail pointer from a chunk pointer
98 */
99 #define C2T(P, C) ((struct mbtrail *)((char *)(C) + (P)->chunk_size - \
100 sizeof(struct mbtrail)))
101
102 /*
103 * Make a free chunk pointer from a chunk number
104 */
105 #define N2C(P, PG, C) ((struct mbfree *)((char *)(PG)->va + \
106 (C) * (P)->chunk_size))
107
108 /*
109 * Make/parse handles
110 */
111 #define HMAKE(P, C) ((((P) & MBP_PMSK) << 16) | ((C) << 7))
112 #define HPAGE(H) (((H) >> 16) & MBP_PMSK)
113 #define HCHUNK(H) (((H) >> 7) & MBP_CMSK)
114
115 /*
116 * initialize a pool
117 */
118 int
119 mbp_create(struct mbpool **pp, const char *name, bus_dma_tag_t dmat,
120 u_int max_pages, size_t page_size, size_t chunk_size)
121 {
122 u_int nchunks;
123
124 if (max_pages > MBPOOL_MAX_MAXPAGES || chunk_size == 0)
125 return (EINVAL);
126 nchunks = page_size / chunk_size;
127 if (nchunks == 0 || nchunks > MBPOOL_MAX_CHUNKS)
128 return (EINVAL);
129
130 (*pp) = malloc(sizeof(struct mbpool) +
131 max_pages * sizeof(struct mbpage),
132 M_MBPOOL, M_WAITOK | M_ZERO);
133
134 (*pp)->name = name;
135 (*pp)->dmat = dmat;
136 (*pp)->max_pages = max_pages;
137 (*pp)->page_size = page_size;
138 (*pp)->chunk_size = chunk_size;
139 (*pp)->nchunks = nchunks;
140
141 SLIST_INIT(&(*pp)->free_list);
142 mtx_init(&(*pp)->free_lock, name, NULL, MTX_DEF);
143
144 return (0);
145 }
146
147 /*
148 * destroy a pool
149 */
150 void
151 mbp_destroy(struct mbpool *p)
152 {
153 u_int i;
154 struct mbpage *pg;
155 #ifdef DIAGNOSTIC
156 struct mbtrail *tr;
157 u_int b;
158 #endif
159
160 for (i = 0; i < p->npages; i++) {
161 pg = &p->pages[i];
162 #ifdef DIAGNOSTIC
163 for (b = 0; b < p->nchunks; b++) {
164 tr = C2T(p, N2C(p, pg, b));
165 if (tr->page & MBP_CARD)
166 printf("%s: (%s) buf still on card"
167 " %u/%u\n", __func__, p->name, i, b);
168 if (tr->page & MBP_USED)
169 printf("%s: (%s) sbuf still in use"
170 " %u/%u\n", __func__, p->name, i, b);
171 }
172 #endif
173 bus_dmamap_unload(p->dmat, pg->map);
174 bus_dmamem_free(p->dmat, pg->va, pg->map);
175 }
176 mtx_destroy(&p->free_lock);
177
178 free(p, M_MBPOOL);
179 }
180
181 /*
182 * Helper function when loading a one segment DMA buffer.
183 */
184 static void
185 mbp_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
186 {
187 if (error == 0)
188 *(bus_addr_t *)arg = segs[0].ds_addr;
189 }
190
191 /*
192 * Allocate a new page
193 */
194 static void
195 mbp_alloc_page(struct mbpool *p)
196 {
197 int error;
198 struct mbpage *pg;
199 u_int i;
200 struct mbfree *f;
201 struct mbtrail *t;
202
203 if (p->npages == p->max_pages) {
204 #ifdef DIAGNOSTIC
205 printf("%s: (%s) page limit reached %u\n", __func__,
206 p->name, p->max_pages);
207 #endif
208 return;
209 }
210 pg = &p->pages[p->npages];
211
212 error = bus_dmamem_alloc(p->dmat, &pg->va, BUS_DMA_NOWAIT, &pg->map);
213 if (error != 0)
214 return;
215
216 error = bus_dmamap_load(p->dmat, pg->map, pg->va, p->page_size,
217 mbp_callback, &pg->phy, 0);
218 if (error != 0) {
219 bus_dmamem_free(p->dmat, pg->va, pg->map);
220 return;
221 }
222
223 for (i = 0; i < p->nchunks; i++) {
224 f = N2C(p, pg, i);
225 t = C2T(p, f);
226 t->page = p->npages;
227 t->chunk = i;
228 SLIST_INSERT_HEAD(&p->free_list, f, link);
229 }
230
231 p->npages++;
232 }
233
234 /*
235 * allocate a chunk
236 */
237 void *
238 mbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp)
239 {
240 struct mbfree *cf;
241 struct mbtrail *t;
242
243 mtx_lock(&p->free_lock);
244 if ((cf = SLIST_FIRST(&p->free_list)) == NULL) {
245 mbp_alloc_page(p);
246 cf = SLIST_FIRST(&p->free_list);
247 }
248 if (cf == NULL) {
249 mtx_unlock(&p->free_lock);
250 return (NULL);
251 }
252 SLIST_REMOVE_HEAD(&p->free_list, link);
253 mtx_unlock(&p->free_lock);
254
255 t = C2T(p, cf);
256
257 *pap = p->pages[t->page].phy + t->chunk * p->chunk_size;
258 *hp = HMAKE(t->page, t->chunk);
259
260 t->page |= MBP_CARD | MBP_USED;
261
262 return (cf);
263 }
264
265 /*
266 * Free a chunk
267 */
268 void
269 mbp_free(struct mbpool *p, void *ptr)
270 {
271 struct mbtrail *t;
272
273 mtx_lock(&p->free_lock);
274 t = C2T(p, ptr);
275 t->page &= ~(MBP_USED | MBP_CARD);
276 SLIST_INSERT_HEAD(&p->free_list, (struct mbfree *)ptr, link);
277 mtx_unlock(&p->free_lock);
278 }
279
280 /*
281 * Mbuf system external mbuf free routine
282 */
283 void
284 mbp_ext_free(struct mbuf *m, void *buf, void *arg)
285 {
286
287 mbp_free(arg, buf);
288 }
289
290 /*
291 * Free all buffers that are marked as being on the card
292 */
293 void
294 mbp_card_free(struct mbpool *p)
295 {
296 u_int i, b;
297 struct mbpage *pg;
298 struct mbtrail *tr;
299 struct mbfree *cf;
300
301 mtx_lock(&p->free_lock);
302 for (i = 0; i < p->npages; i++) {
303 pg = &p->pages[i];
304 for (b = 0; b < p->nchunks; b++) {
305 cf = N2C(p, pg, b);
306 tr = C2T(p, cf);
307 if (tr->page & MBP_CARD) {
308 tr->page &= MBP_PMSK;
309 SLIST_INSERT_HEAD(&p->free_list, cf, link);
310 }
311 }
312 }
313 mtx_unlock(&p->free_lock);
314 }
315
316 /*
317 * Count buffers
318 */
319 void
320 mbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free)
321 {
322 u_int i, b;
323 struct mbpage *pg;
324 struct mbtrail *tr;
325 struct mbfree *cf;
326
327 *used = *card = *free = 0;
328 for (i = 0; i < p->npages; i++) {
329 pg = &p->pages[i];
330 for (b = 0; b < p->nchunks; b++) {
331 tr = C2T(p, N2C(p, pg, b));
332 if (tr->page & MBP_CARD)
333 (*card)++;
334 if (tr->page & MBP_USED)
335 (*used)++;
336 }
337 }
338 mtx_lock(&p->free_lock);
339 SLIST_FOREACH(cf, &p->free_list, link)
340 (*free)++;
341 mtx_unlock(&p->free_lock);
342 }
343
344 /*
345 * Get the buffer from a handle and clear the card flag.
346 */
347 void *
348 mbp_get(struct mbpool *p, uint32_t h)
349 {
350 struct mbfree *cf;
351 struct mbtrail *tr;
352
353 cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
354 tr = C2T(p, cf);
355
356 #ifdef DIAGNOSTIC
357 if (!(tr->page & MBP_CARD))
358 printf("%s: (%s) chunk %u page %u not on card\n", __func__,
359 p->name, HCHUNK(h), HPAGE(h));
360 #endif
361
362 tr->page &= ~MBP_CARD;
363 return (cf);
364 }
365
366 /*
367 * Get the buffer from a handle and keep the card flag.
368 */
369 void *
370 mbp_get_keep(struct mbpool *p, uint32_t h)
371 {
372 struct mbfree *cf;
373 struct mbtrail *tr;
374
375 cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
376 tr = C2T(p, cf);
377
378 #ifdef DIAGNOSTIC
379 if (!(tr->page & MBP_CARD))
380 printf("%s: (%s) chunk %u page %u not on card\n", __func__,
381 p->name, HCHUNK(h), HPAGE(h));
382 #endif
383
384 return (cf);
385 }
386
387 /*
388 * sync the chunk
389 */
390 void
391 mbp_sync(struct mbpool *p, uint32_t h, bus_addr_t off, bus_size_t len, u_int op)
392 {
393
394 #if 0
395 bus_dmamap_sync_size(p->dmat, p->pages[HPAGE(h)].map,
396 HCHUNK(h) * p->chunk_size + off, len, op);
397 #endif
398 }
Cache object: 73010f0369caf403d43865bc5adbf651
|