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