Add the Inferno license to files we got from Inferno
[akaros.git] / kern / src / ns / allocb.c
index 068f05d..ab68534 100644 (file)
@@ -1,4 +1,31 @@
-// INFERNO
+/* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+ * Portions Copyright © 1997-1999 Vita Nuova Limited
+ * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
+ *                                (www.vitanuova.com)
+ * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+ *
+ * Modified for the Akaros operating system:
+ * Copyright (c) 2013-2014 The Regents of the University of California
+ * Copyright (c) 2013-2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
 #include <vfs.h>
 #include <kfs.h>
 #include <slab.h>
 #include <ip.h>
 #include <process.h>
 
-enum
-{
-       Hdrspc          = 64,           /* leave room for high-level headers */
-       Bdead           = 0x51494F42,   /* "QIOB" */
-       BLOCKALIGN  = 32,               /* was the old BY2V in inferno, which was 8 */
+/* Note that Hdrspc is only available via padblock (to the 'left' of the rp). */
+enum {
+       Hdrspc = 128,           /* leave room for high-level headers */
+       Bdead = 0x51494F42,     /* "QIOB" */
+       BLOCKALIGN = 32,        /* was the old BY2V in inferno, which was 8 */
 };
 
 static atomic_t ialloc_bytes = 0;
@@ -28,15 +55,14 @@ static atomic_t ialloc_bytes = 0;
  *  if mallocz gives us more than we asked for, leave room at the front
  *  for header.
  */
-struct block *_allocb(int size)
+static struct block *_allocb(int size, int mem_flags)
 {
        struct block *b;
        uintptr_t addr;
        int n;
 
-       /* TODO: verify we end up with properly aligned blocks */
-       b = kzmalloc(sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - 1),
-                    KMALLOC_WAIT);
+       b = kmalloc(sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - 1),
+                               mem_flags);
        if (b == NULL)
                return NULL;
 
@@ -44,29 +70,104 @@ struct block *_allocb(int size)
        b->list = NULL;
        b->free = NULL;
        b->flag = 0;
+       b->extra_len = 0;
+       b->nr_extra_bufs = 0;
+       b->extra_data = 0;
 
-       addr = (uintptr_t)b;
+       addr = (uintptr_t) b;
        addr = ROUNDUP(addr + sizeof(struct block), BLOCKALIGN);
-       b->base = (uint8_t*)addr;
-       b->lim = ((uint8_t*)b) + msize(b);
+       b->base = (uint8_t *) addr;
+       /* TODO: support this */
+       /* interesting. We can ask the allocator, after allocating,
+        * the *real* size of the block we got. Very nice.
+        * Not on akaros yet.
+        b->lim = ((uint8_t*)b) + msize(b);
+        */
+       b->lim =
+               ((uint8_t *) b) + sizeof(struct block) + size + Hdrspc + (BLOCKALIGN -
+                                                                                                                                 1);
        b->rp = b->base;
        n = b->lim - b->base - size;
        b->rp += n & ~(BLOCKALIGN - 1);
        b->wp = b->rp;
-
+       /* b->base is aligned, rounded up from b
+        * b->lim is the upper bound on our malloc
+        * b->rp is advanced by some aligned amount, based on how much extra we
+        * received from kmalloc and the Hdrspc. */
        return b;
 }
 
 struct block *allocb(int size)
 {
-       struct block *b;
+       return _allocb(size, KMALLOC_WAIT);
+}
 
