18 |
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 |
02111-1307 USA. */ |
02111-1307 USA. */ |
20 |
|
|
|
/* Known limitation: No memory is returned to the system. As a |
|
|
side-effect, the destructor will never be called. */ |
|
|
|
|
21 |
#if HAVE_CONFIG_H |
#if HAVE_CONFIG_H |
22 |
#include <config.h> |
#include <config.h> |
23 |
#endif |
#endif |
29 |
#include <string.h> |
#include <string.h> |
30 |
#include <unistd.h> |
#include <unistd.h> |
31 |
#include <pthread.h> |
#include <pthread.h> |
32 |
|
#include <stdint.h> |
33 |
|
|
34 |
#include "slab.h" |
#include "slab.h" |
35 |
|
|
36 |
|
|
|
/* We do just want to initialize the allocator once. */ |
|
|
static pthread_once_t __hurd_slab_init = PTHREAD_ONCE_INIT; |
|
|
|
|
37 |
/* Number of pages the slab allocator has allocated. */ |
/* Number of pages the slab allocator has allocated. */ |
38 |
static int __hurd_slab_nr_pages; |
static int __hurd_slab_nr_pages; |
39 |
|
|
40 |
/* Internal slab used to allocate hurd_slab_space structures. */ |
/* List of created slab spaces. */ |
41 |
static hurd_slab_space_t __hurd_slab_space; |
static hurd_slab_space_t space_list; |
42 |
|
|
43 |
|
/* Protect __hurd_space_list. */ |
44 |
|
static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER; |
45 |
|
|
46 |
/* Buffer control structure. Lives at the end of an object. If the |
/* Buffer control structure. Lives at the end of an object. If the |
47 |
buffer is allocated, SLAB points to the slab to which it belongs. |
buffer is allocated, SLAB points to the slab to which it belongs. |
77 |
struct hurd_slab *slab_first; |
struct hurd_slab *slab_first; |
78 |
struct hurd_slab *slab_last; |
struct hurd_slab *slab_last; |
79 |
|
|
80 |
|
/* Link in the list of created slab spaces. */ |
81 |
|
struct hurd_slab_space *space_next; |
82 |
|
|
83 |
/* In the double linked list of slabs, empty slabs come first, |
/* In the double linked list of slabs, empty slabs come first, |
84 |
after that there is the slabs that have some buffers allocated, |
after that there is the slabs that have some buffers allocated, |
85 |
and finally the complete slabs (refcount == 0). FIRST_FREE |
and finally the complete slabs (refcount == 0). FIRST_FREE |
121 |
} |
} |
122 |
} |
} |
123 |
|
|
124 |
|
/* Remove SLAB from list of slabs in SPACE. */ |
125 |
|
static void |
126 |
|
remove_slab (struct hurd_slab_space *space, struct hurd_slab *slab) |
127 |
|
{ |
128 |
|
if (slab != space->slab_first |
129 |
|
&& slab != space->slab_last) |
130 |
|
{ |
131 |
|
slab->next->prev = slab->prev; |
132 |
|
slab->prev->next = slab->next; |
133 |
|
return; |
134 |
|
} |
135 |
|
if (slab == space->slab_first) |
136 |
|
{ |
137 |
|
space->slab_first = slab->next; |
138 |
|
if (space->slab_first) |
139 |
|
space->slab_first->prev = NULL; |
140 |
|
} |
141 |
|
if (slab == space->slab_last) |
142 |
|
{ |
143 |
|
if (slab->prev) |
144 |
|
slab->prev->next = NULL; |
145 |
|
space->slab_last = slab->prev; |
146 |
|
} |
147 |
|
} |
148 |
|
|
149 |
|
/* Iterate through slabs in SPACE and release memory for slabs that |
150 |
|
are complete (no allocated buffers). */ |
151 |
|
error_t |
152 |
|
reap (struct hurd_slab_space *space) |
153 |
|
{ |
154 |
|
struct hurd_slab *s, *next, *new_first; |
155 |
|
error_t err = 0; |
156 |
|
|
157 |
|
pthread_mutex_lock (&space->lock); |
158 |
|
|
159 |
|
for (s = space->slab_first; s; s = next) |
160 |
|
{ |
161 |
|
next = s->next; |
162 |
|
|
163 |
|
/* If the reference counter is zero there is no allocated |
164 |
|
buffers, so it can be freed. */ |
165 |
|
if (!s->refcount) |
166 |
|
{ |
167 |
|
remove_slab (space, s); |
168 |
|
|
169 |
|
/* If there is a destructor it must be invoked for every |
170 |
|
buffer in the slab. */ |
171 |
|
if (space->destructor) |
172 |
|
{ |
173 |
|
union hurd_bufctl *bufctl; |
174 |
|
for (bufctl = s->free_list; bufctl; bufctl = bufctl->next) |
175 |
|
{ |
176 |
|
void *buffer = (((void *) bufctl) |
177 |
|
- (space->size - sizeof *bufctl)); |
178 |
|
(*space->destructor) (buffer); |
179 |
|
} |
180 |
|
} |
181 |
|
/* The slab is located at the end of the page (with the buffers |
182 |
|
in front of it), get address by masking with page size. |
183 |
|
This frees the slab and all its buffers, since they live on |
184 |
|
the same page. */ |
185 |
|
err = munmap |
186 |
|
((void *) (((uintptr_t) s) & ~(getpagesize () - 1)), |
187 |
|
getpagesize ()); |
188 |
|
if (err) |
189 |
|
break; |
190 |
|
__hurd_slab_nr_pages--; |
191 |
|
} |
192 |
|
} |
193 |
|
|
194 |
|
/* Even in the case of an error, first_free must be updated since |
195 |
|
that slab may have been deallocated. */ |
196 |
|
new_first = space->slab_first; |
197 |
|
while (new_first) |
198 |
|
{ |
199 |
|
if (new_first->refcount != space->full_refcount) |
200 |
|
break; |
201 |
|
new_first = new_first->next; |
202 |
|
} |
203 |
|
space->first_free = new_first; |
204 |
|
|
205 |
|
pthread_mutex_unlock (&space->lock); |
206 |
|
return err; |
207 |
|
} |
208 |
|
|
209 |
|
/* Release unused memory. */ |
210 |
|
error_t |
211 |
|
hurd_slab_reap (void) |
212 |
|
{ |
213 |
|
struct hurd_slab_space *space; |
214 |
|
error_t err = 0; |
215 |
|
|
216 |
|
pthread_mutex_lock (&list_lock); |
217 |
|
for (space = space_list; space; space = space->space_next) |
218 |
|
{ |
219 |
|
if ((err = reap (space))) |
220 |
|
break; |
221 |
|
} |
222 |
|
pthread_mutex_unlock (&list_lock); |
223 |
|
return err; |
224 |
|
} |
225 |
|
|
226 |
/* SPACE has no more memory. Allocate new slab and insert it into the |
/* SPACE has no more memory. Allocate new slab and insert it into the |
227 |
list, repoint free_list and return possible error. */ |
list, repoint free_list and return possible error. */ |
271 |
return 0; |
return 0; |
272 |
} |
} |
273 |
|
|
274 |
|
/* Insert SPACE into list of created slab spaces. */ |
|
/* Initialize the internal slab space used for allocating other slab |
|
|
spaces. The usual chicken and the egg problem. */ |
|
275 |
static void |
static void |
276 |
init_allocator (void) |
insert_space (struct hurd_slab_space *space) |
277 |
{ |
{ |
278 |
__hurd_slab_space = malloc (sizeof (struct hurd_slab_space)); |
pthread_mutex_lock (&list_lock); |
279 |
__hurd_slab_space->size = (sizeof (struct hurd_slab_space) |
if (!space_list) |
280 |
+ sizeof (union hurd_bufctl)); |
space_list = space; |
281 |
__hurd_slab_space->full_refcount = ((getpagesize () |
else |
282 |
- sizeof (struct hurd_slab)) |
{ |
283 |
/ __hurd_slab_space->size); |
space->space_next = space_list; |
284 |
pthread_mutex_init (&__hurd_slab_space->lock, NULL); |
space_list = space; |
285 |
|
} |
286 |
|
pthread_mutex_unlock (&list_lock); |
287 |
} |
} |
288 |
|
|
289 |
|
|
303 |
- sizeof (union hurd_bufctl))) |
- sizeof (union hurd_bufctl))) |
304 |
return EINVAL; |
return EINVAL; |
305 |
|
|
306 |
pthread_once (&__hurd_slab_init, init_allocator); |
*space = malloc (sizeof (struct hurd_slab_space)); |
307 |
|
if (!*space) |
308 |
if ((err = hurd_slab_alloc (__hurd_slab_space, (void **) space))) |
return ENOMEM; |
|
return err; |
|
309 |
memset (*space, 0, sizeof (struct hurd_slab_space)); |
memset (*space, 0, sizeof (struct hurd_slab_space)); |
310 |
|
|
311 |
err = pthread_mutex_init (&(*space)->lock, NULL); |
err = pthread_mutex_init (&(*space)->lock, NULL); |
312 |
if (err) |
if (err) |
313 |
{ |
{ |
314 |
hurd_slab_dealloc (__hurd_slab_space, *space); |
free (*space); |
315 |
return err; |
return err; |
316 |
} |
} |
317 |
|
|
330 |
/* Calculate the number of objects that fit in a slab. */ |
/* Calculate the number of objects that fit in a slab. */ |
331 |
(*space)->full_refcount |
(*space)->full_refcount |
332 |
= ((getpagesize () - sizeof (struct hurd_slab)) / (*space)->size); |
= ((getpagesize () - sizeof (struct hurd_slab)) / (*space)->size); |
333 |
|
|
334 |
|
insert_space (*space); |
335 |
return 0; |
return 0; |
336 |
} |
} |
337 |
|
|