Add helper function block_append_extra
[akaros.git] / kern / src / ns / allocb.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15 #include <process.h>
16
17 /* Note that Hdrspc is only available via padblock (to the 'left' of the rp). */
18 enum {
19         Hdrspc = 128,           /* leave room for high-level headers */
20         Bdead = 0x51494F42,     /* "QIOB" */
21         BLOCKALIGN = 32,        /* was the old BY2V in inferno, which was 8 */
22 };
23
24 static atomic_t ialloc_bytes = 0;
25
26 /*
27  *  allocate blocks (round data base address to 64 bit boundary).
28  *  if mallocz gives us more than we asked for, leave room at the front
29  *  for header.
30  */
31 static struct block *_allocb(int size, int mem_flags)
32 {
33         struct block *b;
34         uintptr_t addr;
35         int n;
36
37         b = kmalloc(sizeof(struct block) + size + Hdrspc + (BLOCKALIGN - 1),
38                                 mem_flags);
39         if (b == NULL)
40                 return NULL;
41
42         b->next = NULL;
43         b->list = NULL;
44         b->free = NULL;
45         b->flag = 0;
46         b->extra_len = 0;
47         b->nr_extra_bufs = 0;
48         b->extra_data = 0;
49
50         addr = (uintptr_t) b;
51         addr = ROUNDUP(addr + sizeof(struct block), BLOCKALIGN);
52         b->base = (uint8_t *) addr;
53         /* TODO: support this */
54         /* interesting. We can ask the allocator, after allocating,
55          * the *real* size of the block we got. Very nice.
56          * Not on akaros yet.
57          b->lim = ((uint8_t*)b) + msize(b);
58          */
59         b->lim =
60                 ((uint8_t *) b) + sizeof(struct block) + size + Hdrspc + (BLOCKALIGN -
61                                                                                                                                   1);
62         b->rp = b->base;
63         n = b->lim - b->base - size;
64         b->rp += n & ~(BLOCKALIGN - 1);
65         b->wp = b->rp;
66         /* b->base is aligned, rounded up from b
67          * b->lim is the upper bound on our malloc
68          * b->rp is advanced by some aligned amount, based on how much extra we
69          * received from kmalloc and the Hdrspc. */
70         return b;
71 }
72
73 struct block *allocb(int size)
74 {
75         return _allocb(size, KMALLOC_WAIT);
76 }
77
78 /* Makes sure b has nr_bufs extra_data.  Will grow, but not shrink, an existing
79  * extra_data array.  When growing, it'll copy over the old entries.  All new
80  * entries will be zeroed.  mem_flags determines if we'll block on kmallocs.
81  *
82  * Return 0 on success or -1 on error.
83  * Caller is responsible for concurrent access to the block's metadata. */
84 int block_add_extd(struct block *b, unsigned int nr_bufs, int mem_flags)
85 {
86         unsigned int old_nr_bufs = b->nr_extra_bufs;
87         size_t old_amt = sizeof(struct extra_bdata) * old_nr_bufs;
88         size_t new_amt = sizeof(struct extra_bdata) * nr_bufs;
89         void *new_bdata;
90
91         if (old_nr_bufs >= nr_bufs)
92                 return 0;
93         if (b->extra_data) {
94                 new_bdata = krealloc(b->extra_data, new_amt, mem_flags);
95                 if (!new_bdata)
96                         return -1;
97                 memset(new_bdata + old_amt, 0, new_amt - old_amt);
98         } else {
99                 new_bdata = kzmalloc(new_amt, mem_flags);
100                 if (!new_bdata)
101                         return - 1;
102         }
103         b->extra_data = new_bdata;
104         b->nr_extra_bufs = nr_bufs;
105         return 0;
106 }
107
108 /* Go backwards from the end of the list, remember the last unused slot, and
109  * stop when a used slot is encountered. */
110 static struct extra_bdata *next_unused_slot(struct block *b)
111 {
112         struct extra_bdata *ebd = NULL;
113
114         for (int i = b->nr_extra_bufs - 1; i >= 0; i--) {
115                 if (b->extra_data[i].base)
116                         break;
117                 ebd = &b->extra_data[i];
118         }
119         return ebd;
120 }
121
122 /* Append a zero-filled extra data buffer of length @len to block @b.
123  * Reuse an unused extra data slot if there's any.
124  * Return 0 on success or -1 on error. */
125 int block_append_extra(struct block *b, int len, int mem_flags)
126 {
127         unsigned int nr_bufs = b->nr_extra_bufs + 1;
128         struct extra_bdata *ebd;
129
130         ebd = next_unused_slot(b);
131         if (!ebd) {
132                 if (block_add_extd(b, nr_bufs, mem_flags) != 0)
133                         return -1;
134                 ebd = next_unused_slot(b);
135                 assert(ebd);
136         }
137         ebd->base = (uintptr_t)kzmalloc(len, mem_flags);
138         if (!ebd->base)
139                 return -1;
140         ebd->off = 0;
141         ebd->len = len;
142         b->extra_len += ebd->len;
143         return 0;
144 }
145
146 /*
147  *  interrupt time allocation
148  */
149 struct block *iallocb(int size)
150 {
151         struct block *b;
152
153 #if 0   /* conf is some inferno global config */
154         if (atomic_read(&ialloc_bytes) > conf.ialloc) {
155                 //printk("iallocb: limited %lu/%lu\n", atomic_read(&ialloc_bytes),
156                 //       conf.ialloc);
157                 return NULL;
158         }
159 #endif
160
161         b = _allocb(size, 0);   /* no KMALLOC_WAIT */
162         if (b == NULL) {
163                 //printk("iallocb: no memory %lu/%lu\n", atomic_read(&ialloc_bytes),
164                 //       conf.ialloc);
165                 return NULL;
166         }
167         b->flag = BINTR;
168
169         atomic_add(&ialloc_bytes, b->lim - b->base);
170
171         return b;
172 }
173
174 void free_block_extra(struct block *b)
175 {
176         struct extra_bdata *ebd;
177
178         /* assuming our release method is kfree, which will change when we support
179          * user buffers */
180         for (int i = 0; i < b->nr_extra_bufs; i++) {
181                 ebd = &b->extra_data[i];
182                 if (ebd->base)
183                         kfree((void*)ebd->base);
184         }
185         b->extra_len = 0;
186         b->nr_extra_bufs = 0;
187         kfree(b->extra_data);   /* harmless if it is 0 */
188         b->extra_data = 0;              /* in case the block is reused by a free override */
189 }
190
191 void freeb(struct block *b)
192 {
193         void *dead = (void *)Bdead;
194
195         if (b == NULL)
196                 return;
197
198         free_block_extra(b);
199         /*
200          * drivers which perform non cache coherent DMA manage their own buffer
201          * pool of uncached buffers and provide their own free routine.
202          */
203         if (b->free) {
204                 b->free(b);
205                 return;
206         }
207         if (b->flag & BINTR) {
208                 /* subtracting the size of b */
209                 atomic_add(&ialloc_bytes, -(b->lim - b->base));
210         }
211
212         /* poison the block in case someone is still holding onto it */
213         b->next = dead;
214         b->rp = dead;
215         b->wp = dead;
216         b->lim = dead;
217         b->base = dead;
218
219         kfree(b);
220 }
221
222 void checkb(struct block *b, char *msg)
223 {
224         void *dead = (void *)Bdead;
225         struct extra_bdata *ebd;
226
227         if (b == dead)
228                 panic("checkb b %s 0x%lx", msg, b);
229         if (b->base == dead || b->lim == dead || b->next == dead
230                 || b->rp == dead || b->wp == dead) {
231                 printd("checkb: base 0x%8.8lx lim 0x%8.8lx next 0x%8.8lx\n",
232                            b->base, b->lim, b->next);
233                 printd("checkb: rp 0x%8.8lx wp 0x%8.8lx\n", b->rp, b->wp);
234                 panic("checkb dead: %s\n", msg);
235         }
236
237         if (b->base > b->lim)
238                 panic("checkb 0 %s 0x%lx 0x%lx", msg, b->base, b->lim);
239         if (b->rp < b->base)
240                 panic("checkb 1 %s 0x%lx 0x%lx", msg, b->base, b->rp);
241         if (b->wp < b->base)
242                 panic("checkb 2 %s 0x%lx 0x%lx", msg, b->base, b->wp);
243         if (b->rp > b->lim)
244                 panic("checkb 3 %s 0x%lx 0x%lx", msg, b->rp, b->lim);
245         if (b->wp > b->lim)
246                 panic("checkb 4 %s 0x%lx 0x%lx", msg, b->wp, b->lim);
247         if (b->nr_extra_bufs && !b->extra_data)
248                 panic("checkb 5 %s missing extra_data", msg);
249
250         for (int i = 0; i < b->nr_extra_bufs; i++) {
251                 ebd = &b->extra_data[i];
252                 if (ebd->base) {
253                         if (!kmalloc_refcnt((void*)ebd->base))
254                                 panic("checkb buf %d, base %p has no refcnt!\n", i, ebd->base);
255                 }
256         }
257
258 }
259
260 void iallocsummary(void)
261 {
262         printd("ialloc %lu/%lu\n", atomic_read(&ialloc_bytes), 0 /*conf.ialloc */ );
263 }
264
265 void printblock(struct block *b)
266 {
267         unsigned char *c;
268         unsigned int off, elen;
269         struct extra_bdata *e;
270
271         if (b == NULL) {
272                 printk("block is null\n");
273                 return;
274         }
275
276         printk("block of BLEN = %d, with %d header and %d data in %d extras\n",
277                BLEN(b), BHLEN(b), b->extra_len, b->nr_extra_bufs);
278
279         printk("header:\n");
280         printk("%2x:\t", 0);
281         off = 0;
282         for (c = b->rp; c < b->wp; c++) {
283                 printk("  %02x", *c & 0xff);
284                 off++;
285                 if (off % 8 == 0) {
286                         printk("\n");
287                         printk("%2x:\t", off);
288                 }
289         }
290         printk("\n");
291         elen = b->extra_len;
292         for (int i = 0; (i < b->nr_extra_bufs) && elen; i++) {
293                 e = &b->extra_data[i];
294                 if (e->len == 0)
295                         continue;
296                 elen -= e->len;
297                 printk("data %d:\n", i);
298                 printk("%2x:\t", 0);
299                 for (off = 0; off < e->len; off++) {
300                         c = (unsigned char *)e->base + e->off + off;
301                         printk("  %02x", *c & 0xff);
302                         if ((off + 1) % 8 == 0 && off +1 < e->len) {
303                                 printk("\n");
304                                 printk("%2x:\t", off + 1);
305                         }
306                 }
307         }
308         printk("\n");
309 }