02c09637e1ac935dc22f33b0f231ea3361360dc9
[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 /*
109  *  interrupt time allocation
110  */
111 struct block *iallocb(int size)
112 {
113         struct block *b;
114
115 #if 0   /* conf is some inferno global config */
116         if (atomic_read(&ialloc_bytes) > conf.ialloc) {
117                 //printk("iallocb: limited %lu/%lu\n", atomic_read(&ialloc_bytes),
118                 //       conf.ialloc);
119                 return NULL;
120         }
121 #endif
122
123         b = _allocb(size, 0);   /* no KMALLOC_WAIT */
124         if (b == NULL) {
125                 //printk("iallocb: no memory %lu/%lu\n", atomic_read(&ialloc_bytes),
126                 //       conf.ialloc);
127                 return NULL;
128         }
129         b->flag = BINTR;
130
131         atomic_add(&ialloc_bytes, b->lim - b->base);
132
133         return b;
134 }
135
136 void free_block_extra(struct block *b)
137 {
138         struct extra_bdata *ebd;
139
140         /* assuming our release method is kfree, which will change when we support
141          * user buffers */
142         for (int i = 0; i < b->nr_extra_bufs; i++) {
143                 ebd = &b->extra_data[i];
144                 if (ebd->base)
145                         kfree((void*)ebd->base);
146         }
147         b->extra_len = 0;
148         b->nr_extra_bufs = 0;
149         kfree(b->extra_data);   /* harmless if it is 0 */
150         b->extra_data = 0;              /* in case the block is reused by a free override */
151 }
152
153 void freeb(struct block *b)
154 {
155         void *dead = (void *)Bdead;
156
157         if (b == NULL)
158                 return;
159
160         free_block_extra(b);
161         /*
162          * drivers which perform non cache coherent DMA manage their own buffer
163          * pool of uncached buffers and provide their own free routine.
164          */
165         if (b->free) {
166                 b->free(b);
167                 return;
168         }
169         if (b->flag & BINTR) {
170                 /* subtracting the size of b */
171                 atomic_add(&ialloc_bytes, -(b->lim - b->base));
172         }
173
174         /* poison the block in case someone is still holding onto it */
175         b->next = dead;
176         b->rp = dead;
177         b->wp = dead;
178         b->lim = dead;
179         b->base = dead;
180
181         kfree(b);
182 }
183
184 void checkb(struct block *b, char *msg)
185 {
186         void *dead = (void *)Bdead;
187         struct extra_bdata *ebd;
188
189         if (b == dead)
190                 panic("checkb b %s 0x%lx", msg, b);
191         if (b->base == dead || b->lim == dead || b->next == dead
192                 || b->rp == dead || b->wp == dead) {
193                 printd("checkb: base 0x%8.8lx lim 0x%8.8lx next 0x%8.8lx\n",
194                            b->base, b->lim, b->next);
195                 printd("checkb: rp 0x%8.8lx wp 0x%8.8lx\n", b->rp, b->wp);
196                 panic("checkb dead: %s\n", msg);
197         }
198
199         if (b->base > b->lim)
200                 panic("checkb 0 %s 0x%lx 0x%lx", msg, b->base, b->lim);
201         if (b->rp < b->base)
202                 panic("checkb 1 %s 0x%lx 0x%lx", msg, b->base, b->rp);
203         if (b->wp < b->base)
204                 panic("checkb 2 %s 0x%lx 0x%lx", msg, b->base, b->wp);
205         if (b->rp > b->lim)
206                 panic("checkb 3 %s 0x%lx 0x%lx", msg, b->rp, b->lim);
207         if (b->wp > b->lim)
208                 panic("checkb 4 %s 0x%lx 0x%lx", msg, b->wp, b->lim);
209         if (b->nr_extra_bufs && !b->extra_data)
210                 panic("checkb 5 %s missing extra_data", msg);
211
212         for (int i = 0; i < b->nr_extra_bufs; i++) {
213                 ebd = &b->extra_data[i];
214                 if (ebd->base) {
215                         if (!kmalloc_refcnt((void*)ebd->base))
216                                 panic("checkb buf %d, base %p has no refcnt!\n", i, ebd->base);
217                 }
218         }
219
220 }
221
222 void iallocsummary(void)
223 {
224         printd("ialloc %lu/%lu\n", atomic_read(&ialloc_bytes), 0 /*conf.ialloc */ );
225 }
226
227 void printblock(struct block *b)
228 {
229         unsigned char *c;
230         unsigned int off, elen;
231         struct extra_bdata *e;
232
233         if (b == NULL) {
234                 printk("block is null\n");
235                 return;
236         }
237
238         printk("block of BLEN = %d, with %d header and %d data in %d extras\n",
239                BLEN(b), BHLEN(b), b->extra_len, b->nr_extra_bufs);
240
241         printk("header:\n");
242         printk("%2x:\t", 0);
243         off = 0;
244         for (c = b->rp; c < b->wp; c++) {
245                 printk("  %02x", *c & 0xff);
246                 off++;
247                 if (off % 8 == 0) {
248                         printk("\n");
249                         printk("%2x:\t", off);
250                 }
251         }
252         printk("\n");
253         elen = b->extra_len;
254         for (int i = 0; (i < b->nr_extra_bufs) && elen; i++) {
255                 e = &b->extra_data[i];
256                 if (e->len == 0)
257                         continue;
258                 elen -= e->len;
259                 printk("data %d:\n", i);
260                 printk("%2x:\t", 0);
261                 for (off = 0; off < e->len; off++) {
262                         c = (unsigned char *)e->base + e->off + off;
263                         printk("  %02x", *c & 0xff);
264                         if ((off + 1) % 8 == 0 && off +1 < e->len) {
265                                 printk("\n");
266                                 printk("%2x:\t", off + 1);
267                         }
268                 }
269         }
270         printk("\n");
271 }