subr_mbpool.c revision 123263
1178825Sdfr/*
2233294Sstas * Copyright (c) 2003
3233294Sstas *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4233294Sstas * 	All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr * 1. Redistributions of source code must retain the above copyright
10233294Sstas *    notice, this list of conditions and the following disclaimer.
11233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
12178825Sdfr *    notice, this list of conditions and the following disclaimer in the
13233294Sstas *    documentation and/or other materials provided with the distribution.
14233294Sstas *
15233294Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25178825Sdfr * SUCH DAMAGE.
26178825Sdfr *
27178825Sdfr * Author: Hartmut Brandt <harti@freebsd.org>
28178825Sdfr */
29178825Sdfr
30178825Sdfr#include <sys/cdefs.h>
31178825Sdfr__FBSDID("$FreeBSD: head/sys/kern/subr_mbpool.c 123263 2003-12-07 21:53:41Z truckman $");
32178825Sdfr
33178825Sdfr#include <sys/param.h>
34178825Sdfr#include <sys/lock.h>
35178825Sdfr#include <sys/mutex.h>
36233294Sstas#include <sys/kernel.h>
37178825Sdfr#include <sys/systm.h>
38178825Sdfr#include <sys/malloc.h>
39178825Sdfr#include <sys/module.h>
40178825Sdfr
41178825Sdfr#include <machine/bus.h>
42178825Sdfr
43178825Sdfr#include <sys/mbpool.h>
44178825Sdfr
45178825SdfrMODULE_VERSION(libmbpool, 1);
46178825Sdfr
47178825Sdfr/*
48178825Sdfr * Memory is allocated as DMA-able pages. Each page is divided into a number
49233294Sstas * of equal chunks where the last 4 bytes of each chunk are occupied by
50178825Sdfr * the page number and the chunk number. The caller must take these four
51178825Sdfr * bytes into account when specifying the chunk size. Each page is mapped by
52178825Sdfr * its own DMA map using the user specified DMA tag.
53178825Sdfr *
54178825Sdfr * Each chunk has a used and a card bit in the high bits of its page number.
55178825Sdfr *  0    0	chunk is free and may be allocated
56178825Sdfr *  1    1	chunk has been given to the interface
57178825Sdfr *  0    1	chunk is traveling through the system
58178825Sdfr *  1    0	illegal
59178825Sdfr */
60178825Sdfrstruct mbtrail {
61233294Sstas	uint16_t	chunk;
62178825Sdfr	uint16_t	page;
63178825Sdfr};
64178825Sdfr#define	MBP_CARD	0x8000
65178825Sdfr#define	MBP_USED	0x4000
66178825Sdfr#define	MBP_PMSK	0x3fff		/* page number mask */
67178825Sdfr#define	MBP_CMSK	0x01ff		/* chunk number mask */
68178825Sdfr
69178825Sdfrstruct mbfree {
70178825Sdfr	SLIST_ENTRY(mbfree) link;	/* link on free list */
71178825Sdfr};
72178825Sdfr
73178825Sdfrstruct mbpage {
74178825Sdfr	bus_dmamap_t	map;		/* map for this page */
75178825Sdfr	bus_addr_t	phy;		/* physical address */
76178825Sdfr	void		*va;		/* the memory */
77178825Sdfr};
78178825Sdfr
79178825Sdfrstruct mbpool {
80178825Sdfr	const char	*name;		/* a name for this pool */
81178825Sdfr	bus_dma_tag_t	dmat;		/* tag for mapping */
82178825Sdfr	u_int		max_pages;	/* maximum number of pages */
83178825Sdfr	size_t		page_size;	/* size of each allocation */
84178825Sdfr	size_t		chunk_size;	/* size of each external mbuf */
85178825Sdfr
86178825Sdfr	struct mtx	free_lock;	/* lock of free list */
87178825Sdfr	SLIST_HEAD(, mbfree) free_list;	/* free list */
88178825Sdfr	u_int		npages;		/* current number of pages */
89178825Sdfr	u_int		nchunks;	/* chunks per page */
90178825Sdfr	struct mbpage	pages[];	/* pages */
91178825Sdfr};
92178825Sdfr
93178825Sdfrstatic MALLOC_DEFINE(M_MBPOOL, "mbpools", "mbuf pools");
94178825Sdfr
95178825Sdfr/*
96178825Sdfr * Make a trail pointer from a chunk pointer
97178825Sdfr */
98178825Sdfr#define	C2T(P, C)	((struct mbtrail *)((char *)(C) + (P)->chunk_size - \
99178825Sdfr			    sizeof(struct mbtrail)))
100178825Sdfr
101178825Sdfr/*
102178825Sdfr * Make a free chunk pointer from a chunk number
103178825Sdfr */
104178825Sdfr#define	N2C(P, PG, C)	((struct mbfree *)((char *)(PG)->va + \
105178825Sdfr			    (C) * (P)->chunk_size))
106178825Sdfr
107178825Sdfr/*
108178825Sdfr * Make/parse handles
109178825Sdfr */
110178825Sdfr#define	HMAKE(P, C)	((((P) & MBP_PMSK) << 16) | ((C) << 7))
111178825Sdfr#define	HPAGE(H)	(((H) >> 16) & MBP_PMSK)
112178825Sdfr#define	HCHUNK(H)	(((H) >>  7) & MBP_CMSK)
113178825Sdfr
114178825Sdfr/*
115178825Sdfr * initialize a pool
116178825Sdfr */
117178825Sdfrint
118178825Sdfrmbp_create(struct mbpool **pp, const char *name, bus_dma_tag_t dmat,
119178825Sdfr    u_int max_pages, size_t page_size, size_t chunk_size)
120178825Sdfr{
121233294Sstas	u_int nchunks;
122178825Sdfr
123178825Sdfr	if (max_pages > MBPOOL_MAX_MAXPAGES || chunk_size == 0)
124178825Sdfr		return (EINVAL);
125178825Sdfr	nchunks = page_size / chunk_size;
126178825Sdfr	if (nchunks == 0 || nchunks > MBPOOL_MAX_CHUNKS)
127178825Sdfr		return (EINVAL);
128178825Sdfr
129178825Sdfr	(*pp) = malloc(sizeof(struct mbpool) +
130178825Sdfr	    max_pages * sizeof(struct mbpage),
131178825Sdfr	    M_MBPOOL, M_WAITOK | M_ZERO);
132178825Sdfr
133178825Sdfr	(*pp)->name = name;
134178825Sdfr	(*pp)->dmat = dmat;
135178825Sdfr	(*pp)->max_pages = max_pages;
136178825Sdfr	(*pp)->page_size = page_size;
137178825Sdfr	(*pp)->chunk_size = chunk_size;
138178825Sdfr	(*pp)->nchunks = nchunks;
139178825Sdfr
140178825Sdfr	SLIST_INIT(&(*pp)->free_list);
141178825Sdfr	mtx_init(&(*pp)->free_lock, name, NULL, MTX_DEF);
142178825Sdfr
143178825Sdfr	return (0);
144178825Sdfr}
145178825Sdfr
146178825Sdfr/*
147178825Sdfr * destroy a pool
148178825Sdfr */
149178825Sdfrvoid
150178825Sdfrmbp_destroy(struct mbpool *p)
151178825Sdfr{
152178825Sdfr	u_int i;
153178825Sdfr	struct mbpage *pg;
154178825Sdfr#ifdef DIAGNOSTIC
155178825Sdfr	struct mbtrail *tr;
156178825Sdfr	u_int b;
157178825Sdfr#endif
158178825Sdfr
159178825Sdfr	for (i = 0; i < p->npages; i++) {
160178825Sdfr		pg = &p->pages[i];
161178825Sdfr#ifdef DIAGNOSTIC
162178825Sdfr		for (b = 0; b < p->nchunks; b++) {
163178825Sdfr			tr = C2T(p, N2C(p, pg, b));
164178825Sdfr			if (tr->page & MBP_CARD)
165178825Sdfr				printf("%s: (%s) buf still on card"
166178825Sdfr				    " %u/%u\n", __func__, p->name, i, b);
167178825Sdfr			if (tr->page & MBP_USED)
168178825Sdfr				printf("%s: (%s) sbuf still in use"
169178825Sdfr				    " %u/%u\n", __func__, p->name, i, b);
170178825Sdfr		}
171178825Sdfr#endif
172178825Sdfr		bus_dmamap_unload(p->dmat, pg->map);
173178825Sdfr		bus_dmamem_free(p->dmat, pg->va, pg->map);
174178825Sdfr	}
175178825Sdfr	mtx_destroy(&p->free_lock);
176233294Sstas
177178825Sdfr	free(p, M_MBPOOL);
178178825Sdfr}
179178825Sdfr
180178825Sdfr/*
181178825Sdfr * Helper function when loading a one segment DMA buffer.
182178825Sdfr */
183178825Sdfrstatic void
184178825Sdfrmbp_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
185178825Sdfr{
186178825Sdfr	if (error == 0)
187233294Sstas		*(bus_addr_t *)arg = segs[0].ds_addr;
188178825Sdfr}
189178825Sdfr
190178825Sdfr/*
191178825Sdfr * Allocate a new page
192178825Sdfr */
193178825Sdfrstatic void
194178825Sdfrmbp_alloc_page(struct mbpool *p)
195178825Sdfr{
196178825Sdfr	int error;
197178825Sdfr	struct mbpage *pg;
198178825Sdfr	u_int i;
199178825Sdfr	struct mbfree *f;
200178825Sdfr	struct mbtrail *t;
201178825Sdfr
202178825Sdfr	if (p->npages == p->max_pages) {
203178825Sdfr#ifdef DIAGNOSTIC
204178825Sdfr		printf("%s: (%s) page limit reached %u\n", __func__,
205178825Sdfr		    p->name, p->max_pages);
206178825Sdfr#endif
207178825Sdfr		return;
208178825Sdfr	}
209178825Sdfr	pg = &p->pages[p->npages];
210178825Sdfr
211178825Sdfr	error = bus_dmamem_alloc(p->dmat, &pg->va, BUS_DMA_NOWAIT, &pg->map);
212178825Sdfr	if (error != 0) {
213178825Sdfr		free(pg, M_MBPOOL);
214178825Sdfr		return;
215178825Sdfr	}
216178825Sdfr
217178825Sdfr	error = bus_dmamap_load(p->dmat, pg->map, pg->va, p->page_size,
218178825Sdfr	    mbp_callback, &pg->phy, 0);
219178825Sdfr	if (error != 0) {
220178825Sdfr		bus_dmamem_free(p->dmat, pg->va, pg->map);
221178825Sdfr		free(pg, M_MBPOOL);
222178825Sdfr		return;
223178825Sdfr	}
224178825Sdfr
225178825Sdfr	for (i = 0; i < p->nchunks; i++) {
226178825Sdfr		f = N2C(p, pg, i);
227178825Sdfr		t = C2T(p, f);
228178825Sdfr		t->page = p->npages;
229178825Sdfr		t->chunk = i;
230178825Sdfr		SLIST_INSERT_HEAD(&p->free_list, f, link);
231178825Sdfr	}
232178825Sdfr
233178825Sdfr	p->npages++;
234178825Sdfr}
235178825Sdfr
236178825Sdfr/*
237178825Sdfr * allocate a chunk
238178825Sdfr */
239178825Sdfrvoid *
240178825Sdfrmbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp)
241178825Sdfr{
242178825Sdfr	struct mbfree *cf;
243178825Sdfr	struct mbtrail *t;
244178825Sdfr
245178825Sdfr	mtx_lock(&p->free_lock);
246178825Sdfr	if ((cf = SLIST_FIRST(&p->free_list)) == NULL) {
247178825Sdfr		mbp_alloc_page(p);
248233294Sstas		cf = SLIST_FIRST(&p->free_list);
249178825Sdfr	}
250178825Sdfr	if (cf == NULL) {
251178825Sdfr		mtx_unlock(&p->free_lock);
252178825Sdfr		return (NULL);
253178825Sdfr	}
254178825Sdfr	SLIST_REMOVE_HEAD(&p->free_list, link);
255178825Sdfr	mtx_unlock(&p->free_lock);
256178825Sdfr
257178825Sdfr	t = C2T(p, cf);
258178825Sdfr
259178825Sdfr	*pap = p->pages[t->page].phy + t->chunk * p->chunk_size;
260178825Sdfr	*hp = HMAKE(t->page, t->chunk);
261178825Sdfr
262178825Sdfr	t->page |= MBP_CARD | MBP_USED;
263178825Sdfr
264178825Sdfr	return (cf);
265178825Sdfr}
266178825Sdfr
267178825Sdfr/*
268178825Sdfr * Free a chunk
269178825Sdfr */
270178825Sdfrvoid
271178825Sdfrmbp_free(struct mbpool *p, void *ptr)
272178825Sdfr{
273178825Sdfr	struct mbtrail *t;
274178825Sdfr
275178825Sdfr	mtx_lock(&p->free_lock);
276178825Sdfr	t = C2T(p, ptr);
277178825Sdfr	t->page &= ~(MBP_USED | MBP_CARD);
278178825Sdfr	SLIST_INSERT_HEAD(&p->free_list, (struct mbfree *)ptr, link);
279178825Sdfr	mtx_unlock(&p->free_lock);
280178825Sdfr}
281178825Sdfr
282178825Sdfr/*
283178825Sdfr * Mbuf system external mbuf free routine
284178825Sdfr */
285178825Sdfrvoid
286178825Sdfrmbp_ext_free(void *buf, void *arg)
287178825Sdfr{
288178825Sdfr	mbp_free(arg, buf);
289178825Sdfr}
290178825Sdfr
291178825Sdfr/*
292178825Sdfr * Free all buffers that are marked as beeing on the card
293178825Sdfr */
294178825Sdfrvoid
295178825Sdfrmbp_card_free(struct mbpool *p)
296178825Sdfr{
297178825Sdfr	u_int i, b;
298178825Sdfr	struct mbpage *pg;
299178825Sdfr	struct mbtrail *tr;
300178825Sdfr	struct mbfree *cf;
301178825Sdfr
302178825Sdfr	mtx_lock(&p->free_lock);
303178825Sdfr	for (i = 0; i < p->npages; i++) {
304178825Sdfr		pg = &p->pages[i];
305178825Sdfr		for (b = 0; b < p->nchunks; b++) {
306178825Sdfr			cf = N2C(p, pg, b);
307178825Sdfr			tr = C2T(p, cf);
308178825Sdfr			if (tr->page & MBP_CARD) {
309178825Sdfr				tr->page &= MBP_PMSK;
310178825Sdfr				SLIST_INSERT_HEAD(&p->free_list, cf, link);
311178825Sdfr			}
312178825Sdfr		}
313178825Sdfr	}
314178825Sdfr	mtx_unlock(&p->free_lock);
315178825Sdfr}
316178825Sdfr
317178825Sdfr/*
318233294Sstas * Count buffers
319178825Sdfr */
320178825Sdfrvoid
321178825Sdfrmbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free)
322178825Sdfr{
323178825Sdfr	u_int i, b;
324178825Sdfr	struct mbpage *pg;
325178825Sdfr	struct mbtrail *tr;
326178825Sdfr	struct mbfree *cf;
327178825Sdfr
328178825Sdfr	*used = *card = *free = 0;
329178825Sdfr	for (i = 0; i < p->npages; i++) {
330178825Sdfr		pg = &p->pages[i];
331178825Sdfr		for (b = 0; b < p->nchunks; b++) {
332178825Sdfr			tr = C2T(p, N2C(p, pg, b));
333178825Sdfr			if (tr->page & MBP_CARD)
334178825Sdfr				(*card)++;
335178825Sdfr			if (tr->page & MBP_USED)
336178825Sdfr				(*used)++;
337178825Sdfr		}
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