9ns: Fix devtab function pointer signatures
[akaros.git] / kern / src / ns / allocb.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <kfs.h>
31 #include <slab.h>
32 #include <kmalloc.h>
33 #include <kref.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <error.h>
38 #include <cpio.h>
39 #include <pmap.h>
40 #include <smp.h>
41 #include <net/ip.h>
42 #include <process.h>
43
44 /* Note that Hdrspc is only available via padblock (to the 'left' of the rp). */
45 enum {
46         Hdrspc = 128,           /* leave room for high-level headers */
47         Bdead = 0x51494F42,     /* "QIOB" */
48         BLOCKALIGN = 32,        /* was the old BY2V in inferno, which was 8 */
49 };
50
51 /*
52  *  allocate blocks (round data base address to 64 bit boundary).
53  *  if mallocz gives us more than we asked for, leave room at the front
54  *  for header.
55  */
56 struct block *block_alloc(size_t size, int mem_flags)
57 {
58         struct block *b;
59         uintptr_t addr;
60         int n;
61
62         /* If Hdrspc is not block aligned it will cause issues. */
63         static_assert(Hdrspc % BLOCKALIGN == 0);
64
65         b = kmalloc(sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - 1),
66                                 mem_flags);
67         if (b == NULL)
68                 return NULL;
69
70         b->next = NULL;
71         b->list = NULL;
72         b->free = NULL;
73         b->flag = 0;
74         b->extra_len = 0;
75         b->nr_extra_bufs = 0;
76         b->extra_data = 0;
77         b->mss = 0;
78         b->network_offset = 0;
79         b->transport_offset = 0;
80
81         addr = (uintptr_t) b;
82         addr = ROUNDUP(addr + sizeof(struct block), BLOCKALIGN);
83         b->base = (uint8_t *) addr;
84         /* TODO: support this */
85         /* interesting. We can ask the allocator, after allocating,
86          * the *real* size of the block we got. Very nice.
87          * Not on akaros yet.
88          b->lim = ((uint8_t*)b) + msize(b);
89          * See use of n in commented code below
90          */
91         b->lim =
92                 ((uint8_t *) b) + sizeof(struct block) + size + Hdrspc + (BLOCKALIGN -
93                                                                                                                                   1);
94         b->rp = b->base;
95         /* TODO: support this */
96         /* n is supposed to be Hdrspc + rear padding + extra reserved memory, but
97          * since we don't currently support checking how much memory was actually
98          * reserved, this is always Hdrspc + rear padding. After rounding that down
99          * to BLOCKALIGN, it's always Hdrpsc since the padding is < BLOCKALIGN.
100          n = b->lim - b->base - size;
101          b->rp += n & ~(BLOCKALIGN - 1);
102          */
103         b->rp += Hdrspc;
104         b->wp = b->rp;
105         /* b->base is aligned, rounded up from b
106          * b->lim is the upper bound on our malloc
107          * b->rp is advanced by some aligned amount, based on how much extra we
108          * received from kmalloc and the Hdrspc. */
109         return b;
110 }
111
112 /* Makes sure b has nr_bufs extra_data.  Will grow, but not shrink, an existing
113  * extra_data array.  When growing, it'll copy over the old entries.  All new
114  * entries will be zeroed.  mem_flags determines if we'll block on kmallocs.
115  *
116  * Return 0 on success or -1 on error.
117  * Caller is responsible for concurrent access to the block's metadata. */
118 int block_add_extd(struct block *b, unsigned int nr_bufs, int mem_flags)
119 {
120         unsigned int old_nr_bufs = b->nr_extra_bufs;
121         size_t old_amt = sizeof(struct extra_bdata) * old_nr_bufs;
122         size_t new_amt = sizeof(struct extra_bdata) * nr_bufs;
123         void *new_bdata;
124
125         if (old_nr_bufs >= nr_bufs)
126                 return 0;
127         if (b->extra_data) {
128                 new_bdata = krealloc(b->extra_data, new_amt, mem_flags);
129                 if (!new_bdata)
130                         return -1;
131                 memset(new_bdata + old_amt, 0, new_amt - old_amt);
132         } else {
133                 new_bdata = kzmalloc(new_amt, mem_flags);
134                 if (!new_bdata)
135                         return - 1;
136         }
137         b->extra_data = new_bdata;
138         b->nr_extra_bufs = nr_bufs;
139         return 0;
140 }
141
142 /* Go backwards from the end of the list, remember the last unused slot, and
143  * stop when a used slot is encountered. */
144 static struct extra_bdata *next_unused_slot(struct block *b)
145 {
146         struct extra_bdata *ebd = NULL;
147
148         for (int i = b->nr_extra_bufs - 1; i >= 0; i--) {
149                 if (b->extra_data[i].base)
150                         break;
151                 ebd = &b->extra_data[i];
152         }
153         return ebd;
154 }
155
156 /* Append an extra data buffer @base with offset @off of length @len to block
157  * @b.  Reuse an unused extra data slot if there's any.
158  * Return 0 on success or -1 on error. */
159 int block_append_extra(struct block *b, uintptr_t base, uint32_t off,
160                        uint32_t len, int mem_flags)
161 {
162         unsigned int nr_bufs = b->nr_extra_bufs + 1;
163         struct extra_bdata *ebd;
164
165         ebd = next_unused_slot(b);
166         if (!ebd) {
167                 if (block_add_extd(b, nr_bufs, mem_flags) != 0)
168                         return -1;
169                 ebd = next_unused_slot(b);
170                 assert(ebd);
171         }
172         ebd->base = base;
173         ebd->off = off;
174         ebd->len = len;
175         b->extra_len += ebd->len;
176         return 0;
177 }
178
179 /* There's metadata in each block related to the data payload.  For instance,
180  * the TSO mss, the offsets to various headers, whether csums are needed, etc.
181  * When you create a new block, like in copyblock, this will copy those bits
182  * over. */
183 void block_copy_metadata(struct block *new_b, struct block *old_b)
184 {
185         new_b->flag |= (old_b->flag & BLOCK_META_FLAGS);
186         new_b->tx_csum_offset = old_b->tx_csum_offset;
187         new_b->mss = old_b->mss;
188         new_b->network_offset = old_b->network_offset;
189         new_b->transport_offset = old_b->transport_offset;
190 }
191
192 void block_reset_metadata(struct block *b)
193 {
194         b->flag &= ~BLOCK_META_FLAGS;
195         b->tx_csum_offset = 0;
196         b->mss = 0;
197         b->network_offset = 0;
198         b->transport_offset = 0;
199 }
200
201 void free_block_extra(struct block *b)
202 {
203         struct extra_bdata *ebd;
204
205         /* assuming our release method is kfree, which will change when we support
206          * user buffers */
207         for (int i = 0; i < b->nr_extra_bufs; i++) {
208                 ebd = &b->extra_data[i];
209                 if (ebd->base)
210                         kfree((void*)ebd->base);
211         }
212         b->extra_len = 0;
213         b->nr_extra_bufs = 0;
214         kfree(b->extra_data);   /* harmless if it is 0 */
215         b->extra_data = 0;              /* in case the block is reused by a free override */
216 }
217
218 /* Frees a block, returning its size (len, not alloc) */
219 size_t freeb(struct block *b)
220 {
221         void *dead = (void *)Bdead;
222         size_t ret;
223
224         if (b == NULL)
225                 return 0;
226         ret = BLEN(b);
227         free_block_extra(b);
228         /*
229          * drivers which perform non cache coherent DMA manage their own buffer
230          * pool of uncached buffers and provide their own free routine.
231          */
232         if (b->free) {
233                 b->free(b);
234                 return ret;
235         }
236         /* poison the block in case someone is still holding onto it */
237         b->next = dead;
238         b->rp = dead;
239         b->wp = dead;
240         b->lim = dead;
241         b->base = dead;
242         kfree(b);
243         return ret;
244 }
245
246 /* Free a list of blocks, returning their total size. */
247 size_t freeblist(struct block *b)
248 {
249         struct block *next;
250         size_t ret = 0;
251
252         for (; b != 0; b = next) {
253                 next = b->next;
254                 b->next = 0;
255                 ret += freeb(b);
256         }
257         return ret;
258 }
259
260 void checkb(struct block *b, char *msg)
261 {
262         void *dead = (void *)Bdead;
263         struct extra_bdata *ebd;
264         size_t extra_len = 0;
265
266         if (b == dead)
267                 panic("checkb b %s 0x%lx", msg, b);
268         if (b->base == dead || b->lim == dead || b->next == dead
269                 || b->rp == dead || b->wp == dead) {
270                 printd("checkb: base 0x%8.8lx lim 0x%8.8lx next 0x%8.8lx\n",
271                            b->base, b->lim, b->next);
272                 printd("checkb: rp 0x%8.8lx wp 0x%8.8lx\n", b->rp, b->wp);
273                 panic("checkb dead: %s\n", msg);
274         }
275
276         if (b->base > b->lim)
277                 panic("checkb 0 %s 0x%lx 0x%lx", msg, b->base, b->lim);
278         if (b->rp < b->base)
279                 panic("checkb 1 %s 0x%lx 0x%lx", msg, b->base, b->rp);
280         if (b->wp < b->base)
281                 panic("checkb 2 %s 0x%lx 0x%lx", msg, b->base, b->wp);
282         if (b->rp > b->lim)
283                 panic("checkb 3 %s 0x%lx 0x%lx", msg, b->rp, b->lim);
284         if (b->wp > b->lim)
285                 panic("checkb 4 %s 0x%lx 0x%lx", msg, b->wp, b->lim);
286         if (b->nr_extra_bufs && !b->extra_data)
287                 panic("checkb 5 %s missing extra_data", msg);
288
289         for (int i = 0; i < b->nr_extra_bufs; i++) {
290                 ebd = &b->extra_data[i];
291                 if (!ebd->base && (ebd->off || ebd->len))
292                         panic("checkb %s: ebd %d has no base, but has off %d and len %d",
293                               msg, i, ebd->off, ebd->len);
294                 if (ebd->base) {
295                         if (!kmalloc_refcnt((void*)ebd->base))
296                                 panic("checkb %s: buf %d, base %p has no refcnt!\n", msg, i,
297                                       ebd->base);
298                         extra_len += ebd->len;
299                 }
300         }
301         if (extra_len != b->extra_len)
302                 panic("checkb %s: block extra_len %d differs from sum of ebd len %d",
303                       msg, b->extra_len, extra_len);
304 }
305
306 void printblock(struct block *b)
307 {
308         unsigned char *c;
309         unsigned int off, elen;
310         struct extra_bdata *e;
311
312         if (b == NULL) {
313                 printk("block is null\n");
314                 return;
315         }
316
317         printk("block of BLEN = %d, with %d header and %d data in %d extras\n",
318                BLEN(b), BHLEN(b), b->extra_len, b->nr_extra_bufs);
319
320         printk("header:\n");
321         printk("%2x:\t", 0);
322         off = 0;
323         for (c = b->rp; c < b->wp; c++) {
324                 printk("  %02x", *c & 0xff);
325                 off++;
326                 if (off % 8 == 0) {
327                         printk("\n");
328                         printk("%2x:\t", off);
329                 }
330         }
331         printk("\n");
332         elen = b->extra_len;
333         for (int i = 0; (i < b->nr_extra_bufs) && elen; i++) {
334                 e = &b->extra_data[i];
335                 if (e->len == 0)
336                         continue;
337                 elen -= e->len;
338                 printk("data %d:\n", i);
339                 printk("%2x:\t", 0);
340                 for (off = 0; off < e->len; off++) {
341                         c = (unsigned char *)e->base + e->off + off;
342                         printk("  %02x", *c & 0xff);
343                         if ((off + 1) % 8 == 0 && off +1 < e->len) {
344                                 printk("\n");
345                                 printk("%2x:\t", off + 1);
346                         }
347                 }
348         }
349         printk("\n");
350 }