d9e22d7625444314850b9e0c0d662f130827f0dd
[olsrd.git] / src / olsr_cookie.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, 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 #include "olsr.h"
43 #include "defs.h"
44 #include "olsr_cookie.h"
45 #include "log.h"
46 #include "valgrind/valgrind.h"
47 #include "valgrind/memcheck.h"
48
49 #include <assert.h>
50 #include <errno.h>
51 #include <stdlib.h>
52
53
54 /* Root directory of the cookies we have in the system */
55 static struct olsr_cookie_info *cookies[COOKIE_ID_MAX] = { 0 };
56
57 /*
58  * Allocate a cookie for the next available cookie id.
59  */
60 struct olsr_cookie_info *
61 olsr_alloc_cookie(const char *cookie_name, olsr_cookie_type cookie_type)
62 {
63   struct olsr_cookie_info *ci;
64   int ci_index;
65
66   /*
67    * Look for an unused index.
68    * For ease of troubleshooting (non-zero patterns) we start at index 1.
69    */
70   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
71     if (!cookies[ci_index]) {
72       break;
73     }
74   }
75
76   assert(ci_index < COOKIE_ID_MAX);     /* increase COOKIE_ID_MAX */
77
78   ci = olsr_malloc(sizeof(struct olsr_cookie_info), "new cookie");
79   cookies[ci_index] = ci;
80
81   /* Now populate the cookie info */
82   ci->ci_id = ci_index;
83   ci->ci_type = cookie_type;
84   if (cookie_name) {
85     ci->ci_name = olsr_strdup(cookie_name);
86   }
87
88   /* Init the free list */
89   if (cookie_type == OLSR_COOKIE_TYPE_MEMORY) {
90     list_head_init(&ci->ci_free_list);
91     VALGRIND_CREATE_MEMPOOL(ci, 0, 1);
92   }
93
94   return ci;
95 }
96
97 /*
98  * Free a cookie that is no longer being used.
99  */
100 static void
101 olsr_free_cookie(struct olsr_cookie_info *ci)
102 {
103   struct list_node *memory_list;
104
105   /* Mark the cookie as unused */
106   cookies[ci->ci_id] = NULL;
107
108   /* Free name */
109   free(ci->ci_name);
110
111   /* Flush all the memory on the free list */
112   if (ci->ci_type == OLSR_COOKIE_TYPE_MEMORY) {
113
114     /*
115      * First make all items accessible,
116      * such that valgrind does not complain at shutdown.
117      */
118     if (!list_is_empty(&ci->ci_free_list)) {
119       for (memory_list = ci->ci_free_list.next; memory_list != &ci->ci_free_list; memory_list = memory_list->next) {
120         VALGRIND_MAKE_MEM_DEFINED(memory_list, ci->ci_size);
121       }
122     }
123
124     while (!list_is_empty(&ci->ci_free_list)) {
125       memory_list = ci->ci_free_list.next;
126       list_remove(memory_list);
127       free(memory_list);
128     }
129     VALGRIND_DESTROY_MEMPOOL(ci);
130   }
131
132   free(ci);
133 }
134
135 /*
136  * Flush all cookies. This is really only called upon shutdown.
137  */
138 void
139 olsr_delete_all_cookies(void)
140 {
141   int ci_index;
142
143   /*
144    * Walk the full index range and kill 'em all.
145    */
146   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
147     if (!cookies[ci_index]) {
148       continue;
149     }
150     olsr_free_cookie(cookies[ci_index]);
151   }
152 }
153
154 /*
155  * Set the size for fixed block allocations.
156  * This is only allowed for memory cookies.
157  */
158 void
159 olsr_cookie_set_memory_size(struct olsr_cookie_info *ci, size_t size)
160 {
161   if (!ci) {
162     return;
163   }
164
165   assert(ci->ci_type == OLSR_COOKIE_TYPE_MEMORY);
166   ci->ci_size = size;
167 }
168
169 /*
170  * Set if a returned memory block shall be cleared after returning to
171  * the free pool. This is only allowed for memory cookies.
172  */
173 void
174 olsr_cookie_set_memory_clear(struct olsr_cookie_info *ci, bool clear)
175 {
176   if (!ci) {
177     return;
178   }
179
180   assert(ci->ci_type == OLSR_COOKIE_TYPE_MEMORY);
181
182   if (!clear) {
183     ci->ci_flags |= COOKIE_NO_MEMCLEAR;
184   } else {
185     ci->ci_flags &= ~COOKIE_NO_MEMCLEAR;
186   }
187 }
188
189 /*
190  * Set if a returned memory block shall be initialized to an all zero or
191  * to a poison memory pattern after returning to the free pool.
192  * This is only allowed for memory cookies.
193  */
194 void
195 olsr_cookie_set_memory_poison(struct olsr_cookie_info *ci, bool poison)
196 {
197   if (!ci) {
198     return;
199   }
200
201   assert(ci->ci_type == OLSR_COOKIE_TYPE_MEMORY);
202
203   if (poison) {
204     ci->ci_flags |= COOKIE_MEMPOISON;
205   } else {
206     ci->ci_flags &= ~COOKIE_MEMPOISON;
207   }
208 }
209
210 /*
211  * Basic sanity checking for a passed-in cookie-id.
212  */
213 static bool
214 olsr_cookie_valid(olsr_cookie_t cookie_id)
215 {
216   if ((cookie_id < COOKIE_ID_MAX) && cookies[cookie_id]) {
217     return true;
218   }
219   return false;
220 }
221
222 /*
223  * Increment usage state for a given cookie.
224  */
225 void
226 olsr_cookie_usage_incr(olsr_cookie_t cookie_id)
227 {
228   if (olsr_cookie_valid(cookie_id)) {
229     cookies[cookie_id]->ci_usage++;
230     cookies[cookie_id]->ci_changes++;
231   }
232 }
233
234 /*
235  * Decrement usage state for a given cookie.
236  */
237 void
238 olsr_cookie_usage_decr(olsr_cookie_t cookie_id)
239 {
240   if (olsr_cookie_valid(cookie_id)) {
241     cookies[cookie_id]->ci_usage--;
242     cookies[cookie_id]->ci_changes++;
243   }
244 }
245
246 /*
247  * Return a cookie name.
248  * Mostly used for logging purposes.
249  */
250 char *
251 olsr_cookie_name(olsr_cookie_t cookie_id)
252 {
253   static char unknown[] = "unknown";
254
255   if (olsr_cookie_valid(cookie_id)) {
256     return (cookies[cookie_id])->ci_name;
257   }
258
259   return unknown;
260 }
261
262 /*
263  * Allocate a fixed amount of memory based on a passed in cookie type.
264  */
265 void *
266 olsr_cookie_malloc(struct olsr_cookie_info *ci)
267 {
268   void *ptr;
269   struct olsr_cookie_mem_brand *branding;
270   struct list_node *free_list_node;
271 #if !defined REMOVE_LOG_DEBUG
272   bool reuse = false;
273 #endif
274   size_t size;
275
276   /*
277    * Check first if we have reusable memory.
278    */
279   if (!ci->ci_free_list_usage) {
280
281     /*
282      * No reusable memory block on the free_list.
283      * Allocate a fresh one.
284      */
285     size = ci->ci_size + sizeof(struct olsr_cookie_mem_brand);
286     ptr = olsr_malloc(size, ci->ci_name);
287
288     /*
289      * Poison the memory for debug purposes ?
290      */
291     if (ci->ci_flags & COOKIE_MEMPOISON) {
292       memset(ptr, COOKIE_MEMPOISON_PATTERN, size);
293     }
294
295   } else {
296
297     /*
298      * There is a memory block on the free list.
299      * Carve it out of the list, and clean.
300      */
301     free_list_node = ci->ci_free_list.next;
302     ptr = (void *)free_list_node;
303     VALGRIND_MAKE_MEM_DEFINED(ptr, ci->ci_size);
304
305     /*
306      * Before dequeuing the node from the free list,
307      * make the list pointers of the node ahead of
308      * us accessible, such that valgrind does not
309      * log a false positive.
310      */
311     if (free_list_node->next == &ci->ci_free_list) {
312       list_remove(free_list_node);
313     } else {
314
315       /*
316        * Make next item accessible, remove it and make next item inaccessible.
317        */
318       VALGRIND_MAKE_MEM_DEFINED(free_list_node->next, ci->ci_size);
319       list_remove(free_list_node);
320       VALGRIND_MAKE_MEM_NOACCESS(free_list_node->next, ci->ci_size);
321     }
322
323     /*
324      * Reset the memory unless the caller has told us so.
325      */
326     if (!(ci->ci_flags & COOKIE_NO_MEMCLEAR)) {
327
328       /*
329        * Poison the memory for debug purposes ?
330        */
331       if (ci->ci_flags & COOKIE_MEMPOISON) {
332         memset(ptr, COOKIE_MEMPOISON_PATTERN, ci->ci_size);
333       } else {
334         memset(ptr, 0, ci->ci_size);
335       }
336     }
337
338     ci->ci_free_list_usage--;
339 #if !defined REMOVE_LOG_DEBUG
340     reuse = true;
341 #endif
342   }
343
344   /*
345    * Now brand mark the end of the memory block with a short signature
346    * indicating presence of a cookie. This will be checked against
347    * When the block is freed to detect corruption.
348    */
349   branding = (struct olsr_cookie_mem_brand *)
350     ((unsigned char *)ptr + ci->ci_size);
351   memcpy(&branding->cmb_sig, "cookie", 6);
352   branding->cmb_id = ci->ci_id;
353
354   /* Stats keeping */
355   olsr_cookie_usage_incr(ci->ci_id);
356
357   OLSR_DEBUG(LOG_COOKIE, "MEMORY: alloc %s, %p, %lu bytes%s\n",
358              ci->ci_name, ptr, (unsigned long)ci->ci_size, reuse ? ", reuse" : "");
359
360   VALGRIND_MEMPOOL_ALLOC(ci, ptr, ci->ci_size);
361   return ptr;
362 }
363
364 /*
365  * Free a memory block owned by a given cookie.
366  * Run some corruption checks.
367  */
368 void
369 olsr_cookie_free(struct olsr_cookie_info *ci, void *ptr)
370 {
371   struct list_node *free_list_node;
372 #if !defined REMOVE_LOG_DEBUG
373   bool reuse = false;
374 #endif
375   struct olsr_cookie_mem_brand *branding = (struct olsr_cookie_mem_brand *)
376     ((unsigned char *)ptr + ci->ci_size);
377
378   /*
379    * Verify if there has been a memory overrun, or
380    * the wrong owner is trying to free this.
381    */
382   assert(!memcmp(&branding->cmb_sig, "cookie", 6) && branding->cmb_id == ci->ci_id);
383
384   /* Kill the brand */
385   memset(branding, 0, sizeof(*branding));
386
387   /*
388    * Rather than freeing the memory right away, try to reuse at a later
389    * point. Keep at least ten percent of the active used blocks or at least
390    * ten blocks on the free list.
391    */
392   if ((ci->ci_free_list_usage < COOKIE_FREE_LIST_THRESHOLD) || (ci->ci_free_list_usage < ci->ci_usage / COOKIE_FREE_LIST_THRESHOLD)) {
393
394     free_list_node = (struct list_node *)ptr;
395     list_node_init(free_list_node);
396
397     /*
398      * Before enqueuing the node to the free list,
399      * make the list pointers of the node ahead of
400      * us accessible, such that valgrind does not
401      * log a false positive.
402      */
403     if (list_is_empty(&ci->ci_free_list)) {
404       list_add_before(&ci->ci_free_list, free_list_node);
405     } else {
406
407       /*
408        * Make next item accessible, add it and make next item inaccessible.
409        */
410       VALGRIND_MAKE_MEM_DEFINED(ci->ci_free_list.prev, ci->ci_size);
411       list_add_before(&ci->ci_free_list, free_list_node);
412       VALGRIND_MAKE_MEM_NOACCESS(ci->ci_free_list.prev, ci->ci_size);
413     }
414
415     ci->ci_free_list_usage++;
416 #if !defined REMOVE_LOG_DEBUG
417     reuse = true;
418 #endif
419
420   } else {
421
422     /*
423      * No interest in reusing memory.
424      */
425     free(ptr);
426   }
427
428   /* Stats keeping */
429   olsr_cookie_usage_decr(ci->ci_id);
430
431   OLSR_DEBUG(LOG_COOKIE, "MEMORY: free %s, %p, %lu bytes%s\n",
432              ci->ci_name, ptr, (unsigned long)ci->ci_size, reuse ? ", reuse" : "");
433
434   VALGRIND_MEMPOOL_FREE(ci, ptr);
435   VALGRIND_MAKE_MEM_NOACCESS(ptr, ci->ci_size);
436 }
437
438 struct olsr_cookie_info *
439 olsr_cookie_get(int i)
440 {
441   return cookies[i];
442 }
443
444 /*
445  * Local Variables:
446  * c-basic-offset: 2
447  * indent-tabs-mode: nil
448  * End:
449  */