-       if (!current)
-               panic("allocb outside process: %8.8lux", getcallerpc(&size));
-       b = _allocb(size);
-       if (b == 0)
-               exhausted("Blocks");
-       return b;
+/* Makes sure b has nr_bufs extra_data.  Will grow, but not shrink, an existing
+ * extra_data array.  When growing, it'll copy over the old entries.  All new
+ * entries will be zeroed.  mem_flags determines if we'll block on kmallocs.
+ *
+ * Return 0 on success or -1 on error.
+ * Caller is responsible for concurrent access to the block's metadata. */
+int block_add_extd(struct block *b, unsigned int nr_bufs, int mem_flags)
+{
+       unsigned int old_nr_bufs = b->nr_extra_bufs;
+       size_t old_amt = sizeof(struct extra_bdata) * old_nr_bufs;
+       size_t new_amt = sizeof(struct extra_bdata) * nr_bufs;
+       void *new_bdata;
+
+       if (old_nr_bufs >= nr_bufs)
+               return 0;
+       if (b->extra_data) {
+               new_bdata = krealloc(b->extra_data, new_amt, mem_flags);
+               if (!new_bdata)
+                       return -1;
+               memset(new_bdata + old_amt, 0, new_amt - old_amt);
+       } else {
+               new_bdata = kzmalloc(new_amt, mem_flags);
+               if (!new_bdata)
+                       return - 1;
+       }
+       b->extra_data = new_bdata;
+       b->nr_extra_bufs = nr_bufs;
+       return 0;
+}
+
+/* Go backwards from the end of the list, remember the last unused slot, and
+ * stop when a used slot is encountered. */
+static struct extra_bdata *next_unused_slot(struct block *b)
+{
+       struct extra_bdata *ebd = NULL;
+
+       for (int i = b->nr_extra_bufs - 1; i >= 0; i--) {
+               if (b->extra_data[i].base)
+                       break;
+               ebd = &b->extra_data[i];
+       }
+       return ebd;
+}
+
+/* Append a zero-filled extra data buffer of length @len to block @b.
+ * Reuse an unused extra data slot if there's any.
+ * Return 0 on success or -1 on error. */
+int block_append_extra(struct block *b, int len, int mem_flags)
+{
+       unsigned int nr_bufs = b->nr_extra_bufs + 1;
+       struct extra_bdata *ebd;
+
+       ebd = next_unused_slot(b);
+       if (!ebd) {
+               if (block_add_extd(b, nr_bufs, mem_flags) != 0)
+                       return -1;
+               ebd = next_unused_slot(b);
+               assert(ebd);
+       }
+       ebd->base = (uintptr_t)kzmalloc(len, mem_flags);
+       if (!ebd->base)
+               return -1;
+       ebd->off = 0;
+       ebd->len = len;
+       b->extra_len += ebd->len;
+       return 0;
 }
 
 /*
@@ -76,17 +177,17 @@ struct block *iallocb(int size)
 {
        struct block *b;
 
-       #if 0 /* conf is some inferno global config */
+#if 0  /* conf is some inferno global config */
        if (atomic_read(&ialloc_bytes) > conf.ialloc) {
-               //printk("iallocb: limited %lud/%lud\n", atomic_read(&ialloc_bytes),
+               //printk("iallocb: limited %lu/%lu\n", atomic_read(&ialloc_bytes),
                //       conf.ialloc);
                return NULL;
        }
-       #endif
+#endif
 
-       b = _allocb(size);
+       b = _allocb(size, 0);   /* no KMALLOC_WAIT */
        if (b == NULL) {
-               //printk("iallocb: no memory %lud/%lud\n", atomic_read(&ialloc_bytes),
+               //printk("iallocb: no memory %lu/%lu\n", atomic_read(&ialloc_bytes),
                //       conf.ialloc);
                return NULL;
        }
@@ -97,6 +198,23 @@ struct block *iallocb(int size)
        return b;
 }
 
