/[svn]/ircd-hybrid-7.2/src/balloc.c
ViewVC logotype

Contents of /ircd-hybrid-7.2/src/balloc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 908 - (show annotations)
Sun Nov 4 23:21:51 2007 UTC (12 years, 1 month ago) by stu
File MIME type: text/x-chdr
File size: 13020 byte(s)
Completely redo the build system.  Now uses libtool and automake.  Suggest you clean out your tree and then do ./autogen.sh.
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
54 #include "stdinc.h"
55
56 #if USE_BLOCK_ALLOC
57 #ifdef HAVE_MMAP /* We've got mmap() that is good */
58 #include <sys/mman.h>
59
60 /* HP-UX sucks */
61 #ifdef MAP_ANONYMOUS
62 #ifndef MAP_ANON
63 #define MAP_ANON MAP_ANONYMOUS
64 #endif
65 #endif /* MAP_ANONYMOUS */
66 #endif
67
68 #include "ircd.h"
69 #include "balloc.h"
70 #include "irc_string.h"
71 #include "tools.h"
72 #include "client.h"
73 #include "send.h"
74 #include "numeric.h"
75 #include "fdlist.h"
76 #include "event.h"
77
78
79 static BlockHeap *heap_list = NULL;
80
81 static int BlockHeapGarbageCollect(BlockHeap *);
82 static void heap_garbage_collection(void *);
83
84 /*! \brief Returns memory for the block back to either the malloc heap
85 * in case of !HAVE_MMAP, or back to the OS otherwise.
86 * \param ptr Pointer to memory to be freed
87 * \param size The size of the memory space
88 */
89 static inline void
90 free_block(void *ptr, size_t size)
91 {
92 #ifdef HAVE_MMAP
93 munmap(ptr, size);
94 #else
95 free(ptr);
96 #endif
97 }
98
99 #ifdef HAVE_MMAP
100 #ifndef MAP_ANON /* But we cannot mmap() anonymous pages */
101 /* So we mmap() /dev/zero, which is just as good */
102 static fde_t dpfd;
103 #endif
104 #endif
105
106 /*! \brief Opens /dev/zero and saves the file handle for
107 * future allocations.
108 */
109 void
110 initBlockHeap(void)
111 {
112 #ifdef HAVE_MMAP
113 #ifndef MAP_ANON
114 int zero_fd = open("/dev/zero", O_RDWR);
115
116 if (zero_fd < 0)
117 outofmemory();
118 fd_open(&dpfd, zero_fd, 0, "Anonymous mmap()");
119 #endif
120 eventAdd("heap_garbage_collection", &heap_garbage_collection, NULL, 119);
121 #endif
122 }
123
124 /*!
125 * \param size Size of block to allocate
126 * \return Address pointer to allocated data space
127 */
128 static inline void *
129 get_block(size_t size)
130 {
131 #ifdef HAVE_MMAP
132 void *ptr = NULL;
133
134 #ifndef MAP_ANON
135 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dpfd.fd, 0);
136 #else
137 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
138 #endif
139 return ptr == MAP_FAILED ? NULL : ptr;
140 #else
141 return malloc(size);
142 #endif
143 }
144
145 static void
146 heap_garbage_collection(void *arg)
147 {
148 BlockHeap *bh;
149
150 for (bh = heap_list; bh != NULL; bh = bh->next)
151 BlockHeapGarbageCollect(bh);
152 }
153
154 /*! \brief Allocates a new block for addition to a blockheap
155 * \param bh Pointer to parent blockheap
156 * \return 0 if successful, 1 if not
157 */
158 static int
159 newblock(BlockHeap *bh)
160 {
161 MemBlock *newblk = NULL;
162 Block *b = NULL;
163 int i = 0;
164 void *offset = NULL;
165
166 /* Setup the initial data structure. */
167 if ((b = calloc(1, sizeof(Block))) == NULL)
168 return 1;
169
170 b->freeElems = bh->elemsPerBlock;
171 b->next = bh->base;
172 b->alloc_size = bh->elemsPerBlock * (bh->elemSize + sizeof(MemBlock));
173 b->elems = get_block(b->alloc_size);
174
175 if (b->elems == NULL)
176 return 1;
177
178 offset = b->elems;
179
180 /* Setup our blocks now */
181 for (; i < bh->elemsPerBlock; ++i)
182 {
183 void *data;
184
185 newblk = offset;
186 newblk->block = b;
187 data = (void *)((size_t)offset + sizeof(MemBlock));
188
189 dlinkAdd(data, &newblk->self, &b->free_list);
190 offset = (void *)((size_t)offset + bh->elemSize + sizeof(MemBlock));
191 }
192
193 ++bh->blocksAllocated;
194 bh->freeElems += bh->elemsPerBlock;
195 bh->base = b;
196
197 return 0;
198 }
199
200 /*! \brief Creates a new blockheap
201 *
202 * Creates a new blockheap from which smaller blocks can be allocated.
203 * Intended to be used instead of multiple calls to malloc() when
204 * performance is an issue.
205 *
206 * \param name Name of the blockheap
207 * \param elemsize Size of the basic element to be stored
208 * \param elemsperblock Number of elements to be stored in a single block of
209 * memory. When the blockheap runs out of free memory,
210 * it will allocate elemsize * elemsperblock more.
211 * \return Pointer to new BlockHeap, or NULL if unsuccessful
212 */
213 BlockHeap *
214 BlockHeapCreate(const char *const name, size_t elemsize, int elemsperblock)
215 {
216 BlockHeap *bh = NULL;
217 assert(elemsize > 0 && elemsperblock > 0);
218
219 /* Catch idiotic requests up front */
220 if ((elemsize <= 0) || (elemsperblock <= 0))
221 outofmemory(); /* die.. out of memory */
222
223 /* Allocate our new BlockHeap */
224 if ((bh = calloc(1, sizeof(BlockHeap))) == NULL)
225 outofmemory(); /* die.. out of memory */
226
227 if ((elemsize % sizeof(void *)) != 0)
228 {
229 /* Pad to even pointer boundary */
230 elemsize += sizeof(void *);
231 elemsize &= ~(sizeof(void *) - 1);
232 }
233
234 bh->name = name;
235 bh->elemSize = elemsize;
236 bh->elemsPerBlock = elemsperblock;
237
238 /* Be sure our malloc was successful */
239 if (newblock(bh))
240 {
241 if (bh != NULL)
242 free(bh);
243
244 outofmemory(); /* die.. out of memory */
245 }
246
247 assert(bh);
248
249 bh->next = heap_list;
250 heap_list = bh;
251
252 return bh;
253 }
254
255 /*! \brief Returns a pointer to a struct within our BlockHeap that's free for
256 * the taking.
257 * \param bh Pointer to the Blockheap
258 * \return Address pointer to allocated data space, or NULL if unsuccessful
259 */
260 void *
261 BlockHeapAlloc(BlockHeap *bh)
262 {
263 Block *walker = NULL;
264 dlink_node *new_node = NULL;
265
266 assert(bh != NULL);
267
268 if (bh->freeElems == 0)
269 {
270 /* Allocate new block and assign */
271 /* newblock returns 1 if unsuccessful, 0 if not */
272 if (newblock(bh))
273 {
274 /* That didn't work..try to garbage collect */
275 BlockHeapGarbageCollect(bh);
276
277 if (newblock(bh))
278 outofmemory(); /* Well that didn't work either...bail */
279 }
280 }
281
282 for (walker = bh->base; walker != NULL; walker = walker->next)
283 {
284 if (walker->freeElems > 0)
285 {
286 --bh->freeElems;
287 --walker->freeElems;
288 new_node = walker->free_list.head;
289
290 dlinkDelete(new_node, &walker->free_list);
291 assert(new_node->data != NULL);
292
293 memset(new_node->data, 0, bh->elemSize);
294 return new_node->data;
295 }
296 }
297
298 assert(0 == 1);
299 outofmemory();
300 return NULL;
301 }
302
303 /*! \brief Returns an element to the free pool, does not free()
304 * \param bh Pointer to BlockHeap containing element
305 * \param ptr Pointer to element to be "freed"
306 * \return 0 if successful, 1 if element not contained within BlockHeap
307 */
308 int
309 BlockHeapFree(BlockHeap *bh, void *ptr)
310 {
311 Block *block = NULL;
312 struct MemBlock *memblock = NULL;
313
314 assert(bh != NULL);
315 assert(ptr != NULL);
316
317 memblock = (void *)((size_t)ptr - sizeof(MemBlock));
318 assert(memblock->block != NULL);
319
320 if (memblock->block == NULL)
321 outofmemory();
322
323 block = memblock->block;
324 ++bh->freeElems;
325 ++block->freeElems;
326 mem_frob(ptr, bh->elemSize);
327
328 dlinkAdd(ptr, &memblock->self, &block->free_list);
329 return 0;
330 }
331
332 /*! \brief Performs garbage collection on the block heap.
333 *
334 * Performs garbage collection on the block heap. Any blocks that are
335 * completely unallocated are removed from the heap. Garbage collection
336 * will \b never remove the root node of the heap.
337 *
338 * \param bh Pointer to the BlockHeap to be cleaned up
339 * \return 0 if successful, 1 if bh == NULL
340 */
341 static int
342 BlockHeapGarbageCollect(BlockHeap *bh)
343 {
344 Block *walker = NULL, *last = NULL;
345
346 assert(bh != NULL);
347
348 if (bh->freeElems < bh->elemsPerBlock || bh->blocksAllocated == 1)
349 {
350 /* There couldn't possibly be an entire free block. Return. */
351 return 0;
352 }
353
354 walker = bh->base;
355
356 while (walker != NULL)
357 {
358 if (walker->freeElems == bh->elemsPerBlock)
359 {
360 free_block(walker->elems, walker->alloc_size);
361
362 if (last != NULL)
363 {
364 last->next = walker->next;
365
366 if (walker != NULL)
367 free(walker);
368 walker = last->next;
369 }
370 else
371 {
372 bh->base = walker->next;
373
374 if (walker != NULL)
375 free(walker);
376 walker = bh->base;
377 }
378
379 --bh->blocksAllocated;
380 bh->freeElems -= bh->elemsPerBlock;
381 }
382 else
383 {
384 last = walker;
385 walker = walker->next;
386 }
387 }
388
389 return 0;
390 }
391
392 /*! \brief Completely free()s a BlockHeap. Use for cleanup.
393 * \param bh Pointer to the BlockHeap to be destroyed
394 * \return 0 if successful, 1 if bh == NULL
395 */
396 int
397 BlockHeapDestroy(BlockHeap *bh)
398 {
399 Block *walker = NULL, *next = NULL;
400
401 if (bh == NULL)
402 return 1;
403
404 for (walker = bh->base; walker != NULL; walker = next)
405 {
406 next = walker->next;
407 free_block(walker->elems, walker->alloc_size);
408
409 if (walker != NULL)
410 free(walker);
411 }
412
413 if (heap_list == bh)
414 heap_list = bh->next;
415 else {
416 BlockHeap *prev;
417
418 for (prev = heap_list; prev->next != bh; prev = prev->next)
419 /* nothing */ ;
420 prev->next = bh->next;
421 }
422
423 free(bh);
424 return 0;
425 }
426
427 /*! \brief Returns the number of bytes being used
428 * \param bh Pointer to a BlockHeap
429 * \return Number of bytes being used
430 */
431 static size_t
432 block_heap_get_used_mem(const BlockHeap *const bh)
433 {
434 return(((bh->blocksAllocated *
435 bh->elemsPerBlock)-bh->freeElems) *
436 (bh->elemSize + sizeof(MemBlock)));
437 }
438
439 /*! \brief Returns the number of bytes being free for further allocations
440 * \param bh Pointer to a BlockHeap
441 * \return Number of bytes being free for further allocations
442 */
443 static size_t
444 block_heap_get_free_mem(const BlockHeap *const bh)
445 {
446 return(bh->freeElems * (bh->elemSize + sizeof(MemBlock)));
447 }
448
449 /*! \brief Returns the total number of bytes of memory belonging to a heap
450 * \param bh Pointer to a BlockHeap
451 * \return Total number of bytes of memory belonging to a heap
452 */
453 static size_t
454 block_heap_get_size_mem(const BlockHeap *const bh)
455 {
456 return(((bh->blocksAllocated *
457 bh->elemsPerBlock)) *
458 (bh->elemSize + sizeof(MemBlock)));
459 }
460
461 /*! \brief Returns the number of elements being used.
462 * \param bh Pointer to a BlockHeap
463 * \return Number of elements being free for further allocations
464 */
465 static unsigned int
466 block_heap_get_used_elm(const BlockHeap *const bh)
467 {
468 return((bh->blocksAllocated *
469 bh->elemsPerBlock)-bh->freeElems);
470 }
471
472 /*! \brief Returns the number of elements being free for further allocations.
473 * \param bh Pointer to a BlockHeap
474 * \return Number of elements being free for further allocations
475 */
476 static unsigned int
477 block_heap_get_free_elm(const BlockHeap *const bh)
478 {
479 return(bh->freeElems);
480 }
481
482 /*! \brief Returns the number of total elements belonging to a heap.
483 * Includes \b free and \b used elements.
484 * \param bh Pointer to a BlockHeap
485 * \return Number of total elements belonging to a heap
486 */
487 static unsigned int
488 block_heap_get_size_elm(const BlockHeap *const bh)
489 {
490 return(bh->blocksAllocated * bh->elemsPerBlock);
491 }
492
493 void
494 block_heap_report_stats(struct Client *client_p)
495 {
496 const BlockHeap *bh = NULL;
497
498 for (bh = heap_list; bh != NULL; bh = bh->next)
499 sendto_one(client_p, ":%s %d %s z :%s mempool: used %u/%u free %u/%u (size %u/%u)",
500 me.name, RPL_STATSDEBUG, client_p->name, bh->name,
501 block_heap_get_used_elm(bh),
502 block_heap_get_used_mem(bh),
503 block_heap_get_free_elm(bh),
504 block_heap_get_free_mem(bh),
505 block_heap_get_size_elm(bh),
506 block_heap_get_size_mem(bh));
507 }
508
509 #endif

Properties

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

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.26