]> jfr.im git - irc/rqf/shadowircd.git/blob - libratbox/src/balloc.c
sync libratbox - r25599 + charybdis packaging patch
[irc/rqf/shadowircd.git] / libratbox / src / balloc.c
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * balloc.c: A block allocator.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2006 ircd-ratbox development team
8 *
9 * Below are the orignal headers from the old blalloc.c
10 *
11 * File: blalloc.c
12 * Owner: Wohali (Joan Touzet)
13 *
14 * Modified 2001/11/29 for mmap() support by Aaron Sethman <androsyn@ratbox.org>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
29 * USA
30 *
31 * $Id: balloc.c 25375 2008-05-16 15:19:51Z androsyn $
32 */
33
34 /*
35 * About the block allocator
36 *
37 * Basically we have three ways of getting memory off of the operating
38 * system. Below are this list of methods and the order of preference.
39 *
40 * 1. mmap() anonymous pages with the MMAP_ANON flag.
41 * 2. mmap() via the /dev/zero trick.
42 * 3. HeapCreate/HeapAlloc (on win32)
43 * 4. malloc()
44 *
45 * The advantages of 1 and 2 are this. We can munmap() the pages which will
46 * return the pages back to the operating system, thus reducing the size
47 * of the process as the memory is unused. malloc() on many systems just keeps
48 * a heap of memory to itself, which never gets given back to the OS, except on
49 * exit. This of course is bad, if say we have an event that causes us to allocate
50 * say, 200MB of memory, while our normal memory consumption would be 15MB. In the
51 * malloc() case, the amount of memory allocated to our process never goes down, as
52 * malloc() has it locked up in its heap. With the mmap() method, we can munmap()
53 * the block and return it back to the OS, thus causing our memory consumption to go
54 * down after we no longer need it.
55 *
56 *
57 *
58 */
59 #include <libratbox_config.h>
60 #include <ratbox_lib.h>
61
62 #ifdef HAVE_MMAP /* We've got mmap() that is good */
63 #include <sys/mman.h>
64 /* HP-UX sucks */
65 #ifdef MAP_ANONYMOUS
66 #ifndef MAP_ANON
67 #define MAP_ANON MAP_ANONYMOUS
68 #endif
69 #endif
70 #endif
71
72 /* status information for an allocated block in heap */
73 struct rb_heap_block
74 {
75 size_t alloc_size;
76 rb_dlink_node node;
77 unsigned long free_count;
78 void *elems; /* Points to allocated memory */
79 };
80 typedef struct rb_heap_block rb_heap_block;
81
82 struct rb_heap_memblock
83 {
84 rb_heap_block *block;
85 union {
86 rb_dlink_node node;
87 char data[1]; /* stub pointer..this is ugly */
88 } ndata;
89 };
90
91 typedef struct rb_heap_memblock rb_heap_memblock;
92
93 /* information for the root node of the heap */
94 struct rb_bh
95 {
96 rb_dlink_node hlist;
97 size_t elemSize; /* Size of each element to be stored */
98 unsigned long elemsPerBlock; /* Number of elements per block */
99 rb_dlink_list block_list;
100 rb_dlink_list free_list;
101 char *desc;
102 };
103
104 #ifndef NOBALLOC
105 static int newblock(rb_bh * bh);
106 static void rb_bh_gc_event(void *unused);
107 #endif /* !NOBALLOC */
108 static rb_dlink_list *heap_lists;
109
110 #if defined(WIN32)
111 static HANDLE block_heap;
112 #endif
113
114 #define rb_bh_fail(x) _rb_bh_fail(x, __FILE__, __LINE__)
115
116 static void
117 _rb_bh_fail(const char *reason, const char *file, int line)
118 {
119 rb_lib_log("rb_heap_blockheap failure: %s (%s:%d)", reason, file, line);
120 abort();
121 }
122
123 #ifndef NOBALLOC
124 /*
125 * static inline void free_block(void *ptr, size_t size)
126 *
127 * Inputs: The block and its size
128 * Output: None
129 * Side Effects: Returns memory for the block back to the OS
130 */
131 static inline void
132 free_block(void *ptr, size_t size)
133 {
134 #ifdef HAVE_MMAP
135 munmap(ptr, size);
136 #else
137 #ifdef WIN32
138 HeapFree(block_heap, 0, ptr);
139 #else
140 free(ptr);
141 #endif
142 #endif
143 }
144 #endif /* !NOBALLOC */
145
146 /*
147 * void rb_init_bh(void)
148 *
149 * Inputs: None
150 * Outputs: None
151 * Side Effects: Initializes the block heap
152 */
153
154 void
155 rb_init_bh(void)
156 {
157 heap_lists = rb_malloc(sizeof(rb_dlink_list));
158 #ifndef NOBALLOC
159 #ifdef WIN32
160 block_heap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
161 #endif
162 rb_event_addish("rb_bh_gc_event", rb_bh_gc_event, NULL, 300);
163 #endif /* !NOBALLOC */
164 }
165
166 #ifndef NOBALLOC
167 /*
168 * static inline void *get_block(size_t size)
169 *
170 * Input: Size of block to allocate
171 * Output: Pointer to new block
172 * Side Effects: None
173 */
174 static inline void *
175 get_block(size_t size)
176 {
177 void *ptr;
178 #ifdef HAVE_MMAP
179 #ifdef MAP_ANON
180 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
181 #else
182 int zero_fd;
183 zero_fd = open("/dev/zero", O_RDWR);
184 if(zero_fd < 0)
185 rb_bh_fail("Failed opening /dev/zero");
186 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
187 close(zero_fd);
188 #endif /* MAP_ANON */
189 if(ptr == MAP_FAILED)
190 ptr = NULL;
191 #else
192 #ifdef WIN32
193 ptr = HeapAlloc(block_heap, 0, size);
194 #else
195 ptr = malloc(size);
196 #endif
197 #endif
198 return(ptr);
199 }
200
201
202 static void
203 rb_bh_gc_event(void *unused)
204 {
205 rb_dlink_node *ptr;
206 RB_DLINK_FOREACH(ptr, heap_lists->head)
207 {
208 rb_bh_gc(ptr->data);
209 }
210 }
211
212 /* ************************************************************************ */
213 /* FUNCTION DOCUMENTATION: */
214 /* newblock */
215 /* Description: */
216 /* Allocates a new block for addition to a blockheap */
217 /* Parameters: */
218 /* bh (IN): Pointer to parent blockheap. */
219 /* Returns: */
220 /* 0 if successful, 1 if not */
221 /* ************************************************************************ */
222
223 static int
224 newblock(rb_bh * bh)
225 {
226 rb_heap_block *b;
227 unsigned long i;
228 rb_uintptr_t offset;
229
230 /* Setup the initial data structure. */
231 b = rb_malloc(sizeof(rb_heap_block));
232
233 b->alloc_size = bh->elemsPerBlock * (bh->elemSize + sizeof(rb_heap_block *));
234
235 b->elems = get_block(b->alloc_size);
236 if(rb_unlikely(b->elems == NULL))
237 {
238 return (1);
239 }
240 offset = (rb_uintptr_t)b->elems;
241 /* Setup our blocks now */
242 for (i = 0; i < bh->elemsPerBlock; i++, offset += (bh->elemSize + sizeof(rb_heap_block *)))
243 {
244 rb_heap_memblock *memblock = (rb_heap_memblock *)offset;
245 memblock->block = b;
246 rb_dlinkAdd(memblock, &memblock->ndata.node, &bh->free_list);
247 }
248 rb_dlinkAdd(b, &b->node, &bh->block_list);
249 b->free_count = bh->elemsPerBlock;
250 return (0);
251 }
252 #endif /* !NOBALLOC */
253
254 /* ************************************************************************ */
255 /* FUNCTION DOCUMENTATION: */
256 /* rb_bh_create */
257 /* Description: */
258 /* Creates a new blockheap from which smaller blocks can be allocated. */
259 /* Intended to be used instead of multiple calls to malloc() when */
260 /* performance is an issue. */
261 /* Parameters: */
262 /* elemsize (IN): Size of the basic element to be stored */
263 /* elemsperblock (IN): Number of elements to be stored in a single block */
264 /* of memory. When the blockheap runs out of free memory, it will */
265 /* allocate elemsize * elemsperblock more. */
266 /* Returns: */
267 /* Pointer to new rb_bh, or NULL if unsuccessful */
268 /* ************************************************************************ */
269 rb_bh *
270 rb_bh_create(size_t elemsize, int elemsperblock, const char *desc)
271 {
272 rb_bh *bh;
273 lrb_assert(elemsize > 0 && elemsperblock > 0);
274 lrb_assert(elemsize >= sizeof(rb_dlink_node));
275 /* Catch idiotic requests up front */
276 if((elemsize == 0) || (elemsperblock <= 0))
277 {
278 rb_bh_fail("Attempting to rb_bh_create idiotic sizes");
279 }
280
281 if(elemsize < sizeof(rb_dlink_node))
282 rb_bh_fail("Attempt to rb_bh_create smaller than sizeof(rb_dlink_node)");
283
284 /* Allocate our new rb_bh */
285 bh = rb_malloc(sizeof(rb_bh));
286
287 #ifndef NOBALLOC
288 if((elemsize % sizeof(void *)) != 0)
289 {
290 /* Pad to even pointer boundary */
291 elemsize += sizeof(void *);
292 elemsize &= ~(sizeof(void *) - 1);
293 }
294 #endif /* !NOBALLOC */
295
296 bh->elemSize = elemsize;
297 bh->elemsPerBlock = elemsperblock;
298 if(desc != NULL)
299 bh->desc = rb_strdup(desc);
300
301 #ifndef NOBALLOC
302 /* Be sure our malloc was successful */
303 if(newblock(bh))
304 {
305 if(bh != NULL)
306 free(bh);
307 rb_lib_log("newblock() failed");
308 rb_outofmemory(); /* die.. out of memory */
309 }
310 #endif /* !NOBALLOC */
311
312 if(bh == NULL)
313 {
314 rb_bh_fail("bh == NULL when it shouldn't be");
315 }
316 rb_dlinkAdd(bh, &bh->hlist, heap_lists);
317 return (bh);
318 }
319
320 /* ************************************************************************ */
321 /* FUNCTION DOCUMENTATION: */
322 /* rb_bh_alloc */
323 /* Description: */
324 /* Returns a pointer to a struct within our rb_bh that's free for */
325 /* the taking. */
326 /* Parameters: */
327 /* bh (IN): Pointer to the Blockheap. */
328 /* Returns: */
329 /* Pointer to a structure (void *), or NULL if unsuccessful. */
330 /* ************************************************************************ */
331
332 void *
333 rb_bh_alloc(rb_bh * bh)
334 {
335 #ifndef NOBALLOC
336 rb_dlink_node *new_node;
337 rb_heap_memblock *memblock;
338 #endif
339 lrb_assert(bh != NULL);
340 if(rb_unlikely(bh == NULL))
341 {
342 rb_bh_fail("Cannot allocate if bh == NULL");
343 }
344
345 #ifdef NOBALLOC
346 return(rb_malloc(bh->elemSize));
347 #else
348 if(bh->free_list.head == NULL)
349 {
350 /* Allocate new block and assign */
351 /* newblock returns 1 if unsuccessful, 0 if not */
352
353 if(rb_unlikely(newblock(bh)))
354 {
355 rb_lib_log("newblock() failed");
356 rb_outofmemory(); /* Well that didn't work either...bail */
357 }
358 if(bh->free_list.head == NULL)
359 {
360 rb_lib_log("out of memory after newblock()...");
361 rb_outofmemory();
362 }
363 }
364
365 new_node = bh->free_list.head;
366 memblock = new_node->data;
367 rb_dlinkDelete(new_node, &bh->free_list);
368 memblock->block->free_count--;
369 memset((void *)memblock->ndata.data, 0, bh->elemSize);
370 return((void *)memblock->ndata.data);
371 #endif
372 }
373
374
375 /* ************************************************************************ */
376 /* FUNCTION DOCUMENTATION: */
377 /* rb_bh_free */
378 /* Description: */
379 /* Returns an element to the free pool, does not free() */
380 /* Parameters: */
381 /* bh (IN): Pointer to rb_bh containing element */
382 /* ptr (in): Pointer to element to be "freed" */
383 /* Returns: */
384 /* 0 if successful, 1 if element not contained within rb_bh. */
385 /* ************************************************************************ */
386 int
387 rb_bh_free(rb_bh * bh, void *ptr)
388 {
389 #ifndef NOBALLOC
390 rb_heap_memblock *memblock;
391 #endif
392 lrb_assert(bh != NULL);
393 lrb_assert(ptr != NULL);
394
395 if(rb_unlikely(bh == NULL))
396 {
397 rb_lib_log("balloc.c:rb_bhFree() bh == NULL");
398 return (1);
399 }
400
401 if(rb_unlikely(ptr == NULL))
402 {
403 rb_lib_log("balloc.rb_bhFree() ptr == NULL");
404 return (1);
405 }
406
407 #ifdef NOBALLOC
408 rb_free(ptr);
409 #else
410 memblock = (rb_heap_memblock *) ((uintptr_t)ptr - sizeof(rb_heap_block *));
411 /* XXX */
412 if(rb_unlikely(!((uintptr_t)ptr >= (uintptr_t)memblock->block->elems && (uintptr_t)ptr < (uintptr_t)memblock->block->elems + (uintptr_t)memblock->block->alloc_size)))
413 {
414 rb_bh_fail("rb_bh_free() bogus pointer");
415 }
416 memblock->block->free_count++;
417 rb_dlinkAdd(memblock, &memblock->ndata.node, &bh->free_list);
418 #endif /* !NOBALLOC */
419 return (0);
420 }
421
422
423 /* ************************************************************************ */
424 /* FUNCTION DOCUMENTATION: */
425 /* rb_bhDestroy */
426 /* Description: */
427 /* Completely free()s a rb_bh. Use for cleanup. */
428 /* Parameters: */
429 /* bh (IN): Pointer to the rb_bh to be destroyed. */
430 /* Returns: */
431 /* 0 if successful, 1 if bh == NULL */
432 /* ************************************************************************ */
433 int
434 rb_bh_destroy(rb_bh * bh)
435 {
436 #ifndef NOBALLOC
437 rb_dlink_node *ptr, *next;
438 rb_heap_block *b;
439 #endif
440 if(bh == NULL)
441 return (1);
442
443 #ifndef NOBALLOC
444 RB_DLINK_FOREACH_SAFE(ptr, next, bh->block_list.head)
445 {
446 b = ptr->data;
447 free_block(b->elems, b->alloc_size);
448 rb_free(b);
449 }
450 #endif /* !NOBALLOC */
451
452 rb_dlinkDelete(&bh->hlist, heap_lists);
453 rb_free(bh->desc);
454 rb_free(bh);
455
456 return (0);
457 }
458
459 void
460 rb_bh_usage(rb_bh * bh, size_t * bused, size_t * bfree, size_t * bmemusage, const char **desc)
461 {
462 size_t used, freem, memusage;
463
464 if(bh == NULL)
465 {
466 return;
467 }
468
469 freem = rb_dlink_list_length(&bh->free_list);
470 used = (rb_dlink_list_length(&bh->block_list) * bh->elemsPerBlock) - freem;
471 memusage = used * (bh->elemSize + sizeof(void *));
472 if(bused != NULL)
473 *bused = used;
474 if(bfree != NULL)
475 *bfree = freem;
476 if(bmemusage != NULL)
477 *bmemusage = memusage;
478 if(desc != NULL)
479 *desc = bh->desc;
480 }
481
482 void rb_bh_usage_all(rb_bh_usage_cb *cb, void *data)
483 {
484 rb_dlink_node *ptr;
485 rb_bh *bh;
486 size_t used, freem, memusage, heapalloc;
487 static const char *unnamed = "(unnamed_heap)";
488 const char *desc = unnamed;
489
490 if(cb == NULL)
491 return;
492
493 RB_DLINK_FOREACH(ptr, heap_lists->head)
494 {
495 bh = (rb_bh *)ptr->data;
496 freem = rb_dlink_list_length(&bh->free_list);
497 used = (rb_dlink_list_length(&bh->block_list) * bh->elemsPerBlock) - freem;
498 memusage = used * (bh->elemSize + sizeof(void *));
499 heapalloc = (freem + used) * (bh->elemSize + sizeof(void *));
500 if(bh->desc != NULL)
501 desc = bh->desc;
502 cb(used, freem, memusage, heapalloc, desc, data);
503 }
504 return;
505 }
506
507 void
508 rb_bh_total_usage(size_t *total_alloc, size_t *total_used)
509 {
510 rb_dlink_node *ptr;
511 size_t total_memory = 0, used_memory = 0, used, freem;
512 rb_bh *bh;
513
514 RB_DLINK_FOREACH(ptr, heap_lists->head)
515 {
516 bh = (rb_bh *)ptr->data;
517 freem = rb_dlink_list_length(&bh->free_list);
518 used = (rb_dlink_list_length(&bh->block_list) * bh->elemsPerBlock) - freem;
519 used_memory += used * (bh->elemSize + sizeof(void *));
520 total_memory += (freem + used) * (bh->elemSize + sizeof(void *));
521 }
522
523 if(total_alloc != NULL)
524 *total_alloc = total_memory;
525 if(total_used != NULL)
526 *total_used = used_memory;
527 }
528
529 #ifndef NOBALLOC
530 int
531 rb_bh_gc(rb_bh * bh)
532 {
533 rb_heap_block *b;
534 rb_dlink_node *ptr, *next;
535 unsigned long i;
536 uintptr_t offset;
537
538 if(bh == NULL)
539 {
540 /* somebody is smoking some craq..(probably lee, but don't tell him that) */
541 return (1);
542 }
543
544 if((rb_dlink_list_length(&bh->free_list) < bh->elemsPerBlock) || rb_dlink_list_length(&bh->block_list) == 1)
545 {
546 /* There couldn't possibly be an entire free block. Return. */
547 return (0);
548 }
549
550 RB_DLINK_FOREACH_SAFE(ptr, next, bh->block_list.head)
551 {
552 b = ptr->data;
553 if(rb_dlink_list_length(&bh->block_list) == 1)
554 return (0);
555
556 if(b->free_count == bh->elemsPerBlock)
557 {
558 /* i'm seriously going to hell for this.. */
559
560 offset = (uintptr_t)b->elems;
561 for (i = 0; i < bh->elemsPerBlock; i++, offset += ((uintptr_t)bh->elemSize + sizeof(rb_heap_memblock *)))
562 {
563 rb_heap_memblock *memblock = (rb_heap_memblock *)offset;
564 rb_dlinkDelete(&memblock->ndata.node, &bh->free_list);
565 }
566 rb_dlinkDelete(&b->node, &bh->block_list);
567 free_block(b->elems, b->alloc_size);
568 rb_free(b);
569 }
570
571 }
572 return (0);
573 }
574 #endif /* !NOBALLOC */