1 /* {{{ irc-seven: Cows like it.
3 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
4 * Copyright (C) 1996-2002 Hybrid Development Team
5 * Copyright (C) 2002-2005 ircd-ratbox development team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin St - Fifth Floor
22 * Boston, MA 02110-1301
27 /* {{{ ircd-seven block allocator (balloc)
29 * The block allocator, funnily enough, allocates blocks of memory of variable
30 * size from the operating system. There are two ways we can do this, and they
33 * 1) by mapping in an anonymous page of memory with mmap(), or
34 * 2) increasing the size of the data segment thereby allocating memory for
35 * use with sbrk() [actually done with malloc() and friends].
37 * The advantage of using mmap()-based allocation is that, unlike the sbrk()
38 * method, allocated pages can be returned to the operating system (freed),
39 * whereas memory allocated from the data segment cannot be (because the data
40 * segment cannot be shrunk).
47 #define WE_ARE_MEMORY_C
52 #include "ircd_defs.h" /* DEBUG_BLOCK_ALLOCATOR */
55 #include "irc_string.h"
61 #ifdef HAVE_MMAP /* We've got mmap() that is good */
66 #define MAP_ANON MAP_ANONYMOUS
71 static int newblock(BlockHeap
* bh
);
72 static int BlockHeapGarbageCollect(BlockHeap
*);
73 static void block_heap_gc(void *unused
);
74 static dlink_list heap_lists
;
76 #if defined(HAVE_MMAP) && !defined(MAP_ANON)
77 static int zero_fd
= -1;
80 #define blockheap_fail(x) _blockheap_fail(x, __FILE__, __LINE__)
83 _blockheap_fail(const char *reason
, const char *file
, int line
)
85 libseven_log("Blockheap failure: %s (%s:%d)", reason
, file
, line
);
91 * static inline void free_block(void *ptr, size_t size)
93 * Inputs: The block and its size
95 * Side Effects: Returns memory for the block back to the OS
98 free_block(void *ptr
, size_t size
)
108 /* Check the list length the very slow way */
110 slow_list_length(dlink_list
*list
)
113 unsigned long count
= 0;
115 for (ptr
= list
->head
; ptr
; ptr
= ptr
->next
)
118 if(count
> list
->length
* 2)
120 blockheap_fail("count > list->length * 2 - I give up");
127 bh_sanity_check_block(BlockHeap
*bh
, Block
*block
)
129 unsigned long s_used
, s_free
;
130 s_used
= slow_list_length(&block
->used_list
);
131 s_free
= slow_list_length(&block
->free_list
);
132 if(s_used
!= dlink_list_length(&block
->used_list
))
133 blockheap_fail("used link count doesn't match head count");
134 if(s_free
!= dlink_list_length(&block
->free_list
))
135 blockheap_fail("free link count doesn't match head count");
137 if(dlink_list_length(&block
->used_list
) + dlink_list_length(&block
->free_list
) != bh
->elemsPerBlock
)
138 blockheap_fail("used_list + free_list != elemsPerBlock");
142 /* See how confused we are */
144 bh_sanity_check(BlockHeap
*bh
)
147 unsigned long real_alloc
= 0;
148 unsigned long s_used
, s_free
;
149 unsigned long blockcount
= 0;
150 unsigned long allocated
;
152 blockheap_fail("Trying to sanity check a NULL block");
154 allocated
= bh
->blocksAllocated
* bh
->elemsPerBlock
;
156 for(walker
= bh
->base
; walker
!= NULL
; walker
= walker
->next
)
159 s_used
= slow_list_length(&walker
->used_list
);
160 s_free
= slow_list_length(&walker
->free_list
);
162 if(s_used
!= dlink_list_length(&walker
->used_list
))
163 blockheap_fail("used link count doesn't match head count");
164 if(s_free
!= dlink_list_length(&walker
->free_list
))
165 blockheap_fail("free link count doesn't match head count");
167 if(dlink_list_length(&walker
->used_list
) + dlink_list_length(&walker
->free_list
) != bh
->elemsPerBlock
)
168 blockheap_fail("used_list + free_list != elemsPerBlock");
170 real_alloc
+= dlink_list_length(&walker
->used_list
);
171 real_alloc
+= dlink_list_length(&walker
->free_list
);
174 if(allocated
!= real_alloc
)
175 blockheap_fail("block allocations don't match heap");
177 if(bh
->blocksAllocated
!= blockcount
)
178 blockheap_fail("blocksAllocated don't match blockcount");
184 bh_sanity_check_all(void *unused
)
187 DLINK_FOREACH(ptr
, heap_lists
.head
)
189 bh_sanity_check(ptr
->data
);
199 * void initBlockHeap(void)
203 * Side Effects: Initializes the block heap
209 #if defined(HAVE_MMAP) && !defined(MAP_ANON)
210 zero_fd
= open("/dev/zero", O_RDWR
);
213 blockheap_fail("Failed opening /dev/zero");
214 comm_socket(zero_fd
, FD_FILE
, "Anonymous mmap()");
216 eventAddIsh("block_heap_gc", block_heap_gc
, NULL
, 30);
220 * static inline void *get_block(size_t size)
222 * Input: Size of block to allocate
223 * Output: Pointer to new block
227 get_block(size_t size
)
232 ptr
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANON
, -1, 0);
234 ptr
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
, zero_fd
, 0);
236 if(ptr
== MAP_FAILED
)
248 block_heap_gc(void *unused
)
251 DLINK_FOREACH(ptr
, heap_lists
.head
)
253 BlockHeapGarbageCollect(ptr
->data
);
257 /* ************************************************************************ */
258 /* FUNCTION DOCUMENTATION: */
261 /* Allocates a new block for addition to a blockheap */
263 /* bh (IN): Pointer to parent blockheap. */
265 /* 0 if successful, 1 if not */
266 /* ************************************************************************ */
269 newblock(BlockHeap
* bh
)
276 /* Setup the initial data structure. */
277 b
= (Block
*) calloc(1, sizeof(Block
));
282 b
->free_list
.head
= b
->free_list
.tail
= NULL
;
283 b
->used_list
.head
= b
->used_list
.tail
= NULL
;
286 b
->alloc_size
= (bh
->elemsPerBlock
+ 1) * (bh
->elemSize
+ sizeof(MemBlock
));
288 b
->elems
= get_block(b
->alloc_size
);
294 /* Setup our blocks now */
295 for (i
= 0; i
< bh
->elemsPerBlock
; i
++)
298 newblk
= (void *) offset
;
301 newblk
->magic
= BALLOC_MAGIC
;
303 data
= (void *) ((size_t) offset
+ sizeof(MemBlock
));
305 dlinkAdd(data
, &newblk
->self
, &b
->free_list
);
306 offset
= (unsigned char *) ((unsigned char *) offset
+
307 bh
->elemSize
+ sizeof(MemBlock
));
310 ++bh
->blocksAllocated
;
311 bh
->freeElems
+= bh
->elemsPerBlock
;
318 /* ************************************************************************ */
319 /* FUNCTION DOCUMENTATION: */
320 /* BlockHeapCreate */
322 /* Creates a new blockheap from which smaller blocks can be allocated. */
323 /* Intended to be used instead of multiple calls to malloc() when */
324 /* performance is an issue. */
326 /* elemsize (IN): Size of the basic element to be stored */
327 /* elemsperblock (IN): Number of elements to be stored in a single block */
328 /* of memory. When the blockheap runs out of free memory, it will */
329 /* allocate elemsize * elemsperblock more. */
331 /* Pointer to new BlockHeap, or NULL if unsuccessful */
332 /* ************************************************************************ */
334 BlockHeapCreate(size_t elemsize
, int elemsperblock
)
337 s_assert(elemsize
> 0 && elemsperblock
> 0);
339 /* Catch idiotic requests up front */
340 if((elemsize
<= 0) || (elemsperblock
<= 0))
342 blockheap_fail("Attempting to BlockHeapCreate idiotic sizes");
345 /* Allocate our new BlockHeap */
346 bh
= (BlockHeap
*) calloc(1, sizeof(BlockHeap
));
349 blockheap_fail("Attempt to calloc() failed");
350 outofmemory(); /* die.. out of memory */
353 if((elemsize
% sizeof(void *)) != 0)
355 /* Pad to even pointer boundary */
356 elemsize
+= sizeof(void *);
357 elemsize
&= ~(sizeof(void *) - 1);
360 bh
->elemSize
= elemsize
;
361 bh
->elemsPerBlock
= elemsperblock
;
362 bh
->blocksAllocated
= 0;
366 /* Be sure our malloc was successful */
371 libseven_restart("Aiee! -- newblock() failed!!!");
376 blockheap_fail("bh == NULL when it shouldn't be");
378 dlinkAdd(bh
, &bh
->hlist
, &heap_lists
);
382 /* ************************************************************************ */
383 /* FUNCTION DOCUMENTATION: */
386 /* Returns a pointer to a struct within our BlockHeap that's free for */
389 /* bh (IN): Pointer to the Blockheap. */
391 /* Pointer to a structure (void *), or NULL if unsuccessful. */
392 /* ************************************************************************ */
395 BlockHeapAlloc(BlockHeap
* bh
)
398 dlink_node
*new_node
;
400 s_assert(bh
!= NULL
);
403 blockheap_fail("Cannot allocate if bh == NULL");
406 if(bh
->freeElems
== 0)
408 /* Allocate new block and assign */
409 /* newblock returns 1 if unsuccessful, 0 if not */
413 /* That didn't work..try to garbage collect */
414 BlockHeapGarbageCollect(bh
);
415 if(bh
->freeElems
== 0)
417 libseven_restart("newblock() failed and garbage collection didn't help");
422 for (walker
= bh
->base
; walker
!= NULL
; walker
= walker
->next
)
424 if(dlink_list_length(&walker
->free_list
) > 0)
427 bh_sanity_check_block(bh
, walker
);
430 new_node
= walker
->free_list
.head
;
431 dlinkMoveNode(new_node
, &walker
->free_list
, &walker
->used_list
);
432 s_assert(new_node
->data
!= NULL
);
433 if(new_node
->data
== NULL
)
434 blockheap_fail("new_node->data is NULL and that shouldn't happen!!!");
435 memset(new_node
->data
, 0, bh
->elemSize
);
439 struct MemBlock
*memblock
= (void *) ((size_t) new_node
->data
- sizeof(MemBlock
));
440 if(memblock
->magic
== BALLOC_FREE_MAGIC
)
441 memblock
->magic
= BALLOC_MAGIC
;
444 bh_sanity_check_block(bh
, walker
);
446 return (new_node
->data
);
449 blockheap_fail("BlockHeapAlloc failed, giving up");
454 /* ************************************************************************ */
455 /* FUNCTION DOCUMENTATION: */
458 /* Returns an element to the free pool, does not free() */
460 /* bh (IN): Pointer to BlockHeap containing element */
461 /* ptr (in): Pointer to element to be "freed" */
463 /* 0 if successful, 1 if element not contained within BlockHeap. */
464 /* ************************************************************************ */
466 BlockHeapFree(BlockHeap
* bh
, void *ptr
)
469 struct MemBlock
*memblock
;
471 s_assert(bh
!= NULL
);
472 s_assert(ptr
!= NULL
);
476 libseven_restart("balloc.c:BlockHeapFree() bh == NULL");
482 libseven_restart("balloc.BlockHeapFree() ptr == NULL");
486 memblock
= (void *) ((size_t) ptr
- sizeof(MemBlock
));
488 if(memblock
->magic
== BALLOC_FREE_MAGIC
)
490 blockheap_fail("double free of a block");
493 if(memblock
->magic
!= BALLOC_MAGIC
)
495 blockheap_fail("memblock->magic != BALLOC_MAGIC");
499 s_assert(memblock
->block
!= NULL
);
500 if(memblock
->block
== NULL
)
502 blockheap_fail("memblock->block == NULL, not a valid block?");
506 block
= memblock
->block
;
508 bh_sanity_check_block(bh
, block
);
511 mem_frob(ptr
, bh
->elemSize
);
512 dlinkMoveNode(&memblock
->self
, &block
->used_list
, &block
->free_list
);
514 bh_sanity_check_block(bh
, block
);
519 /* ************************************************************************ */
520 /* FUNCTION DOCUMENTATION: */
521 /* BlockHeapGarbageCollect */
523 /* Performs garbage collection on the block heap. Any blocks that are */
524 /* completely unallocated are removed from the heap. Garbage collection */
525 /* will never remove the root node of the heap. */
527 /* bh (IN): Pointer to the BlockHeap to be cleaned up */
529 /* 0 if successful, 1 if bh == NULL */
530 /* ************************************************************************ */
532 BlockHeapGarbageCollect(BlockHeap
* bh
)
534 Block
*walker
, *last
;
540 if(bh
->freeElems
< bh
->elemsPerBlock
|| bh
->blocksAllocated
== 1)
542 /* There couldn't possibly be an entire free block. Return. */
549 while (walker
!= NULL
)
551 if((dlink_list_length(&walker
->free_list
) == bh
->elemsPerBlock
) != 0)
553 free_block(walker
->elems
, walker
->alloc_size
);
556 last
->next
= walker
->next
;
563 bh
->base
= walker
->next
;
568 bh
->blocksAllocated
--;
569 bh
->freeElems
-= bh
->elemsPerBlock
;
574 walker
= walker
->next
;
580 /* ************************************************************************ */
581 /* FUNCTION DOCUMENTATION: */
582 /* BlockHeapDestroy */
584 /* Completely free()s a BlockHeap. Use for cleanup. */
586 /* bh (IN): Pointer to the BlockHeap to be destroyed. */
588 /* 0 if successful, 1 if bh == NULL */
589 /* ************************************************************************ */
591 BlockHeapDestroy(BlockHeap
* bh
)
593 Block
*walker
, *next
;
600 for (walker
= bh
->base
; walker
!= NULL
; walker
= next
)
603 free_block(walker
->elems
, walker
->alloc_size
);
607 dlinkDelete(&bh
->hlist
, &heap_lists
);
613 BlockHeapUsage(BlockHeap
* bh
, size_t * bused
, size_t * bfree
, size_t * bmemusage
)
623 freem
= bh
->freeElems
;
624 used
= (bh
->blocksAllocated
* bh
->elemsPerBlock
) - bh
->freeElems
;
625 memusage
= used
* (bh
->elemSize
+ sizeof(MemBlock
));
631 if(bmemusage
!= NULL
)
632 *bmemusage
= memusage
;
635 #endif /* NOBALLOC */