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