]>
Commit | Line | Data |
---|---|---|
18c8241a MM |
1 | /* |
2 | * BIRD Resource Manager | |
3 | * | |
5cc1e1f8 | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
18c8241a MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
46eb80d5 | 11 | #include <stdint.h> |
18c8241a MM |
12 | |
13 | #include "nest/bird.h" | |
14 | #include "lib/resource.h" | |
221135d6 | 15 | #include "lib/string.h" |
18c8241a | 16 | |
5cc1e1f8 MM |
17 | /** |
18 | * DOC: Resource pools | |
19 | * | |
20 | * Resource pools (&pool) are just containers holding a list of | |
21 | * other resources. Freeing a pool causes all the listed resources | |
22 | * to be freed as well. Each existing &resource is linked to some pool | |
23 | * except for a root pool which isn't linked anywhere, so all the | |
24 | * resources form a tree structure with internal nodes corresponding | |
25 | * to pools and leaves being the other resources. | |
26 | * | |
27 | * Example: Almost all modules of BIRD have their private pool which | |
28 | * is freed upon shutdown of the module. | |
29 | */ | |
30 | ||
18c8241a MM |
31 | struct pool { |
32 | resource r; | |
33 | list inside; | |
65d2a88d | 34 | const char *name; |
18c8241a MM |
35 | }; |
36 | ||
d4bc8dc0 MM |
37 | static void pool_dump(resource *); |
38 | static void pool_free(resource *); | |
c9763428 | 39 | static resource *pool_lookup(resource *, unsigned long); |
acb60628 | 40 | static size_t pool_memsize(resource *P); |
18c8241a MM |
41 | |
42 | static struct resclass pool_class = { | |
43 | "Pool", | |
44 | sizeof(pool), | |
45 | pool_free, | |
c9763428 | 46 | pool_dump, |
acb60628 OZ |
47 | pool_lookup, |
48 | pool_memsize | |
18c8241a MM |
49 | }; |
50 | ||
51 | pool root_pool; | |
52 | ||
53 | static int indent; | |
54 | ||
5cc1e1f8 MM |
55 | /** |
56 | * rp_new - create a resource pool | |
57 | * @p: parent pool | |
58 | * @name: pool name (to be included in debugging dumps) | |
59 | * | |
60 | * rp_new() creates a new resource pool inside the specified | |
61 | * parent pool. | |
62 | */ | |
18c8241a | 63 | pool * |
65d2a88d | 64 | rp_new(pool *p, const char *name) |
18c8241a MM |
65 | { |
66 | pool *z = ralloc(p, &pool_class); | |
5bc512aa | 67 | z->name = name; |
18c8241a MM |
68 | init_list(&z->inside); |
69 | return z; | |
70 | } | |
71 | ||
d4bc8dc0 | 72 | static void |
18c8241a MM |
73 | pool_free(resource *P) |
74 | { | |
75 | pool *p = (pool *) P; | |
76 | resource *r, *rr; | |
77 | ||
78 | r = HEAD(p->inside); | |
79 | while (rr = (resource *) r->n.next) | |
80 | { | |
81 | r->class->free(r); | |
82 | xfree(r); | |
83 | r = rr; | |
84 | } | |
85 | } | |
86 | ||
d4bc8dc0 | 87 | static void |
18c8241a MM |
88 | pool_dump(resource *P) |
89 | { | |
90 | pool *p = (pool *) P; | |
91 | resource *r; | |
92 | ||
5bc512aa | 93 | debug("%s\n", p->name); |
18c8241a MM |
94 | indent += 3; |
95 | WALK_LIST(r, p->inside) | |
96 | rdump(r); | |
97 | indent -= 3; | |
98 | } | |
99 | ||
acb60628 OZ |
100 | static size_t |
101 | pool_memsize(resource *P) | |
102 | { | |
103 | pool *p = (pool *) P; | |
104 | resource *r; | |
105 | size_t sum = sizeof(pool) + ALLOC_OVERHEAD; | |
106 | ||
107 | WALK_LIST(r, p->inside) | |
108 | sum += rmemsize(r); | |
109 | ||
110 | return sum; | |
111 | } | |
112 | ||
c9763428 MM |
113 | static resource * |
114 | pool_lookup(resource *P, unsigned long a) | |
115 | { | |
116 | pool *p = (pool *) P; | |
117 | resource *r, *q; | |
118 | ||
119 | WALK_LIST(r, p->inside) | |
120 | if (r->class->lookup && (q = r->class->lookup(r, a))) | |
121 | return q; | |
122 | return NULL; | |
123 | } | |
124 | ||
2cc37815 MM |
125 | /** |
126 | * rmove - move a resource | |
127 | * @res: resource | |
128 | * @p: pool to move the resource to | |
129 | * | |
130 | * rmove() moves a resource from one pool to another. | |
131 | */ | |
132 | ||
133 | void rmove(void *res, pool *p) | |
134 | { | |
135 | resource *r = res; | |
136 | ||
137 | if (r) | |
138 | { | |
139 | if (r->n.next) | |
140 | rem_node(&r->n); | |
141 | add_tail(&p->inside, &r->n); | |
142 | } | |
143 | } | |
144 | ||
5cc1e1f8 MM |
145 | /** |
146 | * rfree - free a resource | |
147 | * @res: resource | |
148 | * | |
149 | * rfree() frees the given resource and all information associated | |
150 | * with it. In case it's a resource pool, it also frees all the objects | |
151 | * living inside the pool. | |
152 | * | |
153 | * It works by calling a class-specific freeing function. | |
154 | */ | |
18c8241a MM |
155 | void |
156 | rfree(void *res) | |
157 | { | |
158 | resource *r = res; | |
159 | ||
48e5f32d OZ |
160 | if (!r) |
161 | return; | |
162 | ||
163 | if (r->n.next) | |
164 | rem_node(&r->n); | |
165 | r->class->free(r); | |
665b8e52 | 166 | r->class = NULL; |
48e5f32d | 167 | xfree(r); |
18c8241a MM |
168 | } |
169 | ||
5cc1e1f8 MM |
170 | /** |
171 | * rdump - dump a resource | |
172 | * @res: resource | |
173 | * | |
174 | * This function prints out all available information about the given | |
175 | * resource to the debugging output. | |
176 | * | |
177 | * It works by calling a class-specific dump function. | |
178 | */ | |
18c8241a MM |
179 | void |
180 | rdump(void *res) | |
181 | { | |
182 | char x[16]; | |
183 | resource *r = res; | |
184 | ||
083c43e2 OZ |
185 | bsprintf(x, "%%%ds%%p ", indent); |
186 | debug(x, "", r); | |
18c8241a MM |
187 | if (r) |
188 | { | |
5bc512aa | 189 | debug("%s ", r->class->name); |
18c8241a MM |
190 | r->class->dump(r); |
191 | } | |
192 | else | |
193 | debug("NULL\n"); | |
194 | } | |
195 | ||
acb60628 OZ |
196 | size_t |
197 | rmemsize(void *res) | |
198 | { | |
199 | resource *r = res; | |
200 | if (!r) | |
201 | return 0; | |
202 | if (!r->class->memsize) | |
203 | return r->class->size + ALLOC_OVERHEAD; | |
204 | return r->class->memsize(r); | |
205 | } | |
206 | ||
5cc1e1f8 MM |
207 | /** |
208 | * ralloc - create a resource | |
209 | * @p: pool to create the resource in | |
210 | * @c: class of the new resource | |
211 | * | |
212 | * This function is called by the resource classes to create a new | |
213 | * resource of the specified class and link it to the given pool. | |
daeeb8e9 OZ |
214 | * Allocated memory is zeroed. Size of the resource structure is taken |
215 | * from the @size field of the &resclass. | |
5cc1e1f8 | 216 | */ |
18c8241a MM |
217 | void * |
218 | ralloc(pool *p, struct resclass *c) | |
219 | { | |
220 | resource *r = xmalloc(c->size); | |
daeeb8e9 | 221 | bzero(r, c->size); |
18c8241a MM |
222 | |
223 | r->class = c; | |
6a8d3f1c OZ |
224 | if (p) |
225 | add_tail(&p->inside, &r->n); | |
18c8241a MM |
226 | return r; |
227 | } | |
228 | ||
5cc1e1f8 MM |
229 | /** |
230 | * rlookup - look up a memory location | |
231 | * @a: memory address | |
232 | * | |
233 | * This function examines all existing resources to see whether | |
234 | * the address @a is inside any resource. It's used for debugging | |
235 | * purposes only. | |
236 | * | |
237 | * It works by calling a class-specific lookup function for each | |
238 | * resource. | |
239 | */ | |
c9763428 MM |
240 | void |
241 | rlookup(unsigned long a) | |
242 | { | |
243 | resource *r; | |
244 | ||
245 | debug("Looking up %08lx\n", a); | |
246 | if (r = pool_lookup(&root_pool.r, a)) | |
247 | rdump(r); | |
248 | else | |
249 | debug("Not found.\n"); | |
250 | } | |
251 | ||
5cc1e1f8 MM |
252 | /** |
253 | * resource_init - initialize the resource manager | |
254 | * | |
255 | * This function is called during BIRD startup. It initializes | |
256 | * all data structures of the resource manager and creates the | |
257 | * root pool. | |
258 | */ | |
18c8241a MM |
259 | void |
260 | resource_init(void) | |
261 | { | |
262 | root_pool.r.class = &pool_class; | |
ed68a5c6 | 263 | root_pool.name = "Root"; |
18c8241a MM |
264 | init_list(&root_pool.inside); |
265 | } | |
266 | ||
5cc1e1f8 MM |
267 | /** |
268 | * DOC: Memory blocks | |
269 | * | |
270 | * Memory blocks are pieces of contiguous allocated memory. | |
271 | * They are a bit non-standard since they are represented not by a pointer | |
272 | * to &resource, but by a void pointer to the start of data of the | |
273 | * memory block. All memory block functions know how to locate the header | |
274 | * given the data pointer. | |
275 | * | |
276 | * Example: All "unique" data structures such as hash tables are allocated | |
277 | * as memory blocks. | |
18c8241a MM |
278 | */ |
279 | ||
280 | struct mblock { | |
281 | resource r; | |
282 | unsigned size; | |
d1abbeac | 283 | uintptr_t data_align[0]; |
18c8241a MM |
284 | byte data[0]; |
285 | }; | |
286 | ||
fab37e81 | 287 | static void mbl_free(resource *r UNUSED) |
18c8241a MM |
288 | { |
289 | } | |
290 | ||
d4bc8dc0 | 291 | static void mbl_debug(resource *r) |
18c8241a MM |
292 | { |
293 | struct mblock *m = (struct mblock *) r; | |
294 | ||
295 | debug("(size=%d)\n", m->size); | |
296 | } | |
297 | ||
c9763428 MM |
298 | static resource * |
299 | mbl_lookup(resource *r, unsigned long a) | |
300 | { | |
301 | struct mblock *m = (struct mblock *) r; | |
302 | ||
303 | if ((unsigned long) m->data <= a && (unsigned long) m->data + m->size > a) | |
304 | return r; | |
305 | return NULL; | |
306 | } | |
307 | ||
acb60628 OZ |
308 | static size_t |
309 | mbl_memsize(resource *r) | |
310 | { | |
311 | struct mblock *m = (struct mblock *) r; | |
312 | return ALLOC_OVERHEAD + sizeof(struct mblock) + m->size; | |
313 | } | |
314 | ||
d4bc8dc0 | 315 | static struct resclass mb_class = { |
18c8241a MM |
316 | "Memory", |
317 | 0, | |
318 | mbl_free, | |
319 | mbl_debug, | |
acb60628 OZ |
320 | mbl_lookup, |
321 | mbl_memsize | |
18c8241a MM |
322 | }; |
323 | ||
5cc1e1f8 MM |
324 | /** |
325 | * mb_alloc - allocate a memory block | |
326 | * @p: pool | |
327 | * @size: size of the block | |
328 | * | |
329 | * mb_alloc() allocates memory of a given size and creates | |
330 | * a memory block resource representing this memory chunk | |
331 | * in the pool @p. | |
332 | * | |
333 | * Please note that mb_alloc() returns a pointer to the memory | |
334 | * chunk, not to the resource, hence you have to free it using | |
335 | * mb_free(), not rfree(). | |
336 | */ | |
18c8241a MM |
337 | void * |
338 | mb_alloc(pool *p, unsigned size) | |
339 | { | |
340 | struct mblock *b = xmalloc(sizeof(struct mblock) + size); | |
341 | ||
342 | b->r.class = &mb_class; | |
343 | add_tail(&p->inside, &b->r.n); | |
344 | b->size = size; | |
345 | return b->data; | |
346 | } | |
347 | ||
5cc1e1f8 MM |
348 | /** |
349 | * mb_allocz - allocate and clear a memory block | |
350 | * @p: pool | |
351 | * @size: size of the block | |
352 | * | |
353 | * mb_allocz() allocates memory of a given size, initializes it to | |
354 | * zeroes and creates a memory block resource representing this memory | |
355 | * chunk in the pool @p. | |
356 | * | |
cca97066 | 357 | * Please note that mb_allocz() returns a pointer to the memory |
5cc1e1f8 MM |
358 | * chunk, not to the resource, hence you have to free it using |
359 | * mb_free(), not rfree(). | |
360 | */ | |
7a2105be MM |
361 | void * |
362 | mb_allocz(pool *p, unsigned size) | |
363 | { | |
364 | void *x = mb_alloc(p, size); | |
365 | bzero(x, size); | |
366 | return x; | |
367 | } | |
368 | ||
3d15dcdb OZ |
369 | /** |
370 | * mb_realloc - reallocate a memory block | |
3d15dcdb OZ |
371 | * @m: memory block |
372 | * @size: new size of the block | |
373 | * | |
374 | * mb_realloc() changes the size of the memory block @m to a given size. | |
375 | * The contents will be unchanged to the minimum of the old and new sizes; | |
bf139664 OZ |
376 | * newly allocated memory will be uninitialized. Contrary to realloc() |
377 | * behavior, @m must be non-NULL, because the resource pool is inherited | |
378 | * from it. | |
3d15dcdb OZ |
379 | * |
380 | * Like mb_alloc(), mb_realloc() also returns a pointer to the memory | |
bf139664 | 381 | * chunk, not to the resource, hence you have to free it using |
3d15dcdb OZ |
382 | * mb_free(), not rfree(). |
383 | */ | |
384 | void * | |
bf139664 | 385 | mb_realloc(void *m, unsigned size) |
3d15dcdb | 386 | { |
665b8e52 | 387 | struct mblock *b = SKIP_BACK(struct mblock, data, m); |
3d15dcdb | 388 | |
665b8e52 | 389 | b = xrealloc(b, sizeof(struct mblock) + size); |
bf139664 | 390 | replace_node(&b->r.n, &b->r.n); |
3d15dcdb OZ |
391 | b->size = size; |
392 | return b->data; | |
393 | } | |
394 | ||
395 | ||
5cc1e1f8 MM |
396 | /** |
397 | * mb_free - free a memory block | |
398 | * @m: memory block | |
399 | * | |
400 | * mb_free() frees all memory associated with the block @m. | |
401 | */ | |
18c8241a MM |
402 | void |
403 | mb_free(void *m) | |
404 | { | |
48e5f32d OZ |
405 | if (!m) |
406 | return; | |
407 | ||
18c8241a MM |
408 | struct mblock *b = SKIP_BACK(struct mblock, data, m); |
409 | rfree(b); | |
410 | } | |
3d15dcdb | 411 | |
bf139664 OZ |
412 | |
413 | ||
414 | #define STEP_UP(x) ((x) + (x)/2 + 4) | |
415 | ||
416 | void | |
417 | buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size) | |
418 | { | |
419 | unsigned nsize = MIN(*size, need); | |
420 | ||
421 | while (nsize < need) | |
422 | nsize = STEP_UP(nsize); | |
423 | ||
6a8d3f1c | 424 | *buf = mb_realloc(*buf, nsize * item_size); |
bf139664 OZ |
425 | *size = nsize; |
426 | } |