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$");
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/mbpool.h>
44
45 MODULE_VERSION(libmbpool, 1);
46
47 /*
48 * Memory is allocated as DMA-able pages. Each page is divided into a number
49 * of equal chunks where the last 4 bytes of each chunk are occupied by
50 * the page number and the chunk number. The caller must take these four
51 * bytes into account when specifying the chunk size. Each page is mapped by
52 * its own DMA map using the user specified DMA tag.
53 *
54 * Each chunk has a used and a card bit in the high bits of its page number.
55 * 0 0 chunk is free and may be allocated
56 * 1 1 chunk has been given to the interface
57 * 0 1 chunk is traveling through the system
58 * 1 0 illegal
59 */
60 struct mbtrail {
61 uint16_t chunk;
62 uint16_t page;
63 };
64 #define MBP_CARD 0x8000
65 #define MBP_USED 0x4000
66 #define MBP_PMSK 0x3fff /* page number mask */
67 #define MBP_CMSK 0x01ff /* chunk number mask */
68
69 struct mbfree {
70 SLIST_ENTRY(mbfree) link; /* link on free list */
71 };
72
73 struct mbpage {
74 bus_dmamap_t map; /* map for this page */
75 bus_addr_t phy; /* physical address */
76 void *va; /* the memory */
77 };
78
79 struct mbpool {
80 const char *name; /* a name for this pool */
81 bus_dma_tag_t dmat; /* tag for mapping */
82 u_int max_pages; /* maximum number of pages */
83 size_t page_size; /* size of each allocation */
84 size_t chunk_size; /* size of each external mbuf */
85
86 struct mtx free_lock; /* lock of free list */
87 SLIST_HEAD(, mbfree) free_list; /* free list */
88 u_int npages; /* current number of pages */
89 u_int nchunks; /* chunks per page */
90 struct mbpage pages[]; /* pages */
91 };
92
93 static MALLOC_DEFINE(M_MBPOOL, "mbpools", "mbuf pools");
94
95 /*
96 * Make a trail pointer from a chunk pointer
97 */
98 #define C2T(P, C) ((struct mbtrail *)((char *)(C) + (P)->chunk_size - \
99 sizeof(struct mbtrail)))
100
101 /*
102 * Make a free chunk pointer from a chunk number
103 */
104 #define N2C(P, PG, C) ((struct mbfree *)((char *)(PG)->va + \
105 (C) * (P)->chunk_size))
106
107 /*
108 * Make/parse handles
109 */
110 #define HMAKE(P, C) ((((P) & MBP_PMSK) << 16) | ((C) << 7))
111 #define HPAGE(H) (((H) >> 16) & MBP_PMSK)
112 #define HCHUNK(H) (((H) >> 7) & MBP_CMSK)
113
114 /*
115 * initialize a pool
116 */
117 int
118 mbp_create(struct mbpool **pp, const char *name, bus_dma_tag_t dmat,
119 u_int max_pages, size_t page_size, size_t chunk_size)
120 {
121 u_int nchunks;
122
123 if (max_pages > MBPOOL_MAX_MAXPAGES || chunk_size == 0)
124 return (EINVAL);
125 nchunks = page_size / chunk_size;
126 if (nchunks == 0 || nchunks > MBPOOL_MAX_CHUNKS)
127 return (EINVAL);
128
129 (*pp) = malloc(sizeof(struct mbpool) +
130 max_pages * sizeof(struct mbpage),
131 M_MBPOOL, M_WAITOK | M_ZERO);
132
133 (*pp)->name = name;
134 (*pp)->dmat = dmat;
135 (*pp)->max_pages = max_pages;
136 (*pp)->page_size = page_size;
137 (*pp)->chunk_size = chunk_size;
138 (*pp)->nchunks = nchunks;
139
140 SLIST_INIT(&(*pp)->free_list);
141 mtx_init(&(*pp)->free_lock, name, NULL, MTX_DEF);
142
143 return (0);
144 }
145
146 /*
147 * destroy a pool
148 */
149 void
150 mbp_destroy(struct mbpool *p)
151 {
152 u_int i;
153 struct mbpage *pg;
154 #ifdef DIAGNOSTIC
155 struct mbtrail *tr;
156 u_int b;
157 #endif
158
159 for (i = 0; i < p->npages; i++) {
160 pg = &p->pages[i];
161 #ifdef DIAGNOSTIC
162 for (b = 0; b < p->nchunks; b++) {
163 tr = C2T(p, N2C(p, pg, b));
164 if (tr->page & MBP_CARD)
165 printf("%s: (%s) buf still on card"
166 " %u/%u\n", __func__, p->name, i, b);
167 if (tr->page & MBP_USED)
168 printf("%s: (%s) sbuf still in use"
169 " %u/%u\n", __func__, p->name, i, b);
170 }
171 #endif
172 bus_dmamap_unload(p->dmat, pg->map);
173 bus_dmamem_free(p->dmat, pg->va, pg->map);
174 }
175 mtx_destroy(&p->free_lock);
176
177 free(p, M_MBPOOL);
178 }
179
180 /*
181 * Helper function when loading a one segment DMA buffer.
182 */
183 static void
184 mbp_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
185 {
186 if (error == 0)
187 *(bus_addr_t *)arg = segs[0].ds_addr;
188 }
189
190 /*
191 * Allocate a new page
192 */
193 static void
194 mbp_alloc_page(struct mbpool *p)
195 {
196 int error;
197 struct mbpage *pg;
198 u_int i;
199 struct mbfree *f;
200 struct mbtrail *t;
201
202 if (p->npages == p->max_pages) {
203 #ifdef DIAGNOSTIC
204 printf("%s: (%s) page limit reached %u\n", __func__,
205 p->name, p->max_pages);
206 #endif
207 return;
208 }
209 pg = &p->pages[p->npages];
210
211 error = bus_dmamem_alloc(p->dmat, &pg->va, BUS_DMA_NOWAIT, &pg->map);
212 if (error != 0)
213 return;
214
215 error = bus_dmamap_load(p->dmat, pg->map, pg->va, p->page_size,
216 mbp_callback, &pg->phy, 0);
217 if (error != 0) {
218 bus_dmamem_free(p->dmat, pg->va, pg->map);
219 return;
220 }
221
222 for (i = 0; i < p->nchunks; i++) {
223 f = N2C(p, pg, i);
224 t = C2T(p, f);
225 t->page = p->npages;
226 t->chunk = i;
227 SLIST_INSERT_HEAD(&p->free_list, f, link);
228 }
229
230 p->npages++;
231 }
232
233 /*
234 * allocate a chunk
235 */
236 void *
237 mbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp)
238 {
239 struct mbfree *cf;
240 struct mbtrail *t;
241
242 mtx_lock(&p->free_lock);
243 if ((cf = SLIST_FIRST(&p->free_list)) == NULL) {
244 mbp_alloc_page(p);
245 cf = SLIST_FIRST(&p->free_list);
246 }
247 if (cf == NULL) {
248 mtx_unlock(&p->free_lock);
249 return (NULL);
250 }
251 SLIST_REMOVE_HEAD(&p->free_list, link);
252 mtx_unlock(&p->free_lock);
253
254 t = C2T(p, cf);
255
256 *pap = p->pages[t->page].phy + t->chunk * p->chunk_size;
257 *hp = HMAKE(t->page, t->chunk);
258
259 t->page |= MBP_CARD | MBP_USED;
260
261 return (cf);
262 }
263
264 /*
265 * Free a chunk
266 */
267 void
268 mbp_free(struct mbpool *p, void *ptr)
269 {
270 struct mbtrail *t;
271
272 mtx_lock(&p->free_lock);
273 t = C2T(p, ptr);
274 t->page &= ~(MBP_USED | MBP_CARD);
275 SLIST_INSERT_HEAD(&p->free_list, (struct mbfree *)ptr, link);
276 mtx_unlock(&p->free_lock);
277 }
278
279 /*
280 * Mbuf system external mbuf free routine
281 */
282 void
283 mbp_ext_free(void *buf, void *arg)
284 {
285 mbp_free(arg, buf);
286 }
287
288 /*
289 * Free all buffers that are marked as beeing on the card
290 */
291 void
292 mbp_card_free(struct mbpool *p)
293 {
294 u_int i, b;
295 struct mbpage *pg;
296 struct mbtrail *tr;
297 struct mbfree *cf;
298
299 mtx_lock(&p->free_lock);
300 for (i = 0; i < p->npages; i++) {
301 pg = &p->pages[i];
302 for (b = 0; b < p->nchunks; b++) {
303 cf = N2C(p, pg, b);
304 tr = C2T(p, cf);
305 if (tr->page & MBP_CARD) {
306 tr->page &= MBP_PMSK;
307 SLIST_INSERT_HEAD(&p->free_list, cf, link);
308 }
309 }
310 }
311 mtx_unlock(&p->free_lock);
312 }
313
314 /*
315 * Count buffers
316 */
317 void
318 mbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free)
319 {
320 u_int i, b;
321 struct mbpage *pg;
322 struct mbtrail *tr;
323 struct mbfree *cf;
324
325 *used = *card = *free = 0;
326 for (i = 0; i < p->npages; i++) {
327 pg = &p->pages[i];
328 for (b = 0; b < p->nchunks; b++) {
329 tr = C2T(p, N2C(p, pg, b));
330 if (tr->page & MBP_CARD)
331 (*card)++;
332 if (tr->page & MBP_USED)
333 (*used)++;
334 }
335 }
336 mtx_lock(&p->free_lock);
337 SLIST_FOREACH(cf, &p->free_list, link)
338 (*free)++;
339 mtx_unlock(&p->free_lock);
340 }
341
342 /*
343 * Get the buffer from a handle and clear the card flag.
344 */
345 void *
346 mbp_get(struct mbpool *p, uint32_t h)
347 {
348 struct mbfree *cf;
349 struct mbtrail *tr;
350
351 cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
352 tr = C2T(p, cf);
353
354 #ifdef DIAGNOSTIC
355 if (!(tr->page & MBP_CARD))
356 printf("%s: (%s) chunk %u page %u not on card\n", __func__,
357 p->name, HCHUNK(h), HPAGE(h));
358 #endif
359
360 tr->page &= ~MBP_CARD;
361 return (cf);
362 }
363
364 /*
365 * Get the buffer from a handle and keep the card flag.
366 */
367 void *
368 mbp_get_keep(struct mbpool *p, uint32_t h)
369 {
370 struct mbfree *cf;
371 struct mbtrail *tr;
372
373 cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
374 tr = C2T(p, cf);
375
376 #ifdef DIAGNOSTIC
377 if (!(tr->page & MBP_CARD))
378 printf("%s: (%s) chunk %u page %u not on card\n", __func__,
379 p->name, HCHUNK(h), HPAGE(h));
380 #endif
381
382 return (cf);
383 }
384
385 /*
386 * sync the chunk
387 */
388 void
389 mbp_sync(struct mbpool *p, uint32_t h, bus_addr_t off, bus_size_t len, u_int op)
390 {
391
392 #if 0
393 bus_dmamap_sync_size(p->dmat, p->pages[HPAGE(h)].map,
394 HCHUNK(h) * p->chunk_size + off, len, op);
395 #endif
396 }
Cache object: ace23a53cb1a5c713ad17a40ca024fdf
|