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