]> jfr.im git - solanum.git/blob - libratbox/src/balloc.c
WHOIS: Make hide_opers_in_whois not affect opers doing whois.
[solanum.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 26100 2008-09-20 01:27:19Z 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 static uintptr_t offset_pad;
63
64 /* status information for an allocated block in heap */
65 struct rb_heap_block
66 {
67 size_t alloc_size;
68 rb_dlink_node node;
69 unsigned long free_count;
70 void *elems; /* Points to allocated memory */
71 };
72 typedef struct rb_heap_block rb_heap_block;
73
74 /* information for the root node of the heap */
75 struct rb_bh
76 {
77 rb_dlink_node hlist;
78 size_t elemSize; /* Size of each element to be stored */
79 unsigned long elemsPerBlock; /* Number of elements per block */
80 rb_dlink_list block_list;
81 rb_dlink_list free_list;
82 char *desc;
83 };
84
85 static rb_dlink_list *heap_lists;
86
87 #define rb_bh_fail(x) _rb_bh_fail(x, __FILE__, __LINE__)
88
89 static void
90 _rb_bh_fail(const char *reason, const char *file, int line)
91 {
92 rb_lib_log("rb_heap_blockheap failure: %s (%s:%d)", reason, file, line);
93 abort();
94 }
95
96 /*
97 * void rb_init_bh(void)
98 *
99 * Inputs: None
100 * Outputs: None
101 * Side Effects: Initializes the block heap
102 */
103
104 void
105 rb_init_bh(void)
106 {
107 heap_lists = rb_malloc(sizeof(rb_dlink_list));
108 offset_pad = sizeof(void *);
109 /* XXX if you get SIGBUS when trying to use a long long..here is where you need to
110 * fix your shit
111 */
112 #ifdef __sparc__
113 if((offset_pad % __alignof__(long long)) != 0)
114 {
115 offset_pad += __alignof__(long long);
116 offset_pad &= ~(__alignof__(long long) - 1);
117 }
118 #endif
119 }
120
121 /* ************************************************************************ */
122 /* FUNCTION DOCUMENTATION: */
123 /* rb_bh_create */
124 /* Description: */
125 /* Creates a new blockheap from which smaller blocks can be allocated. */
126 /* Intended to be used instead of multiple calls to malloc() when */
127 /* performance is an issue. */
128 /* Parameters: */
129 /* elemsize (IN): Size of the basic element to be stored */
130 /* elemsperblock (IN): Number of elements to be stored in a single block */
131 /* of memory. When the blockheap runs out of free memory, it will */
132 /* allocate elemsize * elemsperblock more. */
133 /* Returns: */
134 /* Pointer to new rb_bh, or NULL if unsuccessful */
135 /* ************************************************************************ */
136 rb_bh *
137 rb_bh_create(size_t elemsize, int elemsperblock, const char *desc)
138 {
139 rb_bh *bh;
140 lrb_assert(elemsize > 0 && elemsperblock > 0);
141 lrb_assert(elemsize >= sizeof(rb_dlink_node));
142
143 /* Catch idiotic requests up front */
144 if((elemsize == 0) || (elemsperblock <= 0))
145 {
146 rb_bh_fail("Attempting to rb_bh_create idiotic sizes");
147 }
148
149 if(elemsize < sizeof(rb_dlink_node))
150 rb_bh_fail("Attempt to rb_bh_create smaller than sizeof(rb_dlink_node)");
151
152 /* Allocate our new rb_bh */
153 bh = rb_malloc(sizeof(rb_bh));
154 bh->elemSize = elemsize;
155 bh->elemsPerBlock = elemsperblock;
156 if(desc != NULL)
157 bh->desc = rb_strdup(desc);
158
159 if(bh == NULL)
160 {
161 rb_bh_fail("bh == NULL when it shouldn't be");
162 }
163 rb_dlinkAdd(bh, &bh->hlist, heap_lists);
164 return (bh);
165 }
166
167 /* ************************************************************************ */
168 /* FUNCTION DOCUMENTATION: */
169 /* rb_bh_alloc */
170 /* Description: */
171 /* Returns a pointer to a struct within our rb_bh that's free for */
172 /* the taking. */
173 /* Parameters: */
174 /* bh (IN): Pointer to the Blockheap. */
175 /* Returns: */
176 /* Pointer to a structure (void *), or NULL if unsuccessful. */
177 /* ************************************************************************ */
178
179 void *
180 rb_bh_alloc(rb_bh *bh)
181 {
182 lrb_assert(bh != NULL);
183 if(rb_unlikely(bh == NULL))
184 {
185 rb_bh_fail("Cannot allocate if bh == NULL");
186 }
187
188 return (rb_malloc(bh->elemSize));
189 }
190
191
192 /* ************************************************************************ */
193 /* FUNCTION DOCUMENTATION: */
194 /* rb_bh_free */
195 /* Description: */
196 /* Returns an element to the free pool, does not free() */
197 /* Parameters: */
198 /* bh (IN): Pointer to rb_bh containing element */
199 /* ptr (in): Pointer to element to be "freed" */
200 /* Returns: */
201 /* 0 if successful, 1 if element not contained within rb_bh. */
202 /* ************************************************************************ */
203 int
204 rb_bh_free(rb_bh *bh, void *ptr)
205 {
206 lrb_assert(bh != NULL);
207 lrb_assert(ptr != NULL);
208
209 if(rb_unlikely(bh == NULL))
210 {
211 rb_lib_log("balloc.c:rb_bhFree() bh == NULL");
212 return (1);
213 }
214
215 if(rb_unlikely(ptr == NULL))
216 {
217 rb_lib_log("balloc.rb_bhFree() ptr == NULL");
218 return (1);
219 }
220
221 rb_free(ptr);
222 return (0);
223 }
224
225
226 /* ************************************************************************ */
227 /* FUNCTION DOCUMENTATION: */
228 /* rb_bhDestroy */
229 /* Description: */
230 /* Completely free()s a rb_bh. Use for cleanup. */
231 /* Parameters: */
232 /* bh (IN): Pointer to the rb_bh to be destroyed. */
233 /* Returns: */
234 /* 0 if successful, 1 if bh == NULL */
235 /* ************************************************************************ */
236 int
237 rb_bh_destroy(rb_bh *bh)
238 {
239 if(bh == NULL)
240 return (1);
241
242 rb_dlinkDelete(&bh->hlist, heap_lists);
243 rb_free(bh->desc);
244 rb_free(bh);
245
246 return (0);
247 }
248
249 void
250 rb_bh_usage(rb_bh *bh, size_t *bused, size_t *bfree, size_t *bmemusage, const char **desc)
251 {
252 if(bused != NULL)
253 *bused = 0;
254 if(bfree != NULL)
255 *bfree = 0;
256 if(bmemusage != NULL)
257 *bmemusage = 0;
258 if(desc != NULL)
259 *desc = "no blockheap";
260 }
261
262 void
263 rb_bh_usage_all(rb_bh_usage_cb *cb, void *data)
264 {
265 rb_dlink_node *ptr;
266 rb_bh *bh;
267 size_t used, freem, memusage, heapalloc;
268 static const char *unnamed = "(unnamed_heap)";
269 const char *desc = unnamed;
270
271 if(cb == NULL)
272 return;
273
274 RB_DLINK_FOREACH(ptr, heap_lists->head)
275 {
276 bh = (rb_bh *)ptr->data;
277 freem = rb_dlink_list_length(&bh->free_list);
278 used = (rb_dlink_list_length(&bh->block_list) * bh->elemsPerBlock) - freem;
279 memusage = used * bh->elemSize;
280 heapalloc = (freem + used) * bh->elemSize;
281 if(bh->desc != NULL)
282 desc = bh->desc;
283 cb(used, freem, memusage, heapalloc, desc, data);
284 }
285 return;
286 }
287
288 void
289 rb_bh_total_usage(size_t *total_alloc, size_t *total_used)
290 {
291 rb_dlink_node *ptr;
292 size_t total_memory = 0, used_memory = 0, used, freem;
293 rb_bh *bh;
294
295 RB_DLINK_FOREACH(ptr, heap_lists->head)
296 {
297 bh = (rb_bh *)ptr->data;
298 freem = rb_dlink_list_length(&bh->free_list);
299 used = (rb_dlink_list_length(&bh->block_list) * bh->elemsPerBlock) - freem;
300 used_memory += used * bh->elemSize;
301 total_memory += (freem + used) * bh->elemSize;
302 }
303
304 if(total_alloc != NULL)
305 *total_alloc = total_memory;
306 if(total_used != NULL)
307 *total_used = used_memory;
308 }