/*- * Copyright (c) 2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Hartmut Brandt */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include MODULE_VERSION(libmbpool, 1); /* * Memory is allocated as DMA-able pages. Each page is divided into a number * of equal chunks where the last 4 bytes of each chunk are occupied by * the page number and the chunk number. The caller must take these four * bytes into account when specifying the chunk size. Each page is mapped by * its own DMA map using the user specified DMA tag. * * Each chunk has a used and a card bit in the high bits of its page number. * 0 0 chunk is free and may be allocated * 1 1 chunk has been given to the interface * 0 1 chunk is traveling through the system * 1 0 illegal */ struct mbtrail { uint16_t chunk; uint16_t page; }; #define MBP_CARD 0x8000 #define MBP_USED 0x4000 #define MBP_PMSK 0x3fff /* page number mask */ #define MBP_CMSK 0x01ff /* chunk number mask */ struct mbfree { SLIST_ENTRY(mbfree) link; /* link on free list */ }; struct mbpage { bus_dmamap_t map; /* map for this page */ bus_addr_t phy; /* physical address */ void *va; /* the memory */ }; struct mbpool { const char *name; /* a name for this pool */ bus_dma_tag_t dmat; /* tag for mapping */ u_int max_pages; /* maximum number of pages */ size_t page_size; /* size of each allocation */ size_t chunk_size; /* size of each external mbuf */ struct mtx free_lock; /* lock of free list */ SLIST_HEAD(, mbfree) free_list; /* free list */ u_int npages; /* current number of pages */ u_int nchunks; /* chunks per page */ struct mbpage pages[]; /* pages */ }; static MALLOC_DEFINE(M_MBPOOL, "mbpools", "mbuf pools"); /* * Make a trail pointer from a chunk pointer */ #define C2T(P, C) ((struct mbtrail *)((char *)(C) + (P)->chunk_size - \ sizeof(struct mbtrail))) /* * Make a free chunk pointer from a chunk number */ #define N2C(P, PG, C) ((struct mbfree *)((char *)(PG)->va + \ (C) * (P)->chunk_size)) /* * Make/parse handles */ #define HMAKE(P, C) ((((P) & MBP_PMSK) << 16) | ((C) << 7)) #define HPAGE(H) (((H) >> 16) & MBP_PMSK) #define HCHUNK(H) (((H) >> 7) & MBP_CMSK) /* * initialize a pool */ 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) { u_int nchunks; if (max_pages > MBPOOL_MAX_MAXPAGES || chunk_size == 0) return (EINVAL); nchunks = page_size / chunk_size; if (nchunks == 0 || nchunks > MBPOOL_MAX_CHUNKS) return (EINVAL); (*pp) = malloc(sizeof(struct mbpool) + max_pages * sizeof(struct mbpage), M_MBPOOL, M_WAITOK | M_ZERO); (*pp)->name = name; (*pp)->dmat = dmat; (*pp)->max_pages = max_pages; (*pp)->page_size = page_size; (*pp)->chunk_size = chunk_size; (*pp)->nchunks = nchunks; SLIST_INIT(&(*pp)->free_list); mtx_init(&(*pp)->free_lock, name, NULL, MTX_DEF); return (0); } /* * destroy a pool */ void mbp_destroy(struct mbpool *p) { u_int i; struct mbpage *pg; #ifdef DIAGNOSTIC struct mbtrail *tr; u_int b; #endif for (i = 0; i < p->npages; i++) { pg = &p->pages[i]; #ifdef DIAGNOSTIC for (b = 0; b < p->nchunks; b++) { tr = C2T(p, N2C(p, pg, b)); if (tr->page & MBP_CARD) printf("%s: (%s) buf still on card" " %u/%u\n", __func__, p->name, i, b); if (tr->page & MBP_USED) printf("%s: (%s) sbuf still in use" " %u/%u\n", __func__, p->name, i, b); } #endif bus_dmamap_unload(p->dmat, pg->map); bus_dmamem_free(p->dmat, pg->va, pg->map); } mtx_destroy(&p->free_lock); free(p, M_MBPOOL); } /* * Helper function when loading a one segment DMA buffer. */ static void mbp_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error == 0) *(bus_addr_t *)arg = segs[0].ds_addr; } /* * Allocate a new page */ static void mbp_alloc_page(struct mbpool *p) { int error; struct mbpage *pg; u_int i; struct mbfree *f; struct mbtrail *t; if (p->npages == p->max_pages) { #ifdef DIAGNOSTIC printf("%s: (%s) page limit reached %u\n", __func__, p->name, p->max_pages); #endif return; } pg = &p->pages[p->npages]; error = bus_dmamem_alloc(p->dmat, &pg->va, BUS_DMA_NOWAIT, &pg->map); if (error != 0) { free(pg, M_MBPOOL); return; } error = bus_dmamap_load(p->dmat, pg->map, pg->va, p->page_size, mbp_callback, &pg->phy, 0); if (error != 0) { bus_dmamem_free(p->dmat, pg->va, pg->map); free(pg, M_MBPOOL); return; } for (i = 0; i < p->nchunks; i++) { f = N2C(p, pg, i); t = C2T(p, f); t->page = p->npages; t->chunk = i; SLIST_INSERT_HEAD(&p->free_list, f, link); } p->npages++; } /* * allocate a chunk */ void * mbp_alloc(struct mbpool *p, bus_addr_t *pap, uint32_t *hp) { struct mbfree *cf; struct mbtrail *t; mtx_lock(&p->free_lock); if ((cf = SLIST_FIRST(&p->free_list)) == NULL) { mbp_alloc_page(p); cf = SLIST_FIRST(&p->free_list); } if (cf == NULL) { mtx_unlock(&p->free_lock); return (NULL); } SLIST_REMOVE_HEAD(&p->free_list, link); mtx_unlock(&p->free_lock); t = C2T(p, cf); *pap = p->pages[t->page].phy + t->chunk * p->chunk_size; *hp = HMAKE(t->page, t->chunk); t->page |= MBP_CARD | MBP_USED; return (cf); } /* * Free a chunk */ void mbp_free(struct mbpool *p, void *ptr) { struct mbtrail *t; mtx_lock(&p->free_lock); t = C2T(p, ptr); t->page &= ~(MBP_USED | MBP_CARD); SLIST_INSERT_HEAD(&p->free_list, (struct mbfree *)ptr, link); mtx_unlock(&p->free_lock); } /* * Mbuf system external mbuf free routine */ int mbp_ext_free(struct mbuf *m, void *buf, void *arg) { mbp_free(arg, buf); return (EXT_FREE_OK); } /* * Free all buffers that are marked as beeing on the card */ void mbp_card_free(struct mbpool *p) { u_int i, b; struct mbpage *pg; struct mbtrail *tr; struct mbfree *cf; mtx_lock(&p->free_lock); for (i = 0; i < p->npages; i++) { pg = &p->pages[i]; for (b = 0; b < p->nchunks; b++) { cf = N2C(p, pg, b); tr = C2T(p, cf); if (tr->page & MBP_CARD) { tr->page &= MBP_PMSK; SLIST_INSERT_HEAD(&p->free_list, cf, link); } } } mtx_unlock(&p->free_lock); } /* * Count buffers */ void mbp_count(struct mbpool *p, u_int *used, u_int *card, u_int *free) { u_int i, b; struct mbpage *pg; struct mbtrail *tr; struct mbfree *cf; *used = *card = *free = 0; for (i = 0; i < p->npages; i++) { pg = &p->pages[i]; for (b = 0; b < p->nchunks; b++) { tr = C2T(p, N2C(p, pg, b)); if (tr->page & MBP_CARD) (*card)++; if (tr->page & MBP_USED) (*used)++; } } mtx_lock(&p->free_lock); SLIST_FOREACH(cf, &p->free_list, link) (*free)++; mtx_unlock(&p->free_lock); } /* * Get the buffer from a handle and clear the card flag. */ void * mbp_get(struct mbpool *p, uint32_t h) { struct mbfree *cf; struct mbtrail *tr; cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h)); tr = C2T(p, cf); #ifdef DIAGNOSTIC if (!(tr->page & MBP_CARD)) printf("%s: (%s) chunk %u page %u not on card\n", __func__, p->name, HCHUNK(h), HPAGE(h)); #endif tr->page &= ~MBP_CARD; return (cf); } /* * Get the buffer from a handle and keep the card flag. */ void * mbp_get_keep(struct mbpool *p, uint32_t h) { struct mbfree *cf; struct mbtrail *tr; cf = N2C(p, &p->pages[HPAGE(h)], HCHUNK(h)); tr = C2T(p, cf); #ifdef DIAGNOSTIC if (!(tr->page & MBP_CARD)) printf("%s: (%s) chunk %u page %u not on card\n", __func__, p->name, HCHUNK(h), HPAGE(h)); #endif return (cf); } /* * sync the chunk */ void mbp_sync(struct mbpool *p, uint32_t h, bus_addr_t off, bus_size_t len, u_int op) { #if 0 bus_dmamap_sync_size(p->dmat, p->pages[HPAGE(h)].map, HCHUNK(h) * p->chunk_size + off, len, op); #endif }