1 /**
2 * \file l4util/lib/src/list_alloc.c
3 * \brief Simple list-based allocator. Taken from the Fiasco kernel.
4 *
5 * \author Alexander Warg <aw11@os.inf.tu-dresden.de>
6 * Frank Mehnert <fm3@os.inf.tu-dresden.de>
7 */
8 /*
9 * (c) 2003-2009 Author(s)
10 * economic rights: Technische Universität Dresden (Germany)
11 * This file is part of TUD:OS and distributed under the terms of the
12 * GNU Lesser General Public License 2.1.
13 * Please see the COPYING-LGPL-2.1 file for details.
14 */
15
16 #include <l4/sys/l4int.h>
17 #include <l4/sys/kdebug.h>
18 #include <l4/util/list_alloc.h>
19
20 #include <stdio.h>
21 #include <assert.h>
22
23 #define ALIGN_MASK (sizeof(l4la_free_t) - 1)
24
25 #define DEBUG
26 #ifdef DEBUG
27
28 static void
__check_overlap(l4la_free_t ** first,void * b,l4_size_t s)29 __check_overlap(l4la_free_t **first, void *b, l4_size_t s)
30 {
31 l4la_free_t *c = *first;
32 for (; c; c = c->next)
33 {
34 l4_addr_t x_s = (l4_addr_t)b;
35 l4_addr_t x_e = x_s + s;
36 l4_addr_t b_s = (l4_addr_t)c;
37 l4_addr_t b_e = b_s + c->size;
38
39 if ( (x_s >= b_s && x_s < b_e)
40 || (x_e > b_s && x_e <= b_e)
41 || (b_s >= x_s && b_s < x_e)
42 || (b_e > x_s && b_e <= x_e))
43 {
44 printf("trying to free memory that is already free: \n"
45 " [%lx-%lx) overlaps [%lx-%lx)\n",
46 x_s, x_e, b_s, b_e );
47 enter_kdebug("l4la");
48 }
49 }
50 }
51
52 static void
__sanity_check_list(l4la_free_t ** first,char const * func,char const * info)53 __sanity_check_list(l4la_free_t **first, char const *func, char const *info)
54 {
55 l4la_free_t *c = *first;
56 for (;c ; c = c->next)
57 {
58 if (c->next)
59 {
60 if (c >= c->next)
61 {
62 printf("%s: %s(%s): list order violation\n",
63 __FILE__, func, info);
64 enter_kdebug("l4la");
65 }
66
67 if (((l4_addr_t)c) + c->size > (l4_addr_t)c->next)
68 {
69 printf("%s: %s(%s): overlapping blocks\n",
70 __FILE__, func, info);
71 enter_kdebug("l4la");
72 }
73 }
74 }
75 }
76
77 #else
78
79 static inline void
__check_overlap(l4la_free_t ** first,void * b,l4_size_t s)80 __check_overlap(l4la_free_t **first, void *b, l4_size_t s) {};
81
82 static inline void
__sanity_check_list(l4la_free_t ** first,char const * f,char const * i)83 __sanity_check_list(l4la_free_t **first, char const *f, char const *i) {};
84
85 #endif
86
87 static void
__merge(l4la_free_t ** first)88 __merge(l4la_free_t **first)
89 {
90 __sanity_check_list(first, __FUNCTION__, "entry");
91
92 l4la_free_t *c = *first;
93 while (c && c->next)
94 {
95 l4_addr_t f_start = (l4_addr_t)c;
96 l4_addr_t f_end = f_start + c->size;
97 l4_addr_t n_start = (l4_addr_t)c->next;
98
99 if (f_end == n_start)
100 {
101 c->size += c->next->size;
102 assert(c->size >= sizeof(l4la_free_t));
103 c->next = c->next->next;
104 continue;
105 }
106
107 c = c->next;
108 }
109
110 __sanity_check_list(first, __FUNCTION__, "exit");
111 }
112
113 L4_CV void
l4la_free(l4la_free_t ** first,void * block,l4_size_t size)114 l4la_free(l4la_free_t **first, void *block, l4_size_t size)
115 {
116 l4la_free_t **c = first;
117 l4la_free_t *next = 0;
118
119 __sanity_check_list(first, __FUNCTION__, "entry");
120 __check_overlap(first, block, size);
121
122 size = (size + ALIGN_MASK) & ~ALIGN_MASK;
123
124 if (*c)
125 {
126 while (*c && *c < (l4la_free_t*)block)
127 c = &(*c)->next;
128
129 next = *c;
130 }
131
132 assert(*c != block);
133 *c = (l4la_free_t*)block;
134 assert(*c != next);
135
136 (*c)->next = next;
137 (*c)->size = size;
138
139 assert((!next) || ((l4_addr_t)(*c) + size <= (l4_addr_t)next));
140
141 __merge(first);
142
143 __sanity_check_list(first, __FUNCTION__, "exit");
144 }
145
146 L4_CV void *
l4la_alloc(l4la_free_t ** first,l4_size_t size,unsigned align)147 l4la_alloc(l4la_free_t **first, l4_size_t size, unsigned align)
148 {
149 void *ret = 0;
150 l4_addr_t almask = (1UL << align) - 1;
151 l4la_free_t **c = first;
152 void *b = 0;
153
154 __sanity_check_list(first, __FUNCTION__, "entry");
155
156 size = (size + ALIGN_MASK) & ~ALIGN_MASK;
157
158 for (; *c; c=&(*c)->next)
159 {
160 l4_addr_t n_start = (l4_addr_t)(*c);
161 l4_addr_t a_start;
162 l4_addr_t a_size;
163
164 if ((*c)->size < size)
165 continue;
166
167 if (!(n_start & almask))
168 {
169 if ((*c)->size >= size)
170 {
171 if ((*c)->size == size)
172 {
173 b = *c;
174 *c = (*c)->next;
175 ret = b;
176 goto done;
177 }
178
179 l4la_free_t *m = (l4la_free_t*)(n_start + size);
180 m->next = (*c)->next;
181 m->size = (*c)->size - size;
182 assert(m->size >= sizeof(l4la_free_t));
183 b = *c;
184 *c = m;
185 ret = b;
186 goto done;
187 }
188
189 continue;
190 }
191
192 a_start = (n_start & ~almask) + 1 + almask;
193 if (a_start - n_start >= (*c)->size)
194 continue;
195
196 a_size = (*c)->size - a_start + n_start;
197
198 if (a_size >= size)
199 {
200 if (a_size == size)
201 {
202 (*c)->size -= a_size;
203 ret = (void*)a_start;
204 goto done;
205 }
206 l4la_free_t *m = (l4la_free_t*)(a_start + size);
207 m->next = (*c)->next;
208 m->size = a_size - size;
209 assert(m->size >= sizeof(l4la_free_t));
210 (*c)->size -= a_size;
211 assert((*c)->size >= sizeof(l4la_free_t));
212 (*c)->next = m;
213 ret = (void*)a_start;
214 goto done;
215 }
216 }
217
218 done:
219 return ret;
220 }
221
222 L4_CV l4_size_t
l4la_avail(l4la_free_t ** first)223 l4la_avail(l4la_free_t **first)
224 {
225 __sanity_check_list(first, __FUNCTION__, "entry");
226
227 l4la_free_t *c = *first;
228 l4_addr_t a = 0;
229 while (c)
230 {
231 a += c->size;
232 c = c->next;
233 }
234
235 return a;
236 }
237
238 L4_CV void
l4la_dump(l4la_free_t ** first)239 l4la_dump(l4la_free_t **first)
240 {
241 printf("List_alloc [first=%p]\n", *first);
242 l4la_free_t *c = *first;
243 for (;c && c!=c->next ; c = c->next)
244 printf(" mem_block_t [this=%p size=0x%lx (%ldkB) next=%p]\n", c,
245 (l4_addr_t)c->size,
246 (l4_addr_t)(c->size+1023)/1024, c->next);
247
248 if (c && c == c->next)
249 printf(" BUG: loop detected\n");
250 }
251
252 L4_CV void
l4la_init(l4la_free_t ** first)253 l4la_init(l4la_free_t **first)
254 {
255 *first = 0;
256 }
257