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