]> jfr.im git - irc/rqf/shadowircd.git/blame - libratbox/src/balloc.c
sync libratbox - r25599 + charybdis packaging patch
[irc/rqf/shadowircd.git] / libratbox / src / balloc.c
CommitLineData
b57f37fb
WP
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 *
033be687 31 * $Id: balloc.c 25375 2008-05-16 15:19:51Z androsyn $
b57f37fb
WP
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 */
73struct 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};
80typedef struct rb_heap_block rb_heap_block;
81
82struct 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
91typedef struct rb_heap_memblock rb_heap_memblock;
92
93/* information for the root node of the heap */
94struct 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
105static int newblock(rb_bh * bh);
106static void rb_bh_gc_event(void *unused);
107#endif /* !NOBALLOC */
108static rb_dlink_list *heap_lists;
109
110#if defined(WIN32)
111static HANDLE block_heap;
112#endif
113
114#define rb_bh_fail(x) _rb_bh_fail(x, __FILE__, __LINE__)
115
116static 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 */
131static inline void
132free_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
154void
155rb_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 */
174static inline void *
175get_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
202static void
203rb_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
223static int
224newblock(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);
033be687 236 if(rb_unlikely(b->elems == NULL))
b57f37fb
WP
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/* ************************************************************************ */
269rb_bh *
270rb_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 */
033be687 276 if((elemsize == 0) || (elemsperblock <= 0))
b57f37fb
WP
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
332void *
333rb_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);
033be687 340 if(rb_unlikely(bh == NULL))
b57f37fb
WP
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
033be687 353 if(rb_unlikely(newblock(bh)))
b57f37fb
WP
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/* ************************************************************************ */
386int
387rb_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
033be687 395 if(rb_unlikely(bh == NULL))
b57f37fb 396 {
033be687 397 rb_lib_log("balloc.c:rb_bhFree() bh == NULL");
b57f37fb
WP
398 return (1);
399 }
400
033be687 401 if(rb_unlikely(ptr == NULL))
b57f37fb 402 {
033be687 403 rb_lib_log("balloc.rb_bhFree() ptr == NULL");
b57f37fb
WP
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 */
033be687 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)))
b57f37fb
WP
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/* ************************************************************************ */
433int
434rb_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
459void
460rb_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
482void 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
507void
508rb_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
530int
531rb_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 */