libnet: adding basic benchmarking for enqueue and lwip stack
[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_init(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 /**
71  * @brief adds a previously allocated frame to the buffer pool
72  *
73  * @param bp            buffer pool to add the frame to
74  * @param frame         frame capability
75  * @param buffersize    size of a buffer
76  *
77  * @return SYS_ERR_OK on success, errval on failure
78  */
79 errval_t net_buf_add(struct net_buf_pool *bp, struct capref frame, size_t buffersize)
80 {
81     errval_t err;
82
83
84     struct net_buf_region *reg = calloc(1, sizeof(struct net_buf_region));
85     if (reg == NULL) {
86         return LIB_ERR_MALLOC_FAIL;
87     }
88
89     reg->buffer_size = ROUND_UP(buffersize, NETWORKING_BUFFER_ALIGN);
90     reg->framecap = frame;
91     reg->pool = bp;
92
93     err = invoke_frame_identify(reg->framecap, &reg->frame);
94     if (err_is_fail(err)) {
95         goto out_err1;
96     }
97
98     NETDEBUG("bp=%p, framesize=%zu kB, elementsize=%zu\n", bp,
99              reg->frame.bytes >> 10, buffersize);
100
101
102     size_t numbuf = reg->frame.bytes / reg->buffer_size;
103
104
105     reg->netbufs = calloc(numbuf, sizeof(struct net_buf_p));
106     if (reg->netbufs == NULL) {
107         err = LIB_ERR_MALLOC_FAIL;
108         goto out_err1;
109     }
110
111     err = vspace_map_one_frame_attr(&reg->vbase, reg->frame.bytes, reg->framecap,
112                                     NETWORKING_DEFAULT_BUFFER_FLAGS, NULL, NULL);
113     if (err_is_fail(err)) {
114         goto out_err2;
115     }
116
117     NETDEBUG("netbufs mapped at %p\n", reg->vbase);
118
119     if (bp->dev_q) {
120         debug_printf("netbuf: registering region with devq...\n");
121         err = devq_register(bp->dev_q, reg->framecap, &reg->regionid);
122         if (err_is_fail(err)) {
123             goto out_err1;
124         }
125         NETDEBUG("registered region with devq. pbase=%" PRIxGENPADDR ", regionid=%" PRIx32 "\n",
126                   reg->frame.base, reg->regionid);
127     }
128
129     size_t offset = 0;
130     for (size_t i = 0; i < numbuf; i++) {
131         struct net_buf_p *nb = &reg->netbufs[i];
132
133         nb->offset = offset;
134         nb->vbase = reg->vbase + offset;
135         nb->region = reg;
136         nb->pbuf.custom_free_function = net_buf_free;
137         nb->allocated = 0;
138
139         /* enqueue to freelist */
140         nb->pbuf.pbuf.next =  bp->pbufs;
141         bp->pbufs = &nb->pbuf.pbuf;
142         bp->buffer_count++;
143         bp->buffer_free++;
144         offset += reg->buffer_size;
145     }
146
147     reg->next = bp->regions;
148     bp->regions = reg;
149
150     assert(bp->pbufs);
151
152     NETDEBUG("new region added to pool. free count: %zu / %zu\n",
153              bp->buffer_free, bp->buffer_count);
154
155     return SYS_ERR_OK;
156
157     out_err2:
158     free(reg->netbufs);
159     out_err1:
160     free(reg);
161
162     return err;
163 }
164
165 /**
166  * @brief grows the number of available buffers
167  *
168  * @param bp        buffer pool to grow
169  * @param numbuf    number of buffers to create
170  * @param size      size of a buffer
171  *
172  * @return SYS_ERR_OK on success, errval on failure
173  */
174 errval_t net_buf_grow(struct net_buf_pool *bp, size_t numbuf,
175                                 size_t size)
176 {
177     errval_t err;
178
179     NETDEBUG("bp=%p, numbuf=%zu, size=%zu\n", bp, numbuf, size);
180
181     size = ROUND_UP(size, NETWORKING_BUFFER_ALIGN);
182
183     size_t alloc_size = ROUND_UP(numbuf * size, BASE_PAGE_SIZE);
184
185     NETDEBUG("allocate frame of %zu kB\n", alloc_size >> 10);
186
187     struct capref frame;
188     err = frame_alloc(&frame, alloc_size, &alloc_size);
189     if (err_is_fail(err)) {
190         return err;
191     }
192
193     err =  net_buf_add(bp, frame, size);
194     if (err_is_fail(err)) {
195         cap_destroy(frame);
196     }
197
198     return err;
199 }
200
201
202 struct pbuf *net_buf_alloc(struct net_buf_pool *bp)
203 {
204     if (bp->pbufs) {
205         struct net_buf_p *nb = (struct net_buf_p *)bp->pbufs;
206 #if BENCH_LWIP_STACK
207         nb->timestamp = 0;
208 #endif
209         assert(nb->allocated == 0);
210
211         bp->pbufs = bp->pbufs->next;
212         bp->buffer_free--;
213         struct pbuf* p;
214         p = pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, &nb->pbuf,
215                                 nb->vbase, nb->region->buffer_size);
216
217         nb->allocated = 1;
218         NETDEBUG("bp=%p, allocated pbuf=%p, free count %zu / %zu\n", bp, p,
219                  bp->buffer_free, bp->buffer_count);
220         return p;
221     }
222
223     NETDEBUG("bp=%p has no free buffers. Free %zu / %zu\n", bp, bp->buffer_free,
224                  bp->buffer_count);
225
226     return NULL;
227 }
228
229 void net_buf_free(struct pbuf *p)
230 {
231     NETDEBUG("pbuf=%p\n", p);
232     // TODO sanity checks ?
233     struct net_buf_p *nb = (struct net_buf_p *)p;
234     struct net_buf_pool *bp = nb->region->pool;
235     nb->pbuf.pbuf.next =  bp->pbufs;
236     bp->pbufs = &nb->pbuf.pbuf;
237     bp->buffer_free++;
238     assert(nb->allocated == 1);
239     nb->allocated = 0;
240 }
241
242 struct pbuf *net_buf_get_by_region(struct net_buf_pool *bp,
243                                              uint32_t regionid, size_t offset)
244 {
245     NETDEBUG("bp=%p, rid=%u, offset=%zu\n", bp, regionid, offset);
246
247     struct net_buf_region *reg = bp->regions;
248     while(reg) {
249         if (reg->regionid == regionid) {
250             /* found */
251             if (reg->frame.bytes < offset) {
252                 return NULL;
253             }
254             assert(reg->netbufs[offset / reg->buffer_size].allocated);
255             return &reg->netbufs[offset / reg->buffer_size].pbuf.pbuf;
256         }
257         reg = reg->next;
258     }
259     return NULL;
260 }