FreeBSD kernel kern code
subr_mbpool.c
Go to the documentation of this file.
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("$BSDSUniX$");
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
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
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 }
void mbp_destroy(struct mbpool *p)
Definition: subr_mbpool.c:150
#define HMAKE(P, C)
Definition: subr_mbpool.c:110
struct buf * buf
Definition: vfs_bio.c:97
u_int max_pages
Definition: subr_mbpool.c:82
size_t chunk_size
Definition: subr_mbpool.c:84
void * mbp_get(struct mbpool *p, uint32_t h)
Definition: subr_mbpool.c:346
struct mtx free_lock
Definition: subr_mbpool.c:86
void * malloc(unsigned long size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:454
bus_addr_t phy
Definition: subr_mbpool.c:75
void * va
Definition: subr_mbpool.c:76
uint16_t page
Definition: subr_mbpool.c:62
const char * name
Definition: subr_mbpool.c:80
void mbp_free(struct mbpool *p, void *ptr)
Definition: subr_mbpool.c:268
static MALLOC_DEFINE(M_MBPOOL,"mbpools","mbuf pools")
#define C2T(P, C)
Definition: subr_mbpool.c:98
void * mbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp)
Definition: subr_mbpool.c:237
#define MBP_CARD
Definition: subr_mbpool.c:64
uint16_t chunk
Definition: subr_mbpool.c:61
#define MBP_USED
Definition: subr_mbpool.c:65
void mbp_sync(struct mbpool *p, uint32_t h, bus_addr_t off, bus_size_t len, u_int op)
Definition: subr_mbpool.c:389
__FBSDID("$BSDSUniX$")
int bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, int flags)
Definition: subr_bus_dma.c:276
void mbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free)
Definition: subr_mbpool.c:318
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:554
int printf(const char *fmt,...)
Definition: subr_prf.c:367
void mbp_ext_free(void *buf, void *arg)
Definition: subr_mbpool.c:283
static void mbp_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
Definition: subr_mbpool.c:184
int mbp_create(struct mbpool **pp, const char *name, bus_dma_tag_t dmat, u_int max_pages, size_t page_size, size_t chunk_size)
Definition: subr_mbpool.c:118
void mtx_init(struct mtx *m, const char *name, const char *type, int opts)
Definition: kern_mutex.c:837
MODULE_VERSION(libmbpool, 1)
#define HCHUNK(H)
Definition: subr_mbpool.c:112
SLIST_HEAD(et_eventtimers_list, eventtimer)
#define MBP_PMSK
Definition: subr_mbpool.c:66
bus_dma_tag_t dmat
Definition: subr_mbpool.c:81
void mtx_destroy(struct mtx *m)
Definition: kern_mutex.c:884
#define HPAGE(H)
Definition: subr_mbpool.c:111
#define N2C(P, PG, C)
Definition: subr_mbpool.c:104
static void mbp_alloc_page(struct mbpool *p)
Definition: subr_mbpool.c:194
bus_dmamap_t map
Definition: subr_mbpool.c:74
void mbp_card_free(struct mbpool *p)
Definition: subr_mbpool.c:292
size_t page_size
Definition: subr_mbpool.c:83
void * mbp_get_keep(struct mbpool *p, uint32_t h)
Definition: subr_mbpool.c:368