1/* 2 * Copyright (c) 2017, ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group. 8 */ 9 10 11#include <stdio.h> 12#include <stdlib.h> 13#include <string.h> 14 15#include <barrelfish/barrelfish.h> 16 17#include <devif/queue_interface.h> 18 19#include <lwip/pbuf.h> 20 21#include "networking_internal.h" 22#define NETDEBUG_SUBSYSTEM "net_buf" 23 24 25///< the default flags to map the buffers 26#define NETWORKING_DEFAULT_BUFFER_FLAGS VREGION_FLAGS_READ_WRITE 27 28///< buffer alignment 29#define NETWORKING_BUFFER_ALIGN 2048 30 31 32 33/** 34 * @brief initializes the networking buffer pools 35 * 36 * @param dev_q the device queue to create the buffer pool for 37 * @param numbuf number of initial buffers 38 * @param size size of the networking buffer 39 * @param retbp buffer pool to initialize 40 * 41 * @return SYS_ERR_OK on success, errval on failure 42 */ 43errval_t net_buf_pool_alloc(struct devq *dev_q, size_t numbuf, size_t size, 44 struct net_buf_pool **retbp) 45{ 46 errval_t err; 47 48 assert(retbp); 49 50 NETDEBUG("initializing buffer pool with %zu x %zu buffers...\n", numbuf, size); 51 52 struct net_buf_pool *netbp = calloc(1, sizeof(*netbp)); 53 if (netbp == NULL) { 54 return LIB_ERR_MALLOC_FAIL; 55 } 56 57 netbp->dev_q = dev_q; 58 59 err = net_buf_grow(netbp, numbuf, size); 60 if (err_is_fail(err)) { 61 free(netbp); 62 return err; 63 } 64 65 *retbp = netbp; 66 67 return SYS_ERR_OK; 68} 69 70errval_t net_buf_pool_free(struct net_buf_pool *retbp) 71{ 72 return SYS_ERR_OK; 73} 74 75/** 76 * @brief adds a previously allocated frame to the buffer pool 77 * 78 * @param bp buffer pool to add the frame to 79 * @param frame frame capability 80 * @param buffersize size of a buffer 81 * 82 * @return SYS_ERR_OK on success, errval on failure 83 */ 84errval_t net_buf_add(struct net_buf_pool *bp, struct capref frame, size_t buffersize) 85{ 86 errval_t err; 87 88 89 struct net_buf_region *reg = calloc(1, sizeof(struct net_buf_region)); 90 if (reg == NULL) { 91 return LIB_ERR_MALLOC_FAIL; 92 } 93 94 reg->buffer_size = ROUND_UP(buffersize, NETWORKING_BUFFER_ALIGN); 95 reg->buffer_shift = 0; 96 while(!((reg->buffer_size >> reg->buffer_shift) & 0x1)) { 97 reg->buffer_shift++; 98 } 99 100 reg->framecap = frame; 101 reg->pool = bp; 102 103 err = invoke_frame_identify(reg->framecap, ®->frame); 104 if (err_is_fail(err)) { 105 goto out_err1; 106 } 107 108 NETDEBUG("bp=%p, framesize=%zu kB, elementsize=%zu\n", bp, 109 reg->frame.bytes >> 10, buffersize); 110 111 112 size_t numbuf = reg->frame.bytes / reg->buffer_size; 113 assert(numbuf * reg->buffer_size <= reg->frame.bytes); 114 115 reg->netbufs = calloc(numbuf, sizeof(struct net_buf_p)); 116 if (reg->netbufs == NULL) { 117 err = LIB_ERR_MALLOC_FAIL; 118 goto out_err1; 119 } 120 121 err = vspace_map_one_frame_attr(®->vbase, reg->frame.bytes, reg->framecap, 122 NETWORKING_DEFAULT_BUFFER_FLAGS, NULL, NULL); 123 if (err_is_fail(err)) { 124 goto out_err2; 125 } 126 127 NETDEBUG("netbufs mapped at %p\n", reg->vbase); 128 129 if (bp->dev_q) { 130 debug_printf("netbuf: registering region with devq...\n"); 131 err = devq_register(bp->dev_q, reg->framecap, ®->regionid); 132 if (err_is_fail(err)) { 133 goto out_err1; 134 } 135 NETDEBUG("registered region with devq. pbase=%" PRIxGENPADDR ", regionid=%" PRIx32 "\n", 136 reg->frame.base, reg->regionid); 137 } 138 139 size_t offset = 0; 140 for (size_t i = 0; i < numbuf; i++) { 141 struct net_buf_p *nb = ®->netbufs[i]; 142 143 nb->offset = offset; 144 nb->vbase = reg->vbase + offset; 145 nb->region = reg; 146 nb->pbuf.custom_free_function = net_buf_free; 147#if NETBUF_DEBGUG 148 nb->allocated = 0; 149 nb->enqueued = 0; 150 nb->flags = 0; 151 nb->magic = 0xdeadbeefcafebabe; 152#endif 153 /* enqueue to freelist */ 154 nb->pbuf.pbuf.next = bp->pbufs; 155 bp->pbufs = &nb->pbuf.pbuf; 156 bp->buffer_count++; 157 bp->buffer_free++; 158 offset += reg->buffer_size; 159 } 160 161 reg->next = bp->regions; 162 bp->regions = reg; 163 164 assert(bp->pbufs); 165 166 NETDEBUG("new region added to pool. free count: %zu / %zu\n", 167 bp->buffer_free, bp->buffer_count); 168 169 return SYS_ERR_OK; 170 171 out_err2: 172 free(reg->netbufs); 173 out_err1: 174 free(reg); 175 176 return err; 177} 178 179/** 180 * @brief grows the number of available buffers 181 * 182 * @param bp buffer pool to grow 183 * @param numbuf number of buffers to create 184 * @param size size of a buffer 185 * 186 * @return SYS_ERR_OK on success, errval on failure 187 */ 188errval_t net_buf_grow(struct net_buf_pool *bp, size_t numbuf, 189 size_t size) 190{ 191 errval_t err; 192 193 NETDEBUG("bp=%p, numbuf=%zu, size=%zu\n", bp, numbuf, size); 194 195 size = ROUND_UP(size, NETWORKING_BUFFER_ALIGN); 196 197 size_t alloc_size = ROUND_UP(numbuf * size, BASE_PAGE_SIZE); 198 199 NETDEBUG("allocate frame of %zu kB\n", alloc_size >> 10); 200 201 struct capref frame; 202 err = frame_alloc(&frame, alloc_size, &alloc_size); 203 if (err_is_fail(err)) { 204 return err; 205 } 206 207 err = net_buf_add(bp, frame, size); 208 if (err_is_fail(err)) { 209 cap_destroy(frame); 210 } 211 212 return err; 213} 214 215 216struct pbuf *net_buf_alloc(struct net_buf_pool *bp) 217{ 218 if (bp->pbufs) { 219 struct net_buf_p *nb = (struct net_buf_p *)bp->pbufs; 220#if BENCH_LWIP_STACK 221 nb->timestamp = 0; 222#endif 223 224#if NETBUF_DEBGUG 225 assert(nb->magic == 0xdeadbeefcafebabe); 226 assert(nb->allocated == 0); 227 assert(nb->enqueued == 0); 228 assert(nb->flags == 0); 229 230#endif 231 bp->pbufs = bp->pbufs->next; 232 bp->buffer_free--; 233 struct pbuf* p; 234 p = pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, &nb->pbuf, 235 nb->vbase, nb->region->buffer_size); 236#if NETBUF_DEBGUG 237 nb->allocated = 1; 238 assert(p->next == NULL); 239#endif 240 NETDEBUG("bp=%p, allocated pbuf=%p, free count %zu / %zu\n", bp, p, 241 bp->buffer_free, bp->buffer_count); 242 // printf("alloc: %p\n", p); 243 244 return p; 245 } 246 247 NETDEBUG("bp=%p has no free buffers. Free %zu / %zu\n", bp, bp->buffer_free, 248 bp->buffer_count); 249 250 return NULL; 251} 252 253void net_buf_free(struct pbuf *p) 254{ 255 NETDEBUG("pbuf=%p\n", p); 256 257 if (p->next) { 258 debug_printf("!!!!!! p->NEXT was not NULL (%p)\n", p->next); 259 } 260 261 // printf("free: %p\n", p); 262 263 // TODO sanity checks ? 264 struct net_buf_p *nb = (struct net_buf_p *)p; 265 266#if NETBUF_DEBGUG 267 assert(nb->magic == 0xdeadbeefcafebabe); 268 assert(p->ref == 0); 269 assert(nb->allocated == 1); 270 assert(nb->enqueued == 0); 271 assert(nb->flags == 0); 272 nb->allocated = 0; 273 274#endif 275 276 struct net_buf_pool *bp = nb->region->pool; 277 p->next = bp->pbufs; 278 bp->pbufs = p; 279 bp->buffer_free++; 280} 281 282struct pbuf *net_buf_get_by_region(struct net_buf_pool *bp, 283 uint32_t regionid, size_t offset) 284{ 285 NETDEBUG("bp=%p, rid=%u, offset=%zu\n", bp, regionid, offset); 286 287 struct net_buf_region *reg = bp->regions; 288 while(reg) { 289 if (reg->regionid == regionid) { 290 /* found */ 291 if (reg->frame.bytes < offset) { 292 return NULL; 293 } 294 295 assert((offset & (reg->buffer_size - 1)) == 0); 296 assert(offset / reg->buffer_size < reg->pool->buffer_count); 297 struct net_buf_p *nb = reg->netbufs + (offset / reg->buffer_size); 298 299 assert((offset / reg->buffer_size) == (offset >> reg->buffer_shift)); 300 301 assert(nb->offset == offset); 302 303 return (struct pbuf *)nb; 304 } 305 reg = reg->next; 306 } 307 return NULL; 308} 309