2c4b89d24d1117f1a9119a412283ef4d518e429d
[oonf.git] / src / subsystems / oonf_class.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <stdlib.h>
47
48 #include <oonf/libcommon/avl.h>
49 #include <oonf/libcommon/avl_comp.h>
50 #include <oonf/libcore/oonf_logging.h>
51 #include <oonf/libcore/oonf_subsystem.h>
52
53 #include <oonf/subsystems/oonf_class.h>
54
55 /* Definitions */
56 #define LOG_CLASS (_oonf_class_subsystem.logging)
57
58 /* prototypes */
59 static int _init(void);
60 static void _cleanup(void);
61
62 static void _free_freelist(struct oonf_class *);
63 static size_t _roundup(size_t);
64 static const char *_cb_to_keystring(struct oonf_objectkey_str *, struct oonf_class *, void *);
65
66 /* list of memory cookies */
67 static struct avl_tree _classes_tree;
68
69 /* name of event types */
70 static const char *OONF_CLASS_EVENT_NAME[] = {
71   [OONF_OBJECT_ADDED] = "added",
72   [OONF_OBJECT_REMOVED] = "removed",
73   [OONF_OBJECT_CHANGED] = "changed",
74 };
75
76 /* subsystem definition */
77 static struct oonf_subsystem _oonf_class_subsystem = {
78   .name = OONF_CLASS_SUBSYSTEM,
79   .init = _init,
80   .cleanup = _cleanup,
81 };
82 DECLARE_OONF_PLUGIN(_oonf_class_subsystem);
83
84 /**
85  * Initialize the class system
86  * @return always returns 0
87  */
88 static int
89 _init(void) {
90   avl_init(&_classes_tree, avl_comp_strcasecmp, false);
91   return 0;
92 }
93
94 /**
95  * Cleanup the memory cookie system
96  */
97 static void
98 _cleanup(void) {
99   struct oonf_class *info, *iterator;
100
101   /*
102    * Walk the full index range and kill 'em all.
103    */
104   avl_for_each_element_safe(&_classes_tree, info, _node, iterator) {
105     oonf_class_remove(info);
106   }
107 }
108
109 /**
110  * Allocate a new memcookie.
111  * @param ci initialized memcookie
112  */
113 void
114 oonf_class_add(struct oonf_class *ci) {
115   /* round up size to make block extendable */
116   ci->total_size = _roundup(ci->size);
117
118   /* hook into tree */
119   ci->_node.key = ci->name;
120   avl_insert(&_classes_tree, &ci->_node);
121
122   /* add standard key generator if necessary */
123   if (ci->to_keystring == NULL) {
124     ci->to_keystring = _cb_to_keystring;
125   }
126
127   /* Init list heads */
128   list_init_head(&ci->_free_list);
129   list_init_head(&ci->_extensions);
130
131   OONF_DEBUG(LOG_CLASS, "Class %s added: %" PRINTF_SIZE_T_SPECIFIER " bytes\n", ci->name, ci->total_size);
132 }
133
134 /**
135  * Delete a memcookie and all memory in the free list
136  * @param ci pointer to memcookie
137  */
138 void
139 oonf_class_remove(struct oonf_class *ci) {
140   struct oonf_class_extension *ext, *iterator;
141
142   /* remove memcookie from tree */
143   avl_remove(&_classes_tree, &ci->_node);
144
145   /* remove all free memory blocks */
146   _free_freelist(ci);
147
148   /* remove all listeners */
149   list_for_each_element_safe(&ci->_extensions, ext, _node, iterator) {
150     oonf_class_extension_remove(ext);
151   }
152
153   OONF_DEBUG(LOG_CLASS, "Class %s removed\n", ci->name);
154 }
155
156 /**
157  * Allocate a fixed amount of memory based on a passed in cookie type.
158  * @param ci pointer to memcookie info
159  * @return allocated memory
160  */
161 void *
162 oonf_class_malloc(struct oonf_class *ci) {
163   struct list_entity *entity;
164   void *ptr;
165
166 #ifdef OONF_LOG_DEBUG_INFO
167   bool reuse = false;
168 #endif
169
170   /*
171    * Check first if we have reusable memory.
172    */
173   if (list_is_empty(&ci->_free_list)) {
174     /*
175      * No reusable memory block on the free_list.
176      * Allocate a fresh one.
177      */
178     ptr = calloc(1, ci->total_size);
179     if (ptr == NULL) {
180       OONF_WARN(LOG_CLASS, "Out of memory for: %s", ci->name);
181       return NULL;
182     }
183     ci->_allocated++;
184   }
185   else {
186     /*
187      * There is a memory block on the free list.
188      * Carve it out of the list, and clean.
189      */
190     entity = ci->_free_list.next;
191     list_remove(entity);
192
193     memset(entity, 0, ci->total_size);
194     ptr = entity;
195
196     ci->_free_list_size--;
197     ci->_recycled++;
198 #ifdef OONF_LOG_DEBUG_INFO
199     reuse = true;
200 #endif
201   }
202
203   /* Stats keeping */
204   ci->_current_usage++;
205
206   OONF_DEBUG(LOG_CLASS, "MEMORY: alloc %s, %" PRINTF_SIZE_T_SPECIFIER " bytes%s\n", ci->name, ci->total_size,
207     reuse ? ", reuse" : "");
208   return ptr;
209 }
210
211 /**
212  * Free a memory block owned by a given cookie.
213  * @param ci pointer to memcookie info
214  * @param ptr pointer to memory block
215  */
216 void
217 oonf_class_free(struct oonf_class *ci, void *ptr) {
218   struct list_entity *item;
219 #ifdef OONF_LOG_DEBUG_INFO
220   bool reuse = false;
221 #endif
222
223   /*
224    * Rather than freeing the memory right away, try to reuse at a later
225    * point. Keep at least ten percent of the active used blocks or at least
226    * ten blocks on the free list.
227    */
228   if (ci->_free_list_size < ci->min_free_count || (ci->_free_list_size < ci->_current_usage / 10)) {
229     item = ptr;
230
231     list_add_tail(&ci->_free_list, item);
232
233     ci->_free_list_size++;
234 #ifdef OONF_LOG_DEBUG_INFO
235     reuse = true;
236 #endif
237   }
238   else {
239     /* No interest in reusing memory. */
240     free(ptr);
241   }
242
243   /* Stats keeping */
244   ci->_current_usage--;
245
246   OONF_DEBUG(
247     LOG_CLASS, "MEMORY: free %s, %" PRINTF_SIZE_T_SPECIFIER " bytes%s\n", ci->name, ci->size, reuse ? ", reuse" : "");
248 }
249
250 /**
251  * Register an extension to an existing class without objects.
252  * This function can only fail if ext->size is not 0.
253  * @param ext pointer to class extension
254  * @return 0 if extension was registered, -1 if an error happened
255  */
256 int
257 oonf_class_extension_add(struct oonf_class_extension *ext) {
258   struct oonf_class *c;
259
260   if (oonf_class_is_extension_registered(ext)) {
261     /* already registered */
262     return 0;
263   }
264
265   c = avl_find_element(&_classes_tree, ext->class_name, c, _node);
266   if (c == NULL) {
267     OONF_WARN(LOG_CLASS, "Unknown class %s for extension %s", ext->class_name, ext->ext_name);
268     return -1;
269   }
270
271   if (c->_allocated != 0 && ext->size > 0) {
272     OONF_WARN(LOG_CLASS, "Class %s is already in use and cannot be extended", c->name);
273     return -1;
274   }
275
276   /* add to class extension list */
277   list_add_tail(&c->_extensions, &ext->_node);
278
279   if (ext->size > 0) {
280     /* make sure freelist is empty */
281     _free_freelist(c);
282
283     /* old size is new offset */
284     ext->_offset = c->total_size;
285
286     /* calculate new size */
287     c->total_size = _roundup(c->total_size + ext->size);
288
289     OONF_DEBUG(LOG_CLASS,
290       "Class %s extended: %" PRINTF_SIZE_T_SPECIFIER " bytes,"
291       " '%s' has offset %" PRINTF_SIZE_T_SPECIFIER " and length %" PRINTF_SIZE_T_SPECIFIER "\n",
292       c->name, c->total_size, ext->ext_name, ext->_offset, ext->size);
293   }
294
295   return 0;
296 }
297
298 /**
299  * Remove extension from class
300  * @param ext pointer to class extension
301  */
302 void
303 oonf_class_extension_remove(struct oonf_class_extension *ext) {
304   if (oonf_class_is_extension_registered(ext)) {
305     list_remove(&ext->_node);
306     ext->_offset = 0;
307   }
308 }
309
310 /**
311  * Fire an event for a class
312  * @param c pointer to class
313  * @param ptr pointer to object
314  * @param evt type of event
315  */
316 void
317 oonf_class_event(struct oonf_class *c, void *ptr, enum oonf_class_event evt) {
318   struct oonf_class_extension *ext;
319 #ifdef OONF_LOG_DEBUG_INFO
320   struct oonf_objectkey_str buf;
321 #endif
322
323   OONF_DEBUG(LOG_CLASS, "Fire '%s' event for %s", OONF_CLASS_EVENT_NAME[evt], c->to_keystring(&buf, c, ptr));
324   list_for_each_element(&c->_extensions, ext, _node) {
325     if (evt == OONF_OBJECT_ADDED && ext->cb_add != NULL) {
326       OONF_DEBUG(LOG_CLASS, "Fire listener %s", ext->ext_name);
327       ext->cb_add(ptr);
328     }
329     else if (evt == OONF_OBJECT_REMOVED && ext->cb_remove != NULL) {
330       OONF_DEBUG(LOG_CLASS, "Fire listener %s", ext->ext_name);
331       ext->cb_remove(ptr);
332     }
333     else if (evt == OONF_OBJECT_CHANGED && ext->cb_change != NULL) {
334       OONF_DEBUG(LOG_CLASS, "Fire listener %s", ext->ext_name);
335       ext->cb_change(ptr);
336     }
337   }
338   OONF_DEBUG(LOG_CLASS, "Fire event finished");
339 }
340
341 /**
342  * get tree of memory classes
343  * @return class tree
344  */
345 struct avl_tree *
346 oonf_class_get_tree(void) {
347   return &_classes_tree;
348 }
349
350 /**
351  * get name of memory class event
352  * @param event type of event
353  * @return name of event
354  */
355 const char *
356 oonf_class_get_event_name(enum oonf_class_event event) {
357   return OONF_CLASS_EVENT_NAME[event];
358 }
359
360 /**
361  * @param size memory size in byte
362  * @return rounded up size to sizeof(struct list_entity)
363  */
364 static size_t
365 _roundup(size_t size) {
366   size = size + sizeof(struct list_entity) - 1;
367   size = size & (~(sizeof(struct list_entity) - 1));
368
369   return size;
370 }
371
372 /**
373  * Free all objects in the free_list of a memory cookie
374  * @param ci pointer to memory cookie
375  */
376 static void
377 _free_freelist(struct oonf_class *ci) {
378   while (!list_is_empty(&ci->_free_list)) {
379     struct list_entity *item;
380     item = ci->_free_list.next;
381
382     list_remove(item);
383     free(item);
384   }
385   ci->_free_list_size = 0;
386 }
387
388 /**
389  * Default keystring creator
390  * @param buf pointer to target buffer
391  * @param class olsr class
392  * @param ptr pointer to object
393  * @return pointer to target buffer
394  */
395 static const char *
396 _cb_to_keystring(struct oonf_objectkey_str *buf, struct oonf_class *class, void *ptr) {
397   snprintf(buf->buf, sizeof(*buf), "%s::0x%" PRINTF_SIZE_T_HEX_SPECIFIER, class->name, (size_t)ptr);
398
399   return buf->buf;
400 }