libnet: refactor initialization code
[barrelfish] / lib / net / netbufs.c
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  */
43 errval_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
70 errval_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  */
84 errval_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, &reg->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(&reg->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, &reg->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 = &reg->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  */
188 errval_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
216 struct 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
253 void 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\n");
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
282 struct 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 }