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, &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 */
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