ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/libio/mem/balloc.c
Revision: 86
Committed: Wed Oct 5 20:36:04 2005 UTC (20 years, 10 months ago) by adx
Content type: text/x-csrc
File size: 12393 byte(s)
Log Message:
* attached misc/tools.c to misc/list.c,
  this should really have been done earlier.

* moved mem_frob() to memory.c

* single libio_init() instead of all startup functions;
  btw, I don't know if ircd's still able to boot ..

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2002 by the past and present ircd coders, and others.
5 * Original credit lines follow:
6 *
7 * File: balloc.c
8 * Owner: Wohali (Joan Touzet)
9 *
10 * Modified 2001/11/29 for mmap() support by Aaron Sethman <androsyn@ratbox.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 * USA
26 */
27
28 /*! \file balloc.c
29 * \brief A block allocator
30 * \version $Id$
31 *
32 * About the block allocator
33 *
34 * Basically we have three ways of getting memory off of the operating
35 * system. Below are this list of methods and the order of preference.
36 *
37 * 1. mmap() anonymous pages with the MMAP_ANON flag.\n
38 * 2. mmap() via the /dev/zero trick.\n
39 * 3. malloc()\n
40 *
41 * The advantages of 1 and 2 are this. We can munmap() the pages which will
42 * return the pages back to the operating system, thus reducing the size
43 * of the process as the memory is unused. malloc() on many systems just keeps
44 * a heap of memory to itself, which never gets given back to the OS, except on
45 * exit. This of course is bad, if say we have an event that causes us to allocate
46 * say, 200MB of memory, while our normal memory consumption would be 15MB. In the
47 * malloc() case, the amount of memory allocated to our process never goes down, as
48 * malloc() has it locked up in its heap. With the mmap() method, we can munmap()
49 * the block and return it back to the OS, thus causing our memory consumption to go
50 * down after we no longer need it.
51 */
52
53 #include "stdinc.h"
54 #ifdef HAVE_MMAP /* We've got mmap() that is good */
55 #include <sys/mman.h>
56
57 /* HP-UX sucks */
58 #ifdef MAP_ANONYMOUS
59 #ifndef MAP_ANON
60 #define MAP_ANON MAP_ANONYMOUS
61 #endif
62 #endif /* MAP_ANONYMOUS */
63 #endif
64
65 BlockHeap *heap_list = NULL;
66
67 static int BlockHeapGarbageCollect(BlockHeap *);
68 static void heap_garbage_collection(void *);
69
70 /*! \brief Returns memory for the block back to either the malloc heap
71 * in case of !HAVE_MMAP, or back to the OS otherwise.
72 * \param ptr Pointer to memory to be freed
73 * \param size The size of the memory space
74 */
75 static inline void
76 free_block(void *ptr, size_t size)
77 {
78 #ifdef HAVE_MMAP
79 munmap(ptr, size);
80 #else
81 free(ptr);
82 #endif
83 }
84
85 #ifdef HAVE_MMAP
86 #ifndef MAP_ANON /* But we cannot mmap() anonymous pages */
87 /* So we mmap() /dev/zero, which is just as good */
88 static int zero_fd = -1;
89 #endif
90 #endif
91
92 /*! \brief Opens /dev/zero and saves the file handle for
93 * future allocations.
94 */
95 void
96 initBlockHeap(void)
97 {
98 #ifdef HAVE_MMAP
99 #ifndef MAP_ANON
100 zero_fd = open("/dev/zero", O_RDWR);
101
102 if (zero_fd < 0)
103 outofmemory();
104 fd_open(zero_fd, FD_FILE, "Anonymous mmap()");
105 #endif
106 eventAdd("heap_garbage_collection", &heap_garbage_collection, NULL, 119);
107 #endif
108 }
109
110 /*!
111 * \param size Size of block to allocate
112 * \return Address pointer to allocated data space
113 */
114 static inline void *
115 get_block(size_t size)
116 {
117 #ifdef HAVE_MMAP
118 void *ptr = NULL;
119
120 #ifndef MAP_ANON
121 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
122 #else
123 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
124 #endif
125 return ptr == MAP_FAILED ? NULL : ptr;
126 #else
127 return malloc(size);
128 #endif
129 }
130
131 static void
132 heap_garbage_collection(void *arg)
133 {
134 BlockHeap *bh;
135
136 for (bh = heap_list; bh != NULL; bh = bh->next)
137 BlockHeapGarbageCollect(bh);
138 }
139
140 /*! \brief Allocates a new block for addition to a blockheap
141 * \param bh Pointer to parent blockheap
142 * \return 0 if successful, 1 if not
143 */
144 static int
145 newblock(BlockHeap *bh)
146 {
147 MemBlock *newblk = NULL;
148 Block *b = NULL;
149 int i = 0;
150 void *offset = NULL;
151
152 /* Setup the initial data structure. */
153 if ((b = calloc(1, sizeof(Block))) == NULL)
154 return 1;
155
156 b->freeElems = bh->elemsPerBlock;
157 b->next = bh->base;
158 b->alloc_size = bh->elemsPerBlock * (bh->elemSize + sizeof(MemBlock));
159 b->elems = get_block(b->alloc_size);
160
161 if (b->elems == NULL)
162 return 1;
163
164 offset = b->elems;
165
166 /* Setup our blocks now */
167 for (; i < bh->elemsPerBlock; ++i)
168 {
169 void *data;
170
171 newblk = offset;
172 newblk->block = b;
173 data = (void *)((size_t)offset + sizeof(MemBlock));
174
175 dlinkAdd(data, &newblk->self, &b->free_list);
176 offset = (unsigned char *)((unsigned char *)offset + bh->elemSize + sizeof(MemBlock));
177 }
178
179 ++bh->blocksAllocated;
180 bh->freeElems += bh->elemsPerBlock;
181 bh->base = b;
182
183 return 0;
184 }
185
186 /*! \brief Creates a new blockheap
187 *
188 * Creates a new blockheap from which smaller blocks can be allocated.
189 * Intended to be used instead of multiple calls to malloc() when
190 * performance is an issue.
191 *
192 * \param name Name of the blockheap
193 * \param elemsize Size of the basic element to be stored
194 * \param elemsperblock Number of elements to be stored in a single block of
195 * memory. When the blockheap runs out of free memory,
196 * it will allocate elemsize * elemsperblock more.
197 * \return Pointer to new BlockHeap, or NULL if unsuccessful
198 */
199 BlockHeap *
200 BlockHeapCreate(const char *const name, size_t elemsize, int elemsperblock)
201 {
202 BlockHeap *bh = NULL;
203 assert(elemsize > 0 && elemsperblock > 0);
204
205 /* Catch idiotic requests up front */
206 if ((elemsize <= 0) || (elemsperblock <= 0))
207 outofmemory(); /* die.. out of memory */
208
209 /* Allocate our new BlockHeap */
210 if ((bh = calloc(1, sizeof(BlockHeap))) == NULL)
211 outofmemory(); /* die.. out of memory */
212
213 if ((elemsize % sizeof(void *)) != 0)
214 {
215 /* Pad to even pointer boundary */
216 elemsize += sizeof(void *);
217 elemsize &= ~(sizeof(void *) - 1);
218 }
219
220 bh->name = name;
221 bh->elemSize = elemsize;
222 bh->elemsPerBlock = elemsperblock;
223
224 /* Be sure our malloc was successful */
225 if (newblock(bh))
226 {
227 if (bh != NULL)
228 free(bh);
229
230 outofmemory(); /* die.. out of memory */
231 }
232
233 assert(bh);
234
235 bh->next = heap_list;
236 heap_list = bh;
237
238 return bh;
239 }
240
241 /*! \brief Returns a pointer to a struct within our BlockHeap that's free for
242 * the taking.
243 * \param bh Pointer to the Blockheap
244 * \return Address pointer to allocated data space, or NULL if unsuccessful
245 */
246 void *
247 BlockHeapAlloc(BlockHeap *bh)
248 {
249 Block *walker = NULL;
250 dlink_node *new_node = NULL;
251
252 assert(bh != NULL);
253
254 if (bh->freeElems == 0)
255 {
256 /* Allocate new block and assign */
257 /* newblock returns 1 if unsuccessful, 0 if not */
258 if (newblock(bh))
259 {
260 /* That didn't work..try to garbage collect */
261 BlockHeapGarbageCollect(bh);
262
263 if (newblock(bh))
264 outofmemory(); /* Well that didn't work either...bail */
265 }
266 }
267
268 for (walker = bh->base; walker != NULL; walker = walker->next)
269 {
270 if (walker->freeElems > 0)
271 {
272 --bh->freeElems;
273 --walker->freeElems;
274 new_node = walker->free_list.head;
275
276 dlinkDelete(new_node, &walker->free_list);
277 dlinkAdd(new_node->data, new_node, &walker->used_list);
278 assert(new_node->data != NULL);
279
280 memset(new_node->data, 0, bh->elemSize);
281 return new_node->data;
282 }
283 }
284
285 assert(0 == 1);
286 outofmemory();
287 return NULL;
288 }
289
290 /*! \brief Returns an element to the free pool, does not free()
291 * \param bh Pointer to BlockHeap containing element
292 * \param ptr Pointer to element to be "freed"
293 * \return 0 if successful, 1 if element not contained within BlockHeap
294 */
295 int
296 BlockHeapFree(BlockHeap *bh, void *ptr)
297 {
298 Block *block = NULL;
299 struct MemBlock *memblock = NULL;
300
301 assert(bh != NULL);
302 assert(ptr != NULL);
303
304 memblock = (void *)((size_t)ptr - sizeof(MemBlock));
305 assert(memblock->block != NULL);
306
307 if (memblock->block == NULL)
308 outofmemory();
309
310 /* Is this block really on the used list? */
311 assert(dlinkFind(&memblock->block->used_list, ptr) != NULL);
312
313 block = memblock->block;
314 ++bh->freeElems;
315 ++block->freeElems;
316 #ifndef NDEBUG
317 mem_frob(ptr, bh->elemSize);
318 #endif
319
320 dlinkDelete(&memblock->self, &block->used_list);
321 dlinkAdd(ptr, &memblock->self, &block->free_list);
322 return 0;
323 }
324
325 /*! \brief Performs garbage collection on the block heap.
326 *
327 * Performs garbage collection on the block heap. Any blocks that are
328 * completely unallocated are removed from the heap. Garbage collection
329 * will \b never remove the root node of the heap.
330 *
331 * \param bh Pointer to the BlockHeap to be cleaned up
332 * \return 0 if successful, 1 if bh == NULL
333 */
334 static int
335 BlockHeapGarbageCollect(BlockHeap *bh)
336 {
337 Block *walker = NULL, *last = NULL;
338
339 if (bh == NULL)
340 return 1;
341
342 if (bh->freeElems < bh->elemsPerBlock || bh->blocksAllocated == 1)
343 {
344 /* There couldn't possibly be an entire free block. Return. */
345 return 0;
346 }
347
348 walker = bh->base;
349
350 while (walker != NULL)
351 {
352 if (walker->freeElems == bh->elemsPerBlock)
353 {
354 free_block(walker->elems, walker->alloc_size);
355
356 if (last != NULL)
357 {
358 last->next = walker->next;
359
360 if (walker != NULL)
361 free(walker);
362 walker = last->next;
363 }
364 else
365 {
366 bh->base = walker->next;
367
368 if (walker != NULL)
369 free(walker);
370 walker = bh->base;
371 }
372
373 --bh->blocksAllocated;
374 bh->freeElems -= bh->elemsPerBlock;
375 }
376 else
377 {
378 last = walker;
379 walker = walker->next;
380 }
381 }
382
383 return 0;
384 }
385
386 /*! \brief Completely free()s a BlockHeap. Use for cleanup.
387 * \param bh Pointer to the BlockHeap to be destroyed
388 * \return 0 if successful, 1 if bh == NULL
389 */
390 int
391 BlockHeapDestroy(BlockHeap *bh)
392 {
393 Block *walker = NULL, *next = NULL;
394
395 if (bh == NULL)
396 return 1;
397
398 for (walker = bh->base; walker != NULL; walker = next)
399 {
400 next = walker->next;
401 free_block(walker->elems, walker->alloc_size);
402
403 if (walker != NULL)
404 free(walker);
405 }
406
407 if (heap_list == bh)
408 heap_list = bh->next;
409 else {
410 BlockHeap *prev;
411
412 for (prev = heap_list; prev->next != bh; prev = prev->next)
413 /* nothing */ ;
414 prev->next = bh->next;
415 }
416
417 free(bh);
418 return 0;
419 }
420
421 /*! \brief Returns the number of bytes being used
422 * \param bh Pointer to a BlockHeap
423 * \return Number of bytes being used
424 */
425 size_t
426 block_heap_get_used_mem(const BlockHeap *bh)
427 {
428 return ((bh->blocksAllocated * bh->elemsPerBlock) - bh->freeElems) *
429 (bh->elemSize + sizeof(MemBlock));
430 }
431
432 /*! \brief Returns the number of bytes being free for further allocations
433 * \param bh Pointer to a BlockHeap
434 * \return Number of bytes being free for further allocations
435 */
436 size_t
437 block_heap_get_free_mem(const BlockHeap *bh)
438 {
439 return bh->freeElems * (bh->elemSize + sizeof(MemBlock));
440 }
441
442 /*! \brief Returns the total number of bytes of memory belonging to a heap
443 * \param bh Pointer to a BlockHeap
444 * \return Total number of bytes of memory belonging to a heap
445 */
446 size_t
447 block_heap_get_size_mem(const BlockHeap *bh)
448 {
449 return (bh->blocksAllocated * bh->elemsPerBlock) *
450 (bh->elemSize + sizeof(MemBlock));
451 }
452
453 /*! \brief Returns the number of elements being used.
454 * \param bh Pointer to a BlockHeap
455 * \return Number of elements being free for further allocations
456 */
457 unsigned int
458 block_heap_get_used_elm(const BlockHeap *bh)
459 {
460 return (bh->blocksAllocated * bh->elemsPerBlock) - bh->freeElems;
461 }
462
463 /*! \brief Returns the number of elements being free for further allocations.
464 * \param bh Pointer to a BlockHeap
465 * \return Number of elements being free for further allocations
466 */
467 unsigned int
468 block_heap_get_free_elm(const BlockHeap *bh)
469 {
470 return bh->freeElems;
471 }
472
473 /*! \brief Returns the number of total elements belonging to a heap.
474 * Includes \b free and \b used elements.
475 * \param bh Pointer to a BlockHeap
476 * \return Number of total elements belonging to a heap
477 */
478 unsigned int
479 block_heap_get_size_elm(const BlockHeap *bh)
480 {
481 return bh->blocksAllocated * bh->elemsPerBlock;
482 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision