1ccdc3305SLuigi Rizzo /* 22579e2d7SLuigi Rizzo * Copyright (C) 2012-2013 Matteo Landi, Luigi Rizzo, Giuseppe Lettieri. All rights reserved. 3ccdc3305SLuigi Rizzo * 4ccdc3305SLuigi Rizzo * Redistribution and use in source and binary forms, with or without 5ccdc3305SLuigi Rizzo * modification, are permitted provided that the following conditions 6ccdc3305SLuigi Rizzo * are met: 7ccdc3305SLuigi Rizzo * 1. Redistributions of source code must retain the above copyright 8ccdc3305SLuigi Rizzo * notice, this list of conditions and the following disclaimer. 9ccdc3305SLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 10ccdc3305SLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 11ccdc3305SLuigi Rizzo * documentation and/or other materials provided with the distribution. 12ccdc3305SLuigi Rizzo * 13ccdc3305SLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14ccdc3305SLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15ccdc3305SLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16ccdc3305SLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17ccdc3305SLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18ccdc3305SLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19ccdc3305SLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20ccdc3305SLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21ccdc3305SLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22ccdc3305SLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23ccdc3305SLuigi Rizzo * SUCH DAMAGE. 24ccdc3305SLuigi Rizzo */ 25ccdc3305SLuigi Rizzo 26ccdc3305SLuigi Rizzo /* 27ccdc3305SLuigi Rizzo * $FreeBSD$ 28ccdc3305SLuigi Rizzo * 298241616dSLuigi Rizzo * (New) memory allocator for netmap 30ccdc3305SLuigi Rizzo */ 31ccdc3305SLuigi Rizzo 32ccdc3305SLuigi Rizzo /* 332579e2d7SLuigi Rizzo * This allocator creates three memory pools: 34ccdc3305SLuigi Rizzo * nm_if_pool for the struct netmap_if 35ccdc3305SLuigi Rizzo * nm_ring_pool for the struct netmap_ring 36ccdc3305SLuigi Rizzo * nm_buf_pool for the packet buffers. 37ccdc3305SLuigi Rizzo * 382579e2d7SLuigi Rizzo * that contain netmap objects. Each pool is made of a number of clusters, 392579e2d7SLuigi Rizzo * multiple of a page size, each containing an integer number of objects. 402579e2d7SLuigi Rizzo * The clusters are contiguous in user space but not in the kernel. 412579e2d7SLuigi Rizzo * Only nm_buf_pool needs to be dma-able, 42ccdc3305SLuigi Rizzo * but for convenience use the same type of allocator for all. 43ccdc3305SLuigi Rizzo * 442579e2d7SLuigi Rizzo * Once mapped, the three pools are exported to userspace 45ccdc3305SLuigi Rizzo * as a contiguous block, starting from nm_if_pool. Each 46ccdc3305SLuigi Rizzo * cluster (and pool) is an integral number of pages. 47ccdc3305SLuigi Rizzo * [ . . . ][ . . . . . .][ . . . . . . . . . .] 48ccdc3305SLuigi Rizzo * nm_if nm_ring nm_buf 49ccdc3305SLuigi Rizzo * 50ccdc3305SLuigi Rizzo * The userspace areas contain offsets of the objects in userspace. 51ccdc3305SLuigi Rizzo * When (at init time) we write these offsets, we find out the index 52ccdc3305SLuigi Rizzo * of the object, and from there locate the offset from the beginning 53ccdc3305SLuigi Rizzo * of the region. 54ccdc3305SLuigi Rizzo * 558241616dSLuigi Rizzo * The invididual allocators manage a pool of memory for objects of 568241616dSLuigi Rizzo * the same size. 57ccdc3305SLuigi Rizzo * The pool is split into smaller clusters, whose size is a 58ccdc3305SLuigi Rizzo * multiple of the page size. The cluster size is chosen 59ccdc3305SLuigi Rizzo * to minimize the waste for a given max cluster size 602579e2d7SLuigi Rizzo * (we do it by brute force, as we have relatively few objects 61ccdc3305SLuigi Rizzo * per cluster). 62ccdc3305SLuigi Rizzo * 638241616dSLuigi Rizzo * Objects are aligned to the cache line (64 bytes) rounding up object 648241616dSLuigi Rizzo * sizes when needed. A bitmap contains the state of each object. 658241616dSLuigi Rizzo * Allocation scans the bitmap; this is done only on attach, so we are not 66ccdc3305SLuigi Rizzo * too worried about performance 67ccdc3305SLuigi Rizzo * 688241616dSLuigi Rizzo * For each allocator we can define (thorugh sysctl) the size and 698241616dSLuigi Rizzo * number of each object. Memory is allocated at the first use of a 708241616dSLuigi Rizzo * netmap file descriptor, and can be freed when all such descriptors 718241616dSLuigi Rizzo * have been released (including unmapping the memory). 728241616dSLuigi Rizzo * If memory is scarce, the system tries to get as much as possible 738241616dSLuigi Rizzo * and the sysctl values reflect the actual allocation. 748241616dSLuigi Rizzo * Together with desired values, the sysctl export also absolute 758241616dSLuigi Rizzo * min and maximum values that cannot be overridden. 76ccdc3305SLuigi Rizzo * 778241616dSLuigi Rizzo * struct netmap_if: 788241616dSLuigi Rizzo * variable size, max 16 bytes per ring pair plus some fixed amount. 798241616dSLuigi Rizzo * 1024 bytes should be large enough in practice. 808241616dSLuigi Rizzo * 818241616dSLuigi Rizzo * In the worst case we have one netmap_if per ring in the system. 828241616dSLuigi Rizzo * 838241616dSLuigi Rizzo * struct netmap_ring 842579e2d7SLuigi Rizzo * variable size, 8 byte per slot plus some fixed amount. 858241616dSLuigi Rizzo * Rings can be large (e.g. 4k slots, or >32Kbytes). 868241616dSLuigi Rizzo * We default to 36 KB (9 pages), and a few hundred rings. 878241616dSLuigi Rizzo * 888241616dSLuigi Rizzo * struct netmap_buffer 898241616dSLuigi Rizzo * The more the better, both because fast interfaces tend to have 908241616dSLuigi Rizzo * many slots, and because we may want to use buffers to store 918241616dSLuigi Rizzo * packets in userspace avoiding copies. 928241616dSLuigi Rizzo * Must contain a full frame (eg 1518, or more for vlans, jumbo 938241616dSLuigi Rizzo * frames etc.) plus be nicely aligned, plus some NICs restrict 948241616dSLuigi Rizzo * the size to multiple of 1K or so. Default to 2K 95ccdc3305SLuigi Rizzo */ 96ccdc3305SLuigi Rizzo 978241616dSLuigi Rizzo #define NETMAP_BUF_MAX_NUM 20*4096*2 /* large machine */ 98ccdc3305SLuigi Rizzo 998241616dSLuigi Rizzo #ifdef linux 10028228e08SLuigi Rizzo // XXX a mtx would suffice here 20130415 lr 10128228e08SLuigi Rizzo // #define NMA_LOCK_T safe_spinlock_t 1028241616dSLuigi Rizzo #define NMA_LOCK_T struct semaphore 1038241616dSLuigi Rizzo #define NMA_LOCK_INIT() sema_init(&nm_mem.nm_mtx, 1) 1048241616dSLuigi Rizzo #define NMA_LOCK_DESTROY() 1058241616dSLuigi Rizzo #define NMA_LOCK() down(&nm_mem.nm_mtx) 1068241616dSLuigi Rizzo #define NMA_UNLOCK() up(&nm_mem.nm_mtx) 1078241616dSLuigi Rizzo #else /* !linux */ 1088241616dSLuigi Rizzo #define NMA_LOCK_T struct mtx 1098241616dSLuigi Rizzo #define NMA_LOCK_INIT() mtx_init(&nm_mem.nm_mtx, "netmap memory allocator lock", NULL, MTX_DEF) 1108241616dSLuigi Rizzo #define NMA_LOCK_DESTROY() mtx_destroy(&nm_mem.nm_mtx) 1118241616dSLuigi Rizzo #define NMA_LOCK() mtx_lock(&nm_mem.nm_mtx) 1128241616dSLuigi Rizzo #define NMA_UNLOCK() mtx_unlock(&nm_mem.nm_mtx) 1138241616dSLuigi Rizzo #endif /* linux */ 1148241616dSLuigi Rizzo 1158241616dSLuigi Rizzo enum { 1168241616dSLuigi Rizzo NETMAP_IF_POOL = 0, 1178241616dSLuigi Rizzo NETMAP_RING_POOL, 1188241616dSLuigi Rizzo NETMAP_BUF_POOL, 1198241616dSLuigi Rizzo NETMAP_POOLS_NR 1208241616dSLuigi Rizzo }; 1218241616dSLuigi Rizzo 1228241616dSLuigi Rizzo 1238241616dSLuigi Rizzo struct netmap_obj_params { 1248241616dSLuigi Rizzo u_int size; 1258241616dSLuigi Rizzo u_int num; 1268241616dSLuigi Rizzo }; 1278241616dSLuigi Rizzo 1288241616dSLuigi Rizzo 1298241616dSLuigi Rizzo struct netmap_obj_params netmap_params[NETMAP_POOLS_NR] = { 1308241616dSLuigi Rizzo [NETMAP_IF_POOL] = { 1318241616dSLuigi Rizzo .size = 1024, 1328241616dSLuigi Rizzo .num = 100, 1338241616dSLuigi Rizzo }, 1348241616dSLuigi Rizzo [NETMAP_RING_POOL] = { 1358241616dSLuigi Rizzo .size = 9*PAGE_SIZE, 1368241616dSLuigi Rizzo .num = 200, 1378241616dSLuigi Rizzo }, 1388241616dSLuigi Rizzo [NETMAP_BUF_POOL] = { 1398241616dSLuigi Rizzo .size = 2048, 1408241616dSLuigi Rizzo .num = NETMAP_BUF_MAX_NUM, 1418241616dSLuigi Rizzo }, 1428241616dSLuigi Rizzo }; 1438241616dSLuigi Rizzo 144ccdc3305SLuigi Rizzo 145ccdc3305SLuigi Rizzo struct netmap_obj_pool { 146ccdc3305SLuigi Rizzo char name[16]; /* name of the allocator */ 147ccdc3305SLuigi Rizzo u_int objtotal; /* actual total number of objects. */ 148ccdc3305SLuigi Rizzo u_int objfree; /* number of free objects. */ 149ccdc3305SLuigi Rizzo u_int clustentries; /* actual objects per cluster */ 150ccdc3305SLuigi Rizzo 1518241616dSLuigi Rizzo /* limits */ 1528241616dSLuigi Rizzo u_int objminsize; /* minimum object size */ 1538241616dSLuigi Rizzo u_int objmaxsize; /* maximum object size */ 1548241616dSLuigi Rizzo u_int nummin; /* minimum number of objects */ 1558241616dSLuigi Rizzo u_int nummax; /* maximum number of objects */ 1568241616dSLuigi Rizzo 157ccdc3305SLuigi Rizzo /* the total memory space is _numclusters*_clustsize */ 158ccdc3305SLuigi Rizzo u_int _numclusters; /* how many clusters */ 159ccdc3305SLuigi Rizzo u_int _clustsize; /* cluster size */ 160ccdc3305SLuigi Rizzo u_int _objsize; /* actual object size */ 161ccdc3305SLuigi Rizzo 162ccdc3305SLuigi Rizzo u_int _memtotal; /* _numclusters*_clustsize */ 163ccdc3305SLuigi Rizzo struct lut_entry *lut; /* virt,phys addresses, objtotal entries */ 164ccdc3305SLuigi Rizzo uint32_t *bitmap; /* one bit per buffer, 1 means free */ 1658241616dSLuigi Rizzo uint32_t bitmap_slots; /* number of uint32 entries in bitmap */ 166ccdc3305SLuigi Rizzo }; 167ccdc3305SLuigi Rizzo 1688241616dSLuigi Rizzo 169ccdc3305SLuigi Rizzo struct netmap_mem_d { 1708241616dSLuigi Rizzo NMA_LOCK_T nm_mtx; /* protect the allocator */ 171ccdc3305SLuigi Rizzo u_int nm_totalsize; /* shorthand */ 172ccdc3305SLuigi Rizzo 1738241616dSLuigi Rizzo int finalized; /* !=0 iff preallocation done */ 1748241616dSLuigi Rizzo int lasterr; /* last error for curr config */ 1758241616dSLuigi Rizzo int refcount; /* existing priv structures */ 1768241616dSLuigi Rizzo /* the three allocators */ 1778241616dSLuigi Rizzo struct netmap_obj_pool pools[NETMAP_POOLS_NR]; 1788241616dSLuigi Rizzo }; 1798241616dSLuigi Rizzo 1802579e2d7SLuigi Rizzo /* 1812579e2d7SLuigi Rizzo * nm_mem is the memory allocator used for all physical interfaces 1822579e2d7SLuigi Rizzo * running in netmap mode. 1832579e2d7SLuigi Rizzo * Virtual (VALE) ports will have each its own allocator. 1842579e2d7SLuigi Rizzo */ 1858241616dSLuigi Rizzo static struct netmap_mem_d nm_mem = { /* Our memory allocator. */ 1868241616dSLuigi Rizzo .pools = { 1878241616dSLuigi Rizzo [NETMAP_IF_POOL] = { 1888241616dSLuigi Rizzo .name = "netmap_if", 1898241616dSLuigi Rizzo .objminsize = sizeof(struct netmap_if), 1908241616dSLuigi Rizzo .objmaxsize = 4096, 1918241616dSLuigi Rizzo .nummin = 10, /* don't be stingy */ 1928241616dSLuigi Rizzo .nummax = 10000, /* XXX very large */ 1938241616dSLuigi Rizzo }, 1948241616dSLuigi Rizzo [NETMAP_RING_POOL] = { 1958241616dSLuigi Rizzo .name = "netmap_ring", 1968241616dSLuigi Rizzo .objminsize = sizeof(struct netmap_ring), 1978241616dSLuigi Rizzo .objmaxsize = 32*PAGE_SIZE, 1988241616dSLuigi Rizzo .nummin = 2, 1998241616dSLuigi Rizzo .nummax = 1024, 2008241616dSLuigi Rizzo }, 2018241616dSLuigi Rizzo [NETMAP_BUF_POOL] = { 2028241616dSLuigi Rizzo .name = "netmap_buf", 2038241616dSLuigi Rizzo .objminsize = 64, 2048241616dSLuigi Rizzo .objmaxsize = 65536, 2058241616dSLuigi Rizzo .nummin = 4, 2068241616dSLuigi Rizzo .nummax = 1000000, /* one million! */ 2078241616dSLuigi Rizzo }, 2088241616dSLuigi Rizzo }, 209ccdc3305SLuigi Rizzo }; 210ccdc3305SLuigi Rizzo 2112579e2d7SLuigi Rizzo // XXX logically belongs to nm_mem 212ccdc3305SLuigi Rizzo struct lut_entry *netmap_buffer_lut; /* exported */ 213ccdc3305SLuigi Rizzo 2148241616dSLuigi Rizzo /* memory allocator related sysctls */ 2158241616dSLuigi Rizzo 2168241616dSLuigi Rizzo #define STRINGIFY(x) #x 2178241616dSLuigi Rizzo 2188241616dSLuigi Rizzo #define DECLARE_SYSCTLS(id, name) \ 2198241616dSLuigi Rizzo SYSCTL_INT(_dev_netmap, OID_AUTO, name##_size, \ 2208241616dSLuigi Rizzo CTLFLAG_RW, &netmap_params[id].size, 0, "Requested size of netmap " STRINGIFY(name) "s"); \ 2218241616dSLuigi Rizzo SYSCTL_INT(_dev_netmap, OID_AUTO, name##_curr_size, \ 2228241616dSLuigi Rizzo CTLFLAG_RD, &nm_mem.pools[id]._objsize, 0, "Current size of netmap " STRINGIFY(name) "s"); \ 2238241616dSLuigi Rizzo SYSCTL_INT(_dev_netmap, OID_AUTO, name##_num, \ 2248241616dSLuigi Rizzo CTLFLAG_RW, &netmap_params[id].num, 0, "Requested number of netmap " STRINGIFY(name) "s"); \ 2258241616dSLuigi Rizzo SYSCTL_INT(_dev_netmap, OID_AUTO, name##_curr_num, \ 2268241616dSLuigi Rizzo CTLFLAG_RD, &nm_mem.pools[id].objtotal, 0, "Current number of netmap " STRINGIFY(name) "s") 2278241616dSLuigi Rizzo 2288241616dSLuigi Rizzo DECLARE_SYSCTLS(NETMAP_IF_POOL, if); 2298241616dSLuigi Rizzo DECLARE_SYSCTLS(NETMAP_RING_POOL, ring); 2308241616dSLuigi Rizzo DECLARE_SYSCTLS(NETMAP_BUF_POOL, buf); 231ccdc3305SLuigi Rizzo 232ccdc3305SLuigi Rizzo /* 2332579e2d7SLuigi Rizzo * Convert a userspace offset to a physical address. 2342579e2d7SLuigi Rizzo * XXX only called in the FreeBSD's netmap_mmap() 2352579e2d7SLuigi Rizzo * because in linux we map everything at once. 236ccdc3305SLuigi Rizzo * 2372579e2d7SLuigi Rizzo * First, find the allocator that contains the requested offset, 2382579e2d7SLuigi Rizzo * then locate the cluster through a lookup table. 239ccdc3305SLuigi Rizzo */ 240ccdc3305SLuigi Rizzo static inline vm_paddr_t 241ccdc3305SLuigi Rizzo netmap_ofstophys(vm_offset_t offset) 242ccdc3305SLuigi Rizzo { 243ccdc3305SLuigi Rizzo int i; 244ccdc3305SLuigi Rizzo vm_offset_t o = offset; 2458241616dSLuigi Rizzo struct netmap_obj_pool *p = nm_mem.pools; 246ccdc3305SLuigi Rizzo 2478241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; offset -= p[i]._memtotal, i++) { 2488241616dSLuigi Rizzo if (offset >= p[i]._memtotal) 249ccdc3305SLuigi Rizzo continue; 2502579e2d7SLuigi Rizzo // now lookup the cluster's address 2518241616dSLuigi Rizzo return p[i].lut[offset / p[i]._objsize].paddr + 2528241616dSLuigi Rizzo offset % p[i]._objsize; 253ccdc3305SLuigi Rizzo } 2548241616dSLuigi Rizzo /* this is only in case of errors */ 255b1123b01SLuigi Rizzo D("invalid ofs 0x%x out of 0x%x 0x%x 0x%x", (u_int)o, 2568241616dSLuigi Rizzo p[NETMAP_IF_POOL]._memtotal, 2578241616dSLuigi Rizzo p[NETMAP_IF_POOL]._memtotal 2588241616dSLuigi Rizzo + p[NETMAP_RING_POOL]._memtotal, 2598241616dSLuigi Rizzo p[NETMAP_IF_POOL]._memtotal 2608241616dSLuigi Rizzo + p[NETMAP_RING_POOL]._memtotal 2618241616dSLuigi Rizzo + p[NETMAP_BUF_POOL]._memtotal); 262ccdc3305SLuigi Rizzo return 0; // XXX bad address 263ccdc3305SLuigi Rizzo } 264ccdc3305SLuigi Rizzo 265ccdc3305SLuigi Rizzo /* 266ccdc3305SLuigi Rizzo * we store objects by kernel address, need to find the offset 267ccdc3305SLuigi Rizzo * within the pool to export the value to userspace. 268ccdc3305SLuigi Rizzo * Algorithm: scan until we find the cluster, then add the 269ccdc3305SLuigi Rizzo * actual offset in the cluster 270ccdc3305SLuigi Rizzo */ 271ce2cb792SLuigi Rizzo static ssize_t 272ccdc3305SLuigi Rizzo netmap_obj_offset(struct netmap_obj_pool *p, const void *vaddr) 273ccdc3305SLuigi Rizzo { 274ccdc3305SLuigi Rizzo int i, k = p->clustentries, n = p->objtotal; 275ccdc3305SLuigi Rizzo ssize_t ofs = 0; 276ccdc3305SLuigi Rizzo 277ccdc3305SLuigi Rizzo for (i = 0; i < n; i += k, ofs += p->_clustsize) { 278ccdc3305SLuigi Rizzo const char *base = p->lut[i].vaddr; 279ccdc3305SLuigi Rizzo ssize_t relofs = (const char *) vaddr - base; 280ccdc3305SLuigi Rizzo 281aa76317cSLuigi Rizzo if (relofs < 0 || relofs >= p->_clustsize) 282ccdc3305SLuigi Rizzo continue; 283ccdc3305SLuigi Rizzo 284ccdc3305SLuigi Rizzo ofs = ofs + relofs; 285ccdc3305SLuigi Rizzo ND("%s: return offset %d (cluster %d) for pointer %p", 286ccdc3305SLuigi Rizzo p->name, ofs, i, vaddr); 287ccdc3305SLuigi Rizzo return ofs; 288ccdc3305SLuigi Rizzo } 289ccdc3305SLuigi Rizzo D("address %p is not contained inside any cluster (%s)", 290ccdc3305SLuigi Rizzo vaddr, p->name); 291ccdc3305SLuigi Rizzo return 0; /* An error occurred */ 292ccdc3305SLuigi Rizzo } 293ccdc3305SLuigi Rizzo 294ccdc3305SLuigi Rizzo /* Helper functions which convert virtual addresses to offsets */ 295ccdc3305SLuigi Rizzo #define netmap_if_offset(v) \ 2968241616dSLuigi Rizzo netmap_obj_offset(&nm_mem.pools[NETMAP_IF_POOL], (v)) 297ccdc3305SLuigi Rizzo 298ccdc3305SLuigi Rizzo #define netmap_ring_offset(v) \ 2998241616dSLuigi Rizzo (nm_mem.pools[NETMAP_IF_POOL]._memtotal + \ 3008241616dSLuigi Rizzo netmap_obj_offset(&nm_mem.pools[NETMAP_RING_POOL], (v))) 301ccdc3305SLuigi Rizzo 302ccdc3305SLuigi Rizzo #define netmap_buf_offset(v) \ 3038241616dSLuigi Rizzo (nm_mem.pools[NETMAP_IF_POOL]._memtotal + \ 3048241616dSLuigi Rizzo nm_mem.pools[NETMAP_RING_POOL]._memtotal + \ 3058241616dSLuigi Rizzo netmap_obj_offset(&nm_mem.pools[NETMAP_BUF_POOL], (v))) 306ccdc3305SLuigi Rizzo 307ccdc3305SLuigi Rizzo 3088241616dSLuigi Rizzo /* 3098241616dSLuigi Rizzo * report the index, and use start position as a hint, 3108241616dSLuigi Rizzo * otherwise buffer allocation becomes terribly expensive. 3118241616dSLuigi Rizzo */ 312ccdc3305SLuigi Rizzo static void * 3138241616dSLuigi Rizzo netmap_obj_malloc(struct netmap_obj_pool *p, int len, uint32_t *start, uint32_t *index) 314ccdc3305SLuigi Rizzo { 315ccdc3305SLuigi Rizzo uint32_t i = 0; /* index in the bitmap */ 316ccdc3305SLuigi Rizzo uint32_t mask, j; /* slot counter */ 317ccdc3305SLuigi Rizzo void *vaddr = NULL; 318ccdc3305SLuigi Rizzo 319ccdc3305SLuigi Rizzo if (len > p->_objsize) { 320ccdc3305SLuigi Rizzo D("%s request size %d too large", p->name, len); 321ccdc3305SLuigi Rizzo // XXX cannot reduce the size 322ccdc3305SLuigi Rizzo return NULL; 323ccdc3305SLuigi Rizzo } 324ccdc3305SLuigi Rizzo 325ccdc3305SLuigi Rizzo if (p->objfree == 0) { 326ccdc3305SLuigi Rizzo D("%s allocator: run out of memory", p->name); 327ccdc3305SLuigi Rizzo return NULL; 328ccdc3305SLuigi Rizzo } 3298241616dSLuigi Rizzo if (start) 3308241616dSLuigi Rizzo i = *start; 331ccdc3305SLuigi Rizzo 3328241616dSLuigi Rizzo /* termination is guaranteed by p->free, but better check bounds on i */ 3338241616dSLuigi Rizzo while (vaddr == NULL && i < p->bitmap_slots) { 334ccdc3305SLuigi Rizzo uint32_t cur = p->bitmap[i]; 335ccdc3305SLuigi Rizzo if (cur == 0) { /* bitmask is fully used */ 336ccdc3305SLuigi Rizzo i++; 337ccdc3305SLuigi Rizzo continue; 338ccdc3305SLuigi Rizzo } 339ccdc3305SLuigi Rizzo /* locate a slot */ 340ccdc3305SLuigi Rizzo for (j = 0, mask = 1; (cur & mask) == 0; j++, mask <<= 1) 341ccdc3305SLuigi Rizzo ; 342ccdc3305SLuigi Rizzo 343ccdc3305SLuigi Rizzo p->bitmap[i] &= ~mask; /* mark object as in use */ 344ccdc3305SLuigi Rizzo p->objfree--; 345ccdc3305SLuigi Rizzo 346ccdc3305SLuigi Rizzo vaddr = p->lut[i * 32 + j].vaddr; 3478241616dSLuigi Rizzo if (index) 3488241616dSLuigi Rizzo *index = i * 32 + j; 349ccdc3305SLuigi Rizzo } 350ccdc3305SLuigi Rizzo ND("%s allocator: allocated object @ [%d][%d]: vaddr %p", i, j, vaddr); 351ccdc3305SLuigi Rizzo 3528241616dSLuigi Rizzo if (start) 3538241616dSLuigi Rizzo *start = i; 354ccdc3305SLuigi Rizzo return vaddr; 355ccdc3305SLuigi Rizzo } 356ccdc3305SLuigi Rizzo 357ccdc3305SLuigi Rizzo 358ccdc3305SLuigi Rizzo /* 3592579e2d7SLuigi Rizzo * free by index, not by address. This is slow, but is only used 3602579e2d7SLuigi Rizzo * for a small number of objects (rings, nifp) 361ccdc3305SLuigi Rizzo */ 362ccdc3305SLuigi Rizzo static void 363ccdc3305SLuigi Rizzo netmap_obj_free(struct netmap_obj_pool *p, uint32_t j) 364ccdc3305SLuigi Rizzo { 365ccdc3305SLuigi Rizzo if (j >= p->objtotal) { 366ccdc3305SLuigi Rizzo D("invalid index %u, max %u", j, p->objtotal); 367ccdc3305SLuigi Rizzo return; 368ccdc3305SLuigi Rizzo } 369ccdc3305SLuigi Rizzo p->bitmap[j / 32] |= (1 << (j % 32)); 370ccdc3305SLuigi Rizzo p->objfree++; 371ccdc3305SLuigi Rizzo return; 372ccdc3305SLuigi Rizzo } 373ccdc3305SLuigi Rizzo 374ccdc3305SLuigi Rizzo static void 375ccdc3305SLuigi Rizzo netmap_obj_free_va(struct netmap_obj_pool *p, void *vaddr) 376ccdc3305SLuigi Rizzo { 377ccdc3305SLuigi Rizzo int i, j, n = p->_memtotal / p->_clustsize; 378ccdc3305SLuigi Rizzo 379ccdc3305SLuigi Rizzo for (i = 0, j = 0; i < n; i++, j += p->clustentries) { 380ccdc3305SLuigi Rizzo void *base = p->lut[i * p->clustentries].vaddr; 381ccdc3305SLuigi Rizzo ssize_t relofs = (ssize_t) vaddr - (ssize_t) base; 382ccdc3305SLuigi Rizzo 383ccdc3305SLuigi Rizzo /* Given address, is out of the scope of the current cluster.*/ 384*ede69cffSLuigi Rizzo if (vaddr < base || relofs >= p->_clustsize) 385ccdc3305SLuigi Rizzo continue; 386ccdc3305SLuigi Rizzo 387ccdc3305SLuigi Rizzo j = j + relofs / p->_objsize; 388ccdc3305SLuigi Rizzo KASSERT(j != 0, ("Cannot free object 0")); 389ccdc3305SLuigi Rizzo netmap_obj_free(p, j); 390ccdc3305SLuigi Rizzo return; 391ccdc3305SLuigi Rizzo } 392ae10d1afSLuigi Rizzo D("address %p is not contained inside any cluster (%s)", 393ccdc3305SLuigi Rizzo vaddr, p->name); 394ccdc3305SLuigi Rizzo } 395ccdc3305SLuigi Rizzo 3968241616dSLuigi Rizzo #define netmap_if_malloc(len) netmap_obj_malloc(&nm_mem.pools[NETMAP_IF_POOL], len, NULL, NULL) 3978241616dSLuigi Rizzo #define netmap_if_free(v) netmap_obj_free_va(&nm_mem.pools[NETMAP_IF_POOL], (v)) 3988241616dSLuigi Rizzo #define netmap_ring_malloc(len) netmap_obj_malloc(&nm_mem.pools[NETMAP_RING_POOL], len, NULL, NULL) 3998241616dSLuigi Rizzo #define netmap_ring_free(v) netmap_obj_free_va(&nm_mem.pools[NETMAP_RING_POOL], (v)) 4008241616dSLuigi Rizzo #define netmap_buf_malloc(_pos, _index) \ 4018241616dSLuigi Rizzo netmap_obj_malloc(&nm_mem.pools[NETMAP_BUF_POOL], NETMAP_BUF_SIZE, _pos, _index) 402ccdc3305SLuigi Rizzo 403ccdc3305SLuigi Rizzo 404ccdc3305SLuigi Rizzo /* Return the index associated to the given packet buffer */ 405ccdc3305SLuigi Rizzo #define netmap_buf_index(v) \ 4068241616dSLuigi Rizzo (netmap_obj_offset(&nm_mem.pools[NETMAP_BUF_POOL], (v)) / nm_mem.pools[NETMAP_BUF_POOL]._objsize) 407ccdc3305SLuigi Rizzo 408ccdc3305SLuigi Rizzo 4098241616dSLuigi Rizzo /* Return nonzero on error */ 4108241616dSLuigi Rizzo static int 4110b8ed8e0SLuigi Rizzo netmap_new_bufs(struct netmap_if *nifp, 412ccdc3305SLuigi Rizzo struct netmap_slot *slot, u_int n) 413ccdc3305SLuigi Rizzo { 4148241616dSLuigi Rizzo struct netmap_obj_pool *p = &nm_mem.pools[NETMAP_BUF_POOL]; 4158241616dSLuigi Rizzo int i = 0; /* slot counter */ 4168241616dSLuigi Rizzo uint32_t pos = 0; /* slot in p->bitmap */ 4178241616dSLuigi Rizzo uint32_t index = 0; /* buffer index */ 418ccdc3305SLuigi Rizzo 4190b8ed8e0SLuigi Rizzo (void)nifp; /* UNUSED */ 420ccdc3305SLuigi Rizzo for (i = 0; i < n; i++) { 4218241616dSLuigi Rizzo void *vaddr = netmap_buf_malloc(&pos, &index); 422ccdc3305SLuigi Rizzo if (vaddr == NULL) { 423ccdc3305SLuigi Rizzo D("unable to locate empty packet buffer"); 424ccdc3305SLuigi Rizzo goto cleanup; 425ccdc3305SLuigi Rizzo } 4268241616dSLuigi Rizzo slot[i].buf_idx = index; 427ccdc3305SLuigi Rizzo slot[i].len = p->_objsize; 4288241616dSLuigi Rizzo /* XXX setting flags=NS_BUF_CHANGED forces a pointer reload 4298241616dSLuigi Rizzo * in the NIC ring. This is a hack that hides missing 4308241616dSLuigi Rizzo * initializations in the drivers, and should go away. 4318241616dSLuigi Rizzo */ 4322579e2d7SLuigi Rizzo // slot[i].flags = NS_BUF_CHANGED; 433ccdc3305SLuigi Rizzo } 434ccdc3305SLuigi Rizzo 4358241616dSLuigi Rizzo ND("allocated %d buffers, %d available, first at %d", n, p->objfree, pos); 4368241616dSLuigi Rizzo return (0); 437ccdc3305SLuigi Rizzo 438ccdc3305SLuigi Rizzo cleanup: 4394cf8455fSEd Maste while (i > 0) { 4404cf8455fSEd Maste i--; 4418241616dSLuigi Rizzo netmap_obj_free(p, slot[i].buf_idx); 442ccdc3305SLuigi Rizzo } 4438241616dSLuigi Rizzo bzero(slot, n * sizeof(slot[0])); 4448241616dSLuigi Rizzo return (ENOMEM); 445ccdc3305SLuigi Rizzo } 446ccdc3305SLuigi Rizzo 447ccdc3305SLuigi Rizzo 448ccdc3305SLuigi Rizzo static void 449ccdc3305SLuigi Rizzo netmap_free_buf(struct netmap_if *nifp, uint32_t i) 450ccdc3305SLuigi Rizzo { 4518241616dSLuigi Rizzo struct netmap_obj_pool *p = &nm_mem.pools[NETMAP_BUF_POOL]; 4528241616dSLuigi Rizzo 453ccdc3305SLuigi Rizzo if (i < 2 || i >= p->objtotal) { 454ccdc3305SLuigi Rizzo D("Cannot free buf#%d: should be in [2, %d[", i, p->objtotal); 455ccdc3305SLuigi Rizzo return; 456ccdc3305SLuigi Rizzo } 4578241616dSLuigi Rizzo netmap_obj_free(p, i); 458ccdc3305SLuigi Rizzo } 459ccdc3305SLuigi Rizzo 4608241616dSLuigi Rizzo static void 4618241616dSLuigi Rizzo netmap_reset_obj_allocator(struct netmap_obj_pool *p) 4628241616dSLuigi Rizzo { 4638241616dSLuigi Rizzo if (p == NULL) 4648241616dSLuigi Rizzo return; 4658241616dSLuigi Rizzo if (p->bitmap) 4668241616dSLuigi Rizzo free(p->bitmap, M_NETMAP); 4678241616dSLuigi Rizzo p->bitmap = NULL; 4688241616dSLuigi Rizzo if (p->lut) { 4698241616dSLuigi Rizzo int i; 4708241616dSLuigi Rizzo for (i = 0; i < p->objtotal; i += p->clustentries) { 4718241616dSLuigi Rizzo if (p->lut[i].vaddr) 4728241616dSLuigi Rizzo contigfree(p->lut[i].vaddr, p->_clustsize, M_NETMAP); 4738241616dSLuigi Rizzo } 4748241616dSLuigi Rizzo bzero(p->lut, sizeof(struct lut_entry) * p->objtotal); 4758241616dSLuigi Rizzo #ifdef linux 4768241616dSLuigi Rizzo vfree(p->lut); 4778241616dSLuigi Rizzo #else 4788241616dSLuigi Rizzo free(p->lut, M_NETMAP); 4798241616dSLuigi Rizzo #endif 4808241616dSLuigi Rizzo } 4818241616dSLuigi Rizzo p->lut = NULL; 4828241616dSLuigi Rizzo } 483ccdc3305SLuigi Rizzo 484ccdc3305SLuigi Rizzo /* 485ccdc3305SLuigi Rizzo * Free all resources related to an allocator. 486ccdc3305SLuigi Rizzo */ 487ccdc3305SLuigi Rizzo static void 488ccdc3305SLuigi Rizzo netmap_destroy_obj_allocator(struct netmap_obj_pool *p) 489ccdc3305SLuigi Rizzo { 490ccdc3305SLuigi Rizzo if (p == NULL) 491ccdc3305SLuigi Rizzo return; 4928241616dSLuigi Rizzo netmap_reset_obj_allocator(p); 493ccdc3305SLuigi Rizzo } 494ccdc3305SLuigi Rizzo 495ccdc3305SLuigi Rizzo /* 496ccdc3305SLuigi Rizzo * We receive a request for objtotal objects, of size objsize each. 497ccdc3305SLuigi Rizzo * Internally we may round up both numbers, as we allocate objects 498ccdc3305SLuigi Rizzo * in small clusters multiple of the page size. 499ccdc3305SLuigi Rizzo * In the allocator we don't need to store the objsize, 500ccdc3305SLuigi Rizzo * but we do need to keep track of objtotal' and clustentries, 501ccdc3305SLuigi Rizzo * as they are needed when freeing memory. 502ccdc3305SLuigi Rizzo * 503ccdc3305SLuigi Rizzo * XXX note -- userspace needs the buffers to be contiguous, 504ccdc3305SLuigi Rizzo * so we cannot afford gaps at the end of a cluster. 505ccdc3305SLuigi Rizzo */ 5068241616dSLuigi Rizzo 5078241616dSLuigi Rizzo 5088241616dSLuigi Rizzo /* call with NMA_LOCK held */ 5098241616dSLuigi Rizzo static int 5108241616dSLuigi Rizzo netmap_config_obj_allocator(struct netmap_obj_pool *p, u_int objtotal, u_int objsize) 511ccdc3305SLuigi Rizzo { 512ccdc3305SLuigi Rizzo int i, n; 513ccdc3305SLuigi Rizzo u_int clustsize; /* the cluster size, multiple of page size */ 514ccdc3305SLuigi Rizzo u_int clustentries; /* how many objects per entry */ 515ccdc3305SLuigi Rizzo 516ccdc3305SLuigi Rizzo #define MAX_CLUSTSIZE (1<<17) 517ccdc3305SLuigi Rizzo #define LINE_ROUND 64 518ccdc3305SLuigi Rizzo if (objsize >= MAX_CLUSTSIZE) { 519ccdc3305SLuigi Rizzo /* we could do it but there is no point */ 520ccdc3305SLuigi Rizzo D("unsupported allocation for %d bytes", objsize); 5218241616dSLuigi Rizzo goto error; 522ccdc3305SLuigi Rizzo } 523ccdc3305SLuigi Rizzo /* make sure objsize is a multiple of LINE_ROUND */ 524ccdc3305SLuigi Rizzo i = (objsize & (LINE_ROUND - 1)); 525ccdc3305SLuigi Rizzo if (i) { 526ccdc3305SLuigi Rizzo D("XXX aligning object by %d bytes", LINE_ROUND - i); 527ccdc3305SLuigi Rizzo objsize += LINE_ROUND - i; 528ccdc3305SLuigi Rizzo } 5298241616dSLuigi Rizzo if (objsize < p->objminsize || objsize > p->objmaxsize) { 5308241616dSLuigi Rizzo D("requested objsize %d out of range [%d, %d]", 5318241616dSLuigi Rizzo objsize, p->objminsize, p->objmaxsize); 5328241616dSLuigi Rizzo goto error; 5338241616dSLuigi Rizzo } 5348241616dSLuigi Rizzo if (objtotal < p->nummin || objtotal > p->nummax) { 5358241616dSLuigi Rizzo D("requested objtotal %d out of range [%d, %d]", 5368241616dSLuigi Rizzo objtotal, p->nummin, p->nummax); 5378241616dSLuigi Rizzo goto error; 5388241616dSLuigi Rizzo } 539ccdc3305SLuigi Rizzo /* 540ccdc3305SLuigi Rizzo * Compute number of objects using a brute-force approach: 541ccdc3305SLuigi Rizzo * given a max cluster size, 542ccdc3305SLuigi Rizzo * we try to fill it with objects keeping track of the 543ccdc3305SLuigi Rizzo * wasted space to the next page boundary. 544ccdc3305SLuigi Rizzo */ 545ccdc3305SLuigi Rizzo for (clustentries = 0, i = 1;; i++) { 546ccdc3305SLuigi Rizzo u_int delta, used = i * objsize; 547ccdc3305SLuigi Rizzo if (used > MAX_CLUSTSIZE) 548ccdc3305SLuigi Rizzo break; 549ccdc3305SLuigi Rizzo delta = used % PAGE_SIZE; 550ccdc3305SLuigi Rizzo if (delta == 0) { // exact solution 551ccdc3305SLuigi Rizzo clustentries = i; 552ccdc3305SLuigi Rizzo break; 553ccdc3305SLuigi Rizzo } 554ccdc3305SLuigi Rizzo if (delta > ( (clustentries*objsize) % PAGE_SIZE) ) 555ccdc3305SLuigi Rizzo clustentries = i; 556ccdc3305SLuigi Rizzo } 557ccdc3305SLuigi Rizzo // D("XXX --- ouch, delta %d (bad for buffers)", delta); 558ccdc3305SLuigi Rizzo /* compute clustsize and round to the next page */ 559ccdc3305SLuigi Rizzo clustsize = clustentries * objsize; 560ccdc3305SLuigi Rizzo i = (clustsize & (PAGE_SIZE - 1)); 561ccdc3305SLuigi Rizzo if (i) 562ccdc3305SLuigi Rizzo clustsize += PAGE_SIZE - i; 563ae10d1afSLuigi Rizzo if (netmap_verbose) 564ccdc3305SLuigi Rizzo D("objsize %d clustsize %d objects %d", 565ccdc3305SLuigi Rizzo objsize, clustsize, clustentries); 566ccdc3305SLuigi Rizzo 567ccdc3305SLuigi Rizzo /* 568ccdc3305SLuigi Rizzo * The number of clusters is n = ceil(objtotal/clustentries) 569ccdc3305SLuigi Rizzo * objtotal' = n * clustentries 570ccdc3305SLuigi Rizzo */ 571ccdc3305SLuigi Rizzo p->clustentries = clustentries; 572ccdc3305SLuigi Rizzo p->_clustsize = clustsize; 573ccdc3305SLuigi Rizzo n = (objtotal + clustentries - 1) / clustentries; 574ccdc3305SLuigi Rizzo p->_numclusters = n; 575ccdc3305SLuigi Rizzo p->objtotal = n * clustentries; 576ccdc3305SLuigi Rizzo p->objfree = p->objtotal - 2; /* obj 0 and 1 are reserved */ 577ccdc3305SLuigi Rizzo p->_memtotal = p->_numclusters * p->_clustsize; 5788241616dSLuigi Rizzo p->_objsize = objsize; 579ccdc3305SLuigi Rizzo 5808241616dSLuigi Rizzo return 0; 5818241616dSLuigi Rizzo 5828241616dSLuigi Rizzo error: 5838241616dSLuigi Rizzo p->_objsize = objsize; 5848241616dSLuigi Rizzo p->objtotal = objtotal; 5858241616dSLuigi Rizzo 5868241616dSLuigi Rizzo return EINVAL; 5878241616dSLuigi Rizzo } 5888241616dSLuigi Rizzo 5898241616dSLuigi Rizzo 5908241616dSLuigi Rizzo /* call with NMA_LOCK held */ 5918241616dSLuigi Rizzo static int 5928241616dSLuigi Rizzo netmap_finalize_obj_allocator(struct netmap_obj_pool *p) 5938241616dSLuigi Rizzo { 5948241616dSLuigi Rizzo int i, n; 5958241616dSLuigi Rizzo 5968241616dSLuigi Rizzo n = sizeof(struct lut_entry) * p->objtotal; 5978241616dSLuigi Rizzo #ifdef linux 5988241616dSLuigi Rizzo p->lut = vmalloc(n); 5998241616dSLuigi Rizzo #else 600d2b91851SEd Maste p->lut = malloc(n, M_NETMAP, M_NOWAIT | M_ZERO); 6018241616dSLuigi Rizzo #endif 602ccdc3305SLuigi Rizzo if (p->lut == NULL) { 6038241616dSLuigi Rizzo D("Unable to create lookup table (%d bytes) for '%s'", n, p->name); 604ccdc3305SLuigi Rizzo goto clean; 605ccdc3305SLuigi Rizzo } 606ccdc3305SLuigi Rizzo 607ccdc3305SLuigi Rizzo /* Allocate the bitmap */ 608ccdc3305SLuigi Rizzo n = (p->objtotal + 31) / 32; 609d2b91851SEd Maste p->bitmap = malloc(sizeof(uint32_t) * n, M_NETMAP, M_NOWAIT | M_ZERO); 610ccdc3305SLuigi Rizzo if (p->bitmap == NULL) { 611ccdc3305SLuigi Rizzo D("Unable to create bitmap (%d entries) for allocator '%s'", n, 6128241616dSLuigi Rizzo p->name); 613ccdc3305SLuigi Rizzo goto clean; 614ccdc3305SLuigi Rizzo } 6158241616dSLuigi Rizzo p->bitmap_slots = n; 616ccdc3305SLuigi Rizzo 617ccdc3305SLuigi Rizzo /* 618ccdc3305SLuigi Rizzo * Allocate clusters, init pointers and bitmap 619ccdc3305SLuigi Rizzo */ 620ccdc3305SLuigi Rizzo for (i = 0; i < p->objtotal;) { 6218241616dSLuigi Rizzo int lim = i + p->clustentries; 622ccdc3305SLuigi Rizzo char *clust; 623ccdc3305SLuigi Rizzo 6248241616dSLuigi Rizzo clust = contigmalloc(p->_clustsize, M_NETMAP, M_NOWAIT | M_ZERO, 625ccdc3305SLuigi Rizzo 0, -1UL, PAGE_SIZE, 0); 626ccdc3305SLuigi Rizzo if (clust == NULL) { 627ccdc3305SLuigi Rizzo /* 628ccdc3305SLuigi Rizzo * If we get here, there is a severe memory shortage, 629ccdc3305SLuigi Rizzo * so halve the allocated memory to reclaim some. 6308241616dSLuigi Rizzo * XXX check boundaries 631ccdc3305SLuigi Rizzo */ 632ccdc3305SLuigi Rizzo D("Unable to create cluster at %d for '%s' allocator", 6338241616dSLuigi Rizzo i, p->name); 634ccdc3305SLuigi Rizzo lim = i / 2; 6358241616dSLuigi Rizzo for (i--; i >= lim; i--) { 636ccdc3305SLuigi Rizzo p->bitmap[ (i>>5) ] &= ~( 1 << (i & 31) ); 6378241616dSLuigi Rizzo if (i % p->clustentries == 0 && p->lut[i].vaddr) 638ccdc3305SLuigi Rizzo contigfree(p->lut[i].vaddr, 639ccdc3305SLuigi Rizzo p->_clustsize, M_NETMAP); 640ccdc3305SLuigi Rizzo } 641ccdc3305SLuigi Rizzo p->objtotal = i; 642ccdc3305SLuigi Rizzo p->objfree = p->objtotal - 2; 6438241616dSLuigi Rizzo p->_numclusters = i / p->clustentries; 644ccdc3305SLuigi Rizzo p->_memtotal = p->_numclusters * p->_clustsize; 645ccdc3305SLuigi Rizzo break; 646ccdc3305SLuigi Rizzo } 6478241616dSLuigi Rizzo for (; i < lim; i++, clust += p->_objsize) { 648ccdc3305SLuigi Rizzo p->bitmap[ (i>>5) ] |= ( 1 << (i & 31) ); 649ccdc3305SLuigi Rizzo p->lut[i].vaddr = clust; 650ccdc3305SLuigi Rizzo p->lut[i].paddr = vtophys(clust); 651ccdc3305SLuigi Rizzo } 652ccdc3305SLuigi Rizzo } 653ccdc3305SLuigi Rizzo p->bitmap[0] = ~3; /* objs 0 and 1 is always busy */ 654ae10d1afSLuigi Rizzo if (netmap_verbose) 655ccdc3305SLuigi Rizzo D("Pre-allocated %d clusters (%d/%dKB) for '%s'", 656ccdc3305SLuigi Rizzo p->_numclusters, p->_clustsize >> 10, 6578241616dSLuigi Rizzo p->_memtotal >> 10, p->name); 658ccdc3305SLuigi Rizzo 6598241616dSLuigi Rizzo return 0; 660ccdc3305SLuigi Rizzo 661ccdc3305SLuigi Rizzo clean: 6628241616dSLuigi Rizzo netmap_reset_obj_allocator(p); 6638241616dSLuigi Rizzo return ENOMEM; 6648241616dSLuigi Rizzo } 6658241616dSLuigi Rizzo 6668241616dSLuigi Rizzo /* call with lock held */ 6678241616dSLuigi Rizzo static int 6688241616dSLuigi Rizzo netmap_memory_config_changed(void) 6698241616dSLuigi Rizzo { 6708241616dSLuigi Rizzo int i; 6718241616dSLuigi Rizzo 6728241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 6738241616dSLuigi Rizzo if (nm_mem.pools[i]._objsize != netmap_params[i].size || 6748241616dSLuigi Rizzo nm_mem.pools[i].objtotal != netmap_params[i].num) 6758241616dSLuigi Rizzo return 1; 6768241616dSLuigi Rizzo } 6778241616dSLuigi Rizzo return 0; 6788241616dSLuigi Rizzo } 6798241616dSLuigi Rizzo 6808241616dSLuigi Rizzo 6818241616dSLuigi Rizzo /* call with lock held */ 6828241616dSLuigi Rizzo static int 6838241616dSLuigi Rizzo netmap_memory_config(void) 6848241616dSLuigi Rizzo { 6858241616dSLuigi Rizzo int i; 6868241616dSLuigi Rizzo 6878241616dSLuigi Rizzo if (!netmap_memory_config_changed()) 6888241616dSLuigi Rizzo goto out; 6898241616dSLuigi Rizzo 6908241616dSLuigi Rizzo D("reconfiguring"); 6918241616dSLuigi Rizzo 6928241616dSLuigi Rizzo if (nm_mem.finalized) { 6938241616dSLuigi Rizzo /* reset previous allocation */ 6948241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 6958241616dSLuigi Rizzo netmap_reset_obj_allocator(&nm_mem.pools[i]); 6968241616dSLuigi Rizzo } 6978241616dSLuigi Rizzo nm_mem.finalized = 0; 6988241616dSLuigi Rizzo } 6998241616dSLuigi Rizzo 7008241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 7018241616dSLuigi Rizzo nm_mem.lasterr = netmap_config_obj_allocator(&nm_mem.pools[i], 7028241616dSLuigi Rizzo netmap_params[i].num, netmap_params[i].size); 7038241616dSLuigi Rizzo if (nm_mem.lasterr) 7048241616dSLuigi Rizzo goto out; 7058241616dSLuigi Rizzo } 7068241616dSLuigi Rizzo 7078241616dSLuigi Rizzo D("Have %d KB for interfaces, %d KB for rings and %d MB for buffers", 7088241616dSLuigi Rizzo nm_mem.pools[NETMAP_IF_POOL]._memtotal >> 10, 7098241616dSLuigi Rizzo nm_mem.pools[NETMAP_RING_POOL]._memtotal >> 10, 7108241616dSLuigi Rizzo nm_mem.pools[NETMAP_BUF_POOL]._memtotal >> 20); 7118241616dSLuigi Rizzo 7128241616dSLuigi Rizzo out: 7138241616dSLuigi Rizzo 7148241616dSLuigi Rizzo return nm_mem.lasterr; 7158241616dSLuigi Rizzo } 7168241616dSLuigi Rizzo 7178241616dSLuigi Rizzo /* call with lock held */ 7188241616dSLuigi Rizzo static int 7198241616dSLuigi Rizzo netmap_memory_finalize(void) 7208241616dSLuigi Rizzo { 7218241616dSLuigi Rizzo int i; 7228241616dSLuigi Rizzo u_int totalsize = 0; 7238241616dSLuigi Rizzo 7248241616dSLuigi Rizzo nm_mem.refcount++; 7258241616dSLuigi Rizzo if (nm_mem.refcount > 1) { 726ae10d1afSLuigi Rizzo ND("busy (refcount %d)", nm_mem.refcount); 7278241616dSLuigi Rizzo goto out; 7288241616dSLuigi Rizzo } 7298241616dSLuigi Rizzo 7308241616dSLuigi Rizzo /* update configuration if changed */ 7318241616dSLuigi Rizzo if (netmap_memory_config()) 7328241616dSLuigi Rizzo goto out; 7338241616dSLuigi Rizzo 7348241616dSLuigi Rizzo if (nm_mem.finalized) { 7358241616dSLuigi Rizzo /* may happen if config is not changed */ 7368241616dSLuigi Rizzo ND("nothing to do"); 7378241616dSLuigi Rizzo goto out; 7388241616dSLuigi Rizzo } 7398241616dSLuigi Rizzo 7408241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 7418241616dSLuigi Rizzo nm_mem.lasterr = netmap_finalize_obj_allocator(&nm_mem.pools[i]); 7428241616dSLuigi Rizzo if (nm_mem.lasterr) 7438241616dSLuigi Rizzo goto cleanup; 7448241616dSLuigi Rizzo totalsize += nm_mem.pools[i]._memtotal; 7458241616dSLuigi Rizzo } 7468241616dSLuigi Rizzo nm_mem.nm_totalsize = totalsize; 7478241616dSLuigi Rizzo 7488241616dSLuigi Rizzo /* backward compatibility */ 7498241616dSLuigi Rizzo netmap_buf_size = nm_mem.pools[NETMAP_BUF_POOL]._objsize; 7508241616dSLuigi Rizzo netmap_total_buffers = nm_mem.pools[NETMAP_BUF_POOL].objtotal; 7518241616dSLuigi Rizzo 7528241616dSLuigi Rizzo netmap_buffer_lut = nm_mem.pools[NETMAP_BUF_POOL].lut; 7538241616dSLuigi Rizzo netmap_buffer_base = nm_mem.pools[NETMAP_BUF_POOL].lut[0].vaddr; 7548241616dSLuigi Rizzo 7558241616dSLuigi Rizzo nm_mem.finalized = 1; 7568241616dSLuigi Rizzo nm_mem.lasterr = 0; 7578241616dSLuigi Rizzo 7588241616dSLuigi Rizzo /* make sysctl values match actual values in the pools */ 7598241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 7608241616dSLuigi Rizzo netmap_params[i].size = nm_mem.pools[i]._objsize; 7618241616dSLuigi Rizzo netmap_params[i].num = nm_mem.pools[i].objtotal; 7628241616dSLuigi Rizzo } 7638241616dSLuigi Rizzo 7648241616dSLuigi Rizzo out: 7658241616dSLuigi Rizzo if (nm_mem.lasterr) 7668241616dSLuigi Rizzo nm_mem.refcount--; 7678241616dSLuigi Rizzo 7688241616dSLuigi Rizzo return nm_mem.lasterr; 7698241616dSLuigi Rizzo 7708241616dSLuigi Rizzo cleanup: 7718241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 7728241616dSLuigi Rizzo netmap_reset_obj_allocator(&nm_mem.pools[i]); 7738241616dSLuigi Rizzo } 7748241616dSLuigi Rizzo nm_mem.refcount--; 7758241616dSLuigi Rizzo 7768241616dSLuigi Rizzo return nm_mem.lasterr; 777ccdc3305SLuigi Rizzo } 778ccdc3305SLuigi Rizzo 779ccdc3305SLuigi Rizzo static int 780ccdc3305SLuigi Rizzo netmap_memory_init(void) 781ccdc3305SLuigi Rizzo { 7828241616dSLuigi Rizzo NMA_LOCK_INIT(); 7838241616dSLuigi Rizzo return (0); 784ccdc3305SLuigi Rizzo } 785ccdc3305SLuigi Rizzo 786ccdc3305SLuigi Rizzo static void 787ccdc3305SLuigi Rizzo netmap_memory_fini(void) 788ccdc3305SLuigi Rizzo { 7898241616dSLuigi Rizzo int i; 7908241616dSLuigi Rizzo 7918241616dSLuigi Rizzo for (i = 0; i < NETMAP_POOLS_NR; i++) { 7928241616dSLuigi Rizzo netmap_destroy_obj_allocator(&nm_mem.pools[i]); 7938241616dSLuigi Rizzo } 7948241616dSLuigi Rizzo NMA_LOCK_DESTROY(); 7958241616dSLuigi Rizzo } 7968241616dSLuigi Rizzo 7978241616dSLuigi Rizzo static void 7988241616dSLuigi Rizzo netmap_free_rings(struct netmap_adapter *na) 7998241616dSLuigi Rizzo { 8008241616dSLuigi Rizzo int i; 801ae10d1afSLuigi Rizzo if (!na->tx_rings) 802ae10d1afSLuigi Rizzo return; 8038241616dSLuigi Rizzo for (i = 0; i < na->num_tx_rings + 1; i++) { 8048241616dSLuigi Rizzo netmap_ring_free(na->tx_rings[i].ring); 8058241616dSLuigi Rizzo na->tx_rings[i].ring = NULL; 8068241616dSLuigi Rizzo } 8078241616dSLuigi Rizzo for (i = 0; i < na->num_rx_rings + 1; i++) { 8088241616dSLuigi Rizzo netmap_ring_free(na->rx_rings[i].ring); 8098241616dSLuigi Rizzo na->rx_rings[i].ring = NULL; 8108241616dSLuigi Rizzo } 811ae10d1afSLuigi Rizzo free(na->tx_rings, M_DEVBUF); 812ae10d1afSLuigi Rizzo na->tx_rings = na->rx_rings = NULL; 813ccdc3305SLuigi Rizzo } 814ccdc3305SLuigi Rizzo 815ccdc3305SLuigi Rizzo 816ccdc3305SLuigi Rizzo 8178241616dSLuigi Rizzo /* call with NMA_LOCK held */ 818ae10d1afSLuigi Rizzo /* 819ae10d1afSLuigi Rizzo * Allocate the per-fd structure netmap_if. 820ae10d1afSLuigi Rizzo * If this is the first instance, also allocate the krings, rings etc. 821ae10d1afSLuigi Rizzo */ 822ccdc3305SLuigi Rizzo static void * 823ccdc3305SLuigi Rizzo netmap_if_new(const char *ifname, struct netmap_adapter *na) 824ccdc3305SLuigi Rizzo { 825ccdc3305SLuigi Rizzo struct netmap_if *nifp; 826ccdc3305SLuigi Rizzo struct netmap_ring *ring; 827ccdc3305SLuigi Rizzo ssize_t base; /* handy for relative offsets between rings and nifp */ 828ae10d1afSLuigi Rizzo u_int i, len, ndesc, ntx, nrx; 829ccdc3305SLuigi Rizzo struct netmap_kring *kring; 830ccdc3305SLuigi Rizzo 831ae10d1afSLuigi Rizzo if (netmap_update_config(na)) { 832ae10d1afSLuigi Rizzo /* configuration mismatch, report and fail */ 833ae10d1afSLuigi Rizzo return NULL; 834ae10d1afSLuigi Rizzo } 835ae10d1afSLuigi Rizzo ntx = na->num_tx_rings + 1; /* shorthand, include stack ring */ 836ae10d1afSLuigi Rizzo nrx = na->num_rx_rings + 1; /* shorthand, include stack ring */ 837ccdc3305SLuigi Rizzo /* 838ccdc3305SLuigi Rizzo * the descriptor is followed inline by an array of offsets 839ccdc3305SLuigi Rizzo * to the tx and rx rings in the shared memory region. 840ccdc3305SLuigi Rizzo */ 841ccdc3305SLuigi Rizzo len = sizeof(struct netmap_if) + (nrx + ntx) * sizeof(ssize_t); 842ccdc3305SLuigi Rizzo nifp = netmap_if_malloc(len); 843ccdc3305SLuigi Rizzo if (nifp == NULL) { 844ccdc3305SLuigi Rizzo return NULL; 845ccdc3305SLuigi Rizzo } 846ccdc3305SLuigi Rizzo 847ccdc3305SLuigi Rizzo /* initialize base fields -- override const */ 848ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&nifp->ni_tx_rings = na->num_tx_rings; 849ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&nifp->ni_rx_rings = na->num_rx_rings; 850ccdc3305SLuigi Rizzo strncpy(nifp->ni_name, ifname, IFNAMSIZ); 851ccdc3305SLuigi Rizzo 852ccdc3305SLuigi Rizzo (na->refcount)++; /* XXX atomic ? we are under lock */ 853ccdc3305SLuigi Rizzo if (na->refcount > 1) { /* already setup, we are done */ 854ccdc3305SLuigi Rizzo goto final; 855ccdc3305SLuigi Rizzo } 856ccdc3305SLuigi Rizzo 857ae10d1afSLuigi Rizzo len = (ntx + nrx) * sizeof(struct netmap_kring); 858ae10d1afSLuigi Rizzo na->tx_rings = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO); 859ae10d1afSLuigi Rizzo if (na->tx_rings == NULL) { 860ae10d1afSLuigi Rizzo D("Cannot allocate krings for %s", ifname); 861ae10d1afSLuigi Rizzo goto cleanup; 862ae10d1afSLuigi Rizzo } 863ae10d1afSLuigi Rizzo na->rx_rings = na->tx_rings + ntx; 864ae10d1afSLuigi Rizzo 865ccdc3305SLuigi Rizzo /* 866ccdc3305SLuigi Rizzo * First instance, allocate netmap rings and buffers for this card 867ccdc3305SLuigi Rizzo * The rings are contiguous, but have variable size. 868ccdc3305SLuigi Rizzo */ 869ccdc3305SLuigi Rizzo for (i = 0; i < ntx; i++) { /* Transmit rings */ 870ccdc3305SLuigi Rizzo kring = &na->tx_rings[i]; 871ccdc3305SLuigi Rizzo ndesc = na->num_tx_desc; 872ccdc3305SLuigi Rizzo bzero(kring, sizeof(*kring)); 873ccdc3305SLuigi Rizzo len = sizeof(struct netmap_ring) + 874ccdc3305SLuigi Rizzo ndesc * sizeof(struct netmap_slot); 875ccdc3305SLuigi Rizzo ring = netmap_ring_malloc(len); 876ccdc3305SLuigi Rizzo if (ring == NULL) { 877ccdc3305SLuigi Rizzo D("Cannot allocate tx_ring[%d] for %s", i, ifname); 878ccdc3305SLuigi Rizzo goto cleanup; 879ccdc3305SLuigi Rizzo } 880ccdc3305SLuigi Rizzo ND("txring[%d] at %p ofs %d", i, ring); 881ccdc3305SLuigi Rizzo kring->na = na; 882ccdc3305SLuigi Rizzo kring->ring = ring; 883ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&ring->num_slots = kring->nkr_num_slots = ndesc; 884ccdc3305SLuigi Rizzo *(ssize_t *)(uintptr_t)&ring->buf_ofs = 8858241616dSLuigi Rizzo (nm_mem.pools[NETMAP_IF_POOL]._memtotal + 8868241616dSLuigi Rizzo nm_mem.pools[NETMAP_RING_POOL]._memtotal) - 887ccdc3305SLuigi Rizzo netmap_ring_offset(ring); 888ccdc3305SLuigi Rizzo 889ccdc3305SLuigi Rizzo /* 890ccdc3305SLuigi Rizzo * IMPORTANT: 891ccdc3305SLuigi Rizzo * Always keep one slot empty, so we can detect new 892ccdc3305SLuigi Rizzo * transmissions comparing cur and nr_hwcur (they are 893ccdc3305SLuigi Rizzo * the same only if there are no new transmissions). 894ccdc3305SLuigi Rizzo */ 895ccdc3305SLuigi Rizzo ring->avail = kring->nr_hwavail = ndesc - 1; 896ccdc3305SLuigi Rizzo ring->cur = kring->nr_hwcur = 0; 897ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&ring->nr_buf_size = NETMAP_BUF_SIZE; 898ccdc3305SLuigi Rizzo ND("initializing slots for txring[%d]", i); 8998241616dSLuigi Rizzo if (netmap_new_bufs(nifp, ring->slot, ndesc)) { 9008241616dSLuigi Rizzo D("Cannot allocate buffers for tx_ring[%d] for %s", i, ifname); 9018241616dSLuigi Rizzo goto cleanup; 9028241616dSLuigi Rizzo } 903ccdc3305SLuigi Rizzo } 904ccdc3305SLuigi Rizzo 905ccdc3305SLuigi Rizzo for (i = 0; i < nrx; i++) { /* Receive rings */ 906ccdc3305SLuigi Rizzo kring = &na->rx_rings[i]; 907ccdc3305SLuigi Rizzo ndesc = na->num_rx_desc; 908ccdc3305SLuigi Rizzo bzero(kring, sizeof(*kring)); 909ccdc3305SLuigi Rizzo len = sizeof(struct netmap_ring) + 910ccdc3305SLuigi Rizzo ndesc * sizeof(struct netmap_slot); 911ccdc3305SLuigi Rizzo ring = netmap_ring_malloc(len); 912ccdc3305SLuigi Rizzo if (ring == NULL) { 913ccdc3305SLuigi Rizzo D("Cannot allocate rx_ring[%d] for %s", i, ifname); 914ccdc3305SLuigi Rizzo goto cleanup; 915ccdc3305SLuigi Rizzo } 916ccdc3305SLuigi Rizzo ND("rxring[%d] at %p ofs %d", i, ring); 917ccdc3305SLuigi Rizzo 918ccdc3305SLuigi Rizzo kring->na = na; 919ccdc3305SLuigi Rizzo kring->ring = ring; 920ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&ring->num_slots = kring->nkr_num_slots = ndesc; 921ccdc3305SLuigi Rizzo *(ssize_t *)(uintptr_t)&ring->buf_ofs = 9228241616dSLuigi Rizzo (nm_mem.pools[NETMAP_IF_POOL]._memtotal + 9238241616dSLuigi Rizzo nm_mem.pools[NETMAP_RING_POOL]._memtotal) - 924ccdc3305SLuigi Rizzo netmap_ring_offset(ring); 925ccdc3305SLuigi Rizzo 926ccdc3305SLuigi Rizzo ring->cur = kring->nr_hwcur = 0; 927ccdc3305SLuigi Rizzo ring->avail = kring->nr_hwavail = 0; /* empty */ 928ccdc3305SLuigi Rizzo *(int *)(uintptr_t)&ring->nr_buf_size = NETMAP_BUF_SIZE; 929ccdc3305SLuigi Rizzo ND("initializing slots for rxring[%d]", i); 9308241616dSLuigi Rizzo if (netmap_new_bufs(nifp, ring->slot, ndesc)) { 9318241616dSLuigi Rizzo D("Cannot allocate buffers for rx_ring[%d] for %s", i, ifname); 9328241616dSLuigi Rizzo goto cleanup; 933ccdc3305SLuigi Rizzo } 9348241616dSLuigi Rizzo } 935ccdc3305SLuigi Rizzo #ifdef linux 936ccdc3305SLuigi Rizzo // XXX initialize the selrecord structs. 937ccdc3305SLuigi Rizzo for (i = 0; i < ntx; i++) 938ccdc3305SLuigi Rizzo init_waitqueue_head(&na->tx_rings[i].si); 939f196ce38SLuigi Rizzo for (i = 0; i < nrx; i++) 940f196ce38SLuigi Rizzo init_waitqueue_head(&na->rx_rings[i].si); 941ccdc3305SLuigi Rizzo init_waitqueue_head(&na->tx_si); 942f196ce38SLuigi Rizzo init_waitqueue_head(&na->rx_si); 943ccdc3305SLuigi Rizzo #endif 944ccdc3305SLuigi Rizzo final: 945ccdc3305SLuigi Rizzo /* 946ccdc3305SLuigi Rizzo * fill the slots for the rx and tx rings. They contain the offset 947ccdc3305SLuigi Rizzo * between the ring and nifp, so the information is usable in 948ccdc3305SLuigi Rizzo * userspace to reach the ring from the nifp. 949ccdc3305SLuigi Rizzo */ 950ccdc3305SLuigi Rizzo base = netmap_if_offset(nifp); 951ccdc3305SLuigi Rizzo for (i = 0; i < ntx; i++) { 952ccdc3305SLuigi Rizzo *(ssize_t *)(uintptr_t)&nifp->ring_ofs[i] = 953ccdc3305SLuigi Rizzo netmap_ring_offset(na->tx_rings[i].ring) - base; 954ccdc3305SLuigi Rizzo } 955ccdc3305SLuigi Rizzo for (i = 0; i < nrx; i++) { 956ccdc3305SLuigi Rizzo *(ssize_t *)(uintptr_t)&nifp->ring_ofs[i+ntx] = 957ccdc3305SLuigi Rizzo netmap_ring_offset(na->rx_rings[i].ring) - base; 958ccdc3305SLuigi Rizzo } 959ccdc3305SLuigi Rizzo return (nifp); 960ccdc3305SLuigi Rizzo cleanup: 9618241616dSLuigi Rizzo netmap_free_rings(na); 9628241616dSLuigi Rizzo netmap_if_free(nifp); 9638241616dSLuigi Rizzo (na->refcount)--; 964ccdc3305SLuigi Rizzo return NULL; 965ccdc3305SLuigi Rizzo } 966ccdc3305SLuigi Rizzo 9678241616dSLuigi Rizzo /* call with NMA_LOCK held */ 968ccdc3305SLuigi Rizzo static void 9698241616dSLuigi Rizzo netmap_memory_deref(void) 970ccdc3305SLuigi Rizzo { 9718241616dSLuigi Rizzo nm_mem.refcount--; 972ae10d1afSLuigi Rizzo if (netmap_verbose) 9738241616dSLuigi Rizzo D("refcount = %d", nm_mem.refcount); 974ccdc3305SLuigi Rizzo } 975