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, &reg->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, &reg->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, &reg->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 = &reg->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