subr_mbpool.c revision 139804
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: head/sys/kern/subr_mbpool.c 139804 2005-01-06 23:35:40Z imp $");
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
45MODULE_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 */
60struct 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
69struct mbfree {
70	SLIST_ENTRY(mbfree) link;	/* link on free list */
71};
72
73struct mbpage {
74	bus_dmamap_t	map;		/* map for this page */
75	bus_addr_t	phy;		/* physical address */
76	void		*va;		/* the memory */
77};
78
79struct 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
93static 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 */
117int
118mbp_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 */
149void
150mbp_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 */
183static void
184mbp_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 */
193static void
194mbp_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		free(pg, M_MBPOOL);
214		return;
215	}
216
217	error = bus_dmamap_load(p->dmat, pg->map, pg->va, p->page_size,
218	    mbp_callback, &pg->phy, 0);
219	if (error != 0) {
220		bus_dmamem_free(p->dmat, pg->va, pg->map);
221		free(pg, M_MBPOOL);
222		return;
223	}
224
225	for (i = 0; i < p->nchunks; i++) {
226		f = N2C(p, pg, i);
227		t = C2T(p, f);
228		t->page = p->npages;
229		t->chunk = i;
230		SLIST_INSERT_HEAD(&p->free_list, f, link);
231	}
232
233	p->npages++;
234}
235
236/*
237 * allocate a chunk
238 */
239void *
240mbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp)
241{
242	struct mbfree *cf;
243	struct mbtrail *t;
244
245	mtx_lock(&p->free_lock);
246	if ((cf = SLIST_FIRST(&p->free_list)) == NULL) {
247		mbp_alloc_page(p);
248		cf = SLIST_FIRST(&p->free_list);
249	}
250	if (cf == NULL) {
251		mtx_unlock(&p->free_lock);
252		return (NULL);
253	}
254	SLIST_REMOVE_HEAD(&p->free_list, link);
255	mtx_unlock(&p->free_lock);
256
257	t = C2T(p, cf);
258
259	*pap = p->pages[t->page].phy + t->chunk * p->chunk_size;
260	*hp = HMAKE(t->page, t->chunk);
261
262	t->page |= MBP_CARD | MBP_USED;
263
264	return (cf);
265}
266
267/*
268 * Free a chunk
269 */
270void
271mbp_free(struct mbpool *p, void *ptr)
272{
273	struct mbtrail *t;
274
275	mtx_lock(&p->free_lock);
276	t = C2T(p, ptr);
277	t->page &= ~(MBP_USED | MBP_CARD);
278	SLIST_INSERT_HEAD(&p->free_list, (struct mbfree *)ptr, link);
279	mtx_unlock(&p->free_lock);
280}
281
282/*
283 * Mbuf system external mbuf free routine
284 */
285void
286mbp_ext_free(void *buf, void *arg)
287{
288	mbp_free(arg, buf);
289}
290
291/*
292 * Free all buffers that are marked as beeing on the card
293 */
294void
295mbp_card_free(struct mbpool *p)
296{
297	u_int i, b;
298	struct mbpage *pg;
299	struct mbtrail *tr;
300	struct mbfree *cf;
301
302	mtx_lock(&p->free_lock);
303	for (i = 0; i < p->npages; i++) {
304		pg = &p->pages[i];
305		for (b = 0; b < p->nchunks; b++) {
306			cf = N2C(p, pg, b);
307			tr = C2T(p, cf);
308			if (tr->page & MBP_CARD) {
309				tr->page &= MBP_PMSK;
310				SLIST_INSERT_HEAD(&p->free_list, cf, link);
311			}
312		}
313	}
314	mtx_unlock(&p->free_lock);
315}
316
317/*
318 * Count buffers
319 */
320void
321mbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free)
322{
323	u_int i, b;
324	struct mbpage *pg;
325	struct mbtrail *tr;
326	struct mbfree *cf;
327
328	*used = *card = *free = 0;
329	for (i = 0; i < p->npages; i++) {
330		pg = &p->pages[i];
331		for (b = 0; b < p->nchunks; b++) {
332			tr = C2T(p, N2C(p, pg, b));
333			if (tr->page & MBP_CARD)
334				(*card)++;
335			if (tr->page & MBP_USED)
336				(*used)++;
337		}
338	}
339	mtx_lock(&p->free_lock);
340	SLIST_FOREACH(cf, &p->free_list, link)
341		*free++;
342	mtx_unlock(&p->free_lock);
343}
344
345/*
346 * Get the buffer from a handle and clear the card flag.
347 */
348void *
349mbp_get(struct mbpool *p, uint32_t h)
350{
351	struct mbfree *cf;
352	struct mbtrail *tr;
353
354	cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
355	tr = C2T(p, cf);
356
357#ifdef DIAGNOSTIC
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));
361#endif
362
363	tr->page &= ~MBP_CARD;
364	return (cf);
365}
366
367/*
368 * Get the buffer from a handle and keep the card flag.
369 */
370void *
371mbp_get_keep(struct mbpool *p, uint32_t h)
372{
373	struct mbfree *cf;
374	struct mbtrail *tr;
375
376	cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h));
377	tr = C2T(p, cf);
378
379#ifdef DIAGNOSTIC
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));
383#endif
384
385	return (cf);
386}
387
388/*
389 * sync the chunk
390 */
391void
392mbp_sync(struct mbpool *p, uint32_t h, bus_addr_t off, bus_size_t len, u_int op)
393{
394
395#if 0
396	bus_dmamap_sync_size(p->dmat, p->pages[HPAGE(h)].map,
397	    HCHUNK(h) * p->chunk_size + off, len, op);
398#endif
399}
400