+void free_block_extra(struct block *b)
+{
+       struct extra_bdata *ebd;
+
+       /* assuming our release method is kfree, which will change when we support
+        * user buffers */
+       for (int i = 0; i < b->nr_extra_bufs; i++) {
+               ebd = &b->extra_data[i];
+               if (ebd->base)
+                       kfree((void*)ebd->base);
+       }
+       b->extra_len = 0;
+       b->nr_extra_bufs = 0;
+       kfree(b->extra_data);   /* harmless if it is 0 */
+       b->extra_data = 0;              /* in case the block is reused by a free override */
+}
+
 void freeb(struct block *b)
 {
        void *dead = (void *)Bdead;
@@ -104,6 +222,7 @@ void freeb(struct block *b)
        if (b == NULL)
                return;
 
+       free_block_extra(b);
        /*
         * drivers which perform non cache coherent DMA manage their own buffer
         * pool of uncached buffers and provide their own free routine.
@@ -130,31 +249,88 @@ void freeb(struct block *b)
 void checkb(struct block *b, char *msg)
 {
        void *dead = (void *)Bdead;
+       struct extra_bdata *ebd;
 
        if (b == dead)
-               panic("checkb b %s %lux", msg, b);
+               panic("checkb b %s 0x%lx", msg, b);
        if (b->base == dead || b->lim == dead || b->next == dead
                || b->rp == dead || b->wp == dead) {
-               printd("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n",
+               printd("checkb: base 0x%8.8lx lim 0x%8.8lx next 0x%8.8lx\n",
                           b->base, b->lim, b->next);
-               printd("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp);
+               printd("checkb: rp 0x%8.8lx wp 0x%8.8lx\n", b->rp, b->wp);
                panic("checkb dead: %s\n", msg);
        }
 
        if (b->base > b->lim)
-               panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
+               panic("checkb 0 %s 0x%lx 0x%lx", msg, b->base, b->lim);
        if (b->rp < b->base)
-               panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
+               panic("checkb 1 %s 0x%lx 0x%lx", msg, b->base, b->rp);
        if (b->wp < b->base)
-               panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
+               panic("checkb 2 %s 0x%lx 0x%lx", msg, b->base, b->wp);
        if (b->rp > b->lim)
-               panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
+               panic("checkb 3 %s 0x%lx 0x%lx", msg, b->rp, b->lim);
        if (b->wp > b->lim)
-               panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
+               panic("checkb 4 %s 0x%lx 0x%lx", msg, b->wp, b->lim);
+       if (b->nr_extra_bufs && !b->extra_data)
+               panic("checkb 5 %s missing extra_data", msg);
+
+       for (int i = 0; i < b->nr_extra_bufs; i++) {
+               ebd = &b->extra_data[i];
+               if (ebd->base) {
+                       if (!kmalloc_refcnt((void*)ebd->base))
+                               panic("checkb buf %d, base %p has no refcnt!\n", i, ebd->base);
+               }
+       }
 
 }
 
 void iallocsummary(void)
 {
-       printd("ialloc %lud/%lud\n", atomic_read(&ialloc_bytes), 0 /*conf.ialloc*/);
+       printd("ialloc %lu/%lu\n", atomic_read(&ialloc_bytes), 0 /*conf.ialloc */ );
+}
+
+void printblock(struct block *b)
+{
+       unsigned char *c;
+       unsigned int off, elen;
+       struct extra_bdata *e;
+
+       if (b == NULL) {
+               printk("block is null\n");
+               return;
+       }
+
+       printk("block of BLEN = %d, with %d header and %d data in %d extras\n",
+              BLEN(b), BHLEN(b), b->extra_len, b->nr_extra_bufs);
+
+       printk("header:\n");
+       printk("%2x:\t", 0);
+       off = 0;
+       for (c = b->rp; c < b->wp; c++) {
+               printk("  %02x", *c & 0xff);
+               off++;
+               if (off % 8 == 0) {
+                       printk("\n");
+                       printk("%2x:\t", off);
+               }
+       }
+       printk("\n");
+       elen = b->extra_len;
+       for (int i = 0; (i < b->nr_extra_bufs) && elen; i++) {
+               e = &b->extra_data[i];
+               if (e->len == 0)
+                       continue;
+               elen -= e->len;
+               printk("data %d:\n", i);
+               printk("%2x:\t", 0);
+               for (off = 0; off < e->len; off++) {
+                       c = (unsigned char *)e->base + e->off + off;
+                       printk("  %02x", *c & 0xff);
+                       if ((off + 1) % 8 == 0 && off +1 < e->len) {
+                               printk("\n");
+                               printk("%2x:\t", off + 1);
+                       }
+               }
+       }
+       printk("\n");
 }