3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
27 * Author: Hartmut Brandt <harti@freebsd.org>
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.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>
41 #include <machine/bus.h>
43 #include <sys/mbpool.h>
45 MODULE_VERSION(libmbpool
, 1);
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.
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
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 */
70 SLIST_ENTRY(mbfree
) link
; /* link on free list */
74 bus_dmamap_t map
; /* map for this page */
75 bus_addr_t phy
; /* physical address */
76 void *va
; /* the memory */
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 */
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 */
93 static MALLOC_DEFINE(M_MBPOOL
, "mbpools", "mbuf pools");
96 * Make a trail pointer from a chunk pointer
98 #define C2T(P, C) ((struct mbtrail *)((char *)(C) + (P)->chunk_size - \
99 sizeof(struct mbtrail)))
102 * Make a free chunk pointer from a chunk number
104 #define N2C(P, PG, C) ((struct mbfree *)((char *)(PG)->va + \
105 (C) * (P)->chunk_size))
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)
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
)
123 if (max_pages
> MBPOOL_MAX_MAXPAGES
|| chunk_size
== 0)
125 nchunks
= page_size
/ chunk_size
;
126 if (nchunks
== 0 || nchunks
> MBPOOL_MAX_CHUNKS
)
129 (*pp
) = malloc(sizeof(struct mbpool
) +
130 max_pages
* sizeof(struct mbpage
),
131 M_MBPOOL
, M_WAITOK
| M_ZERO
);
135 (*pp
)->max_pages
= max_pages
;
136 (*pp
)->page_size
= page_size
;
137 (*pp
)->chunk_size
= chunk_size
;
138 (*pp
)->nchunks
= nchunks
;
140 SLIST_INIT(&(*pp
)->free_list
);
141 mtx_init(&(*pp
)->free_lock
, name
, NULL
, MTX_DEF
);
150 mbp_destroy(struct mbpool
*p
)
159 for (i
= 0; i
< p
->npages
; i
++) {
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
);
172 bus_dmamap_unload(p
->dmat
, pg
->map
);
173 bus_dmamem_free(p
->dmat
, pg
->va
, pg
->map
);
175 mtx_destroy(&p
->free_lock
);
181 * Helper function when loading a one segment DMA buffer.
184 mbp_callback(void *arg
, bus_dma_segment_t
*segs
, int nsegs
, int error
)
187 *(bus_addr_t
*)arg
= segs
[0].ds_addr
;
191 * Allocate a new page
194 mbp_alloc_page(struct mbpool
*p
)
202 if (p
->npages
== p
->max_pages
) {
204 printf("%s: (%s) page limit reached %u\n", __func__
,
205 p
->name
, p
->max_pages
);
209 pg
= &p
->pages
[p
->npages
];
211 error
= bus_dmamem_alloc(p
->dmat
, &pg
->va
, BUS_DMA_NOWAIT
, &pg
->map
);
217 error
= bus_dmamap_load(p
->dmat
, pg
->map
, pg
->va
, p
->page_size
,
218 mbp_callback
, &pg
->phy
, 0);
220 bus_dmamem_free(p
->dmat
, pg
->va
, pg
->map
);
225 for (i
= 0; i
< p
->nchunks
; i
++) {
230 SLIST_INSERT_HEAD(&p
->free_list
, f
, link
);
240 mbp_alloc(struct mbpool
*p
, bus_addr_t
*pap
, uint32_t *hp
)
245 mtx_lock(&p
->free_lock
);
246 if ((cf
= SLIST_FIRST(&p
->free_list
)) == NULL
) {
248 cf
= SLIST_FIRST(&p
->free_list
);
251 mtx_unlock(&p
->free_lock
);
254 SLIST_REMOVE_HEAD(&p
->free_list
, link
);
255 mtx_unlock(&p
->free_lock
);
259 *pap
= p
->pages
[t
->page
].phy
+ t
->chunk
* p
->chunk_size
;
260 *hp
= HMAKE(t
->page
, t
->chunk
);
262 t
->page
|= MBP_CARD
| MBP_USED
;
271 mbp_free(struct mbpool
*p
, void *ptr
)
275 mtx_lock(&p
->free_lock
);
277 t
->page
&= ~(MBP_USED
| MBP_CARD
);
278 SLIST_INSERT_HEAD(&p
->free_list
, (struct mbfree
*)ptr
, link
);
279 mtx_unlock(&p
->free_lock
);
283 * Mbuf system external mbuf free routine
286 mbp_ext_free(void *buf
, void *arg
)
292 * Free all buffers that are marked as beeing on the card
295 mbp_card_free(struct mbpool
*p
)
302 mtx_lock(&p
->free_lock
);
303 for (i
= 0; i
< p
->npages
; i
++) {
305 for (b
= 0; b
< p
->nchunks
; b
++) {
308 if (tr
->page
& MBP_CARD
) {
309 tr
->page
&= MBP_PMSK
;
310 SLIST_INSERT_HEAD(&p
->free_list
, cf
, link
);
314 mtx_unlock(&p
->free_lock
);
321 mbp_count(struct mbpool
*p
, u_int
*used
, u_int
*card
, u_int
*free
)
328 *used
= *card
= *free
= 0;
329 for (i
= 0; i
< p
->npages
; i
++) {
331 for (b
= 0; b
< p
->nchunks
; b
++) {
332 tr
= C2T(p
, N2C(p
, pg
, b
));
333 if (tr
->page
& MBP_CARD
)
335 if (tr
->page
& MBP_USED
)
339 mtx_lock(&p
->free_lock
);
340 SLIST_FOREACH(cf
, &p
->free_list
, link
)
342 mtx_unlock(&p
->free_lock
);
346 * Get the buffer from a handle and clear the card flag.
349 mbp_get(struct mbpool
*p
, uint32_t h
)
354 cf
= N2C(p
, &p
->pages
[HPAGE(h
)], HCHUNK(h
));
358 if (!(tr
->page
& MBP_CARD
))
359 printf("%s: (%s) chunk %u page %u not on card\n", __func__
,
360 p
->name
, HCHUNK(h
), HPAGE(h
));
363 tr
->page
&= ~MBP_CARD
;
368 * Get the buffer from a handle and keep the card flag.
371 mbp_get_keep(struct mbpool
*p
, uint32_t h
)
376 cf
= N2C(p
, &p
->pages
[HPAGE(h
)], HCHUNK(h
));
380 if (!(tr
->page
& MBP_CARD
))
381 printf("%s: (%s) chunk %u page %u not on card\n", __func__
,
382 p
->name
, HCHUNK(h
), HPAGE(h
));
392 mbp_sync(struct mbpool
*p
, uint32_t h
, bus_addr_t off
, bus_size_t len
, u_int op
)
396 bus_dmamap_sync_size(p
->dmat
, p
->pages
[HPAGE(h
)].map
,
397 HCHUNK(h
) * p
->chunk_size
+ off
, len
, op
);