Patch ALIGN_NOWARN_ARM macro for better priority handling.
[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
48 /* Root directory of the cookies we have in the system */
49 static struct olsr_cookie_info *cookies[COOKIE_ID_MAX] = { 0 };
50
51 /*
52  * Allocate a cookie for the next available cookie id.
53  */
54 struct olsr_cookie_info *
55 olsr_alloc_cookie(const char *cookie_name, olsr_cookie_type cookie_type)
56 {
57   struct olsr_cookie_info *ci;
58   int ci_index;
59
60   /*
61    * Look for an unused index.
62    * For ease of troubleshooting (non-zero patterns) we start at index 1.
63    */
64   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
65     if (!cookies[ci_index]) {
66       break;
67     }
68   }
69
70   assert(ci_index < COOKIE_ID_MAX);     /* increase COOKIE_ID_MAX */
71
72   ci = calloc(1, sizeof(struct olsr_cookie_info));
73   cookies[ci_index] = ci;
74
75   /* Now populate the cookie info */
76   ci->ci_id = ci_index;
77   ci->ci_type = cookie_type;
78   if (cookie_name) {
79     ci->ci_name = strdup(cookie_name);
80   }
81
82   /* Init the free list */
83   if (cookie_type == OLSR_COOKIE_TYPE_MEMORY) {
84     list_head_init(&ci->ci_free_list);
85   }
86
87   return ci;
88 }
89
90 /*
91  * Free a cookie that is no longer being used.
92  */
93 void
94 olsr_free_cookie(struct olsr_cookie_info *ci)
95 {
96   struct list_node *memory_list;
97
98   /* Mark the cookie as unused */
99   cookies[ci->ci_id] = NULL;
100
101   /* Free name if set */
102   if (ci->ci_name) {
103     free(ci->ci_name);
104   }
105
106   /* Flush all the memory on the free list */
107   if (ci->ci_type == OLSR_COOKIE_TYPE_MEMORY) {
108     while (!list_is_empty(&ci->ci_free_list)) {
109       memory_list = ci->ci_free_list.next;
110       list_remove(memory_list);
111       free(memory_list);
112     }
113   }
114
115   free(ci);
116 }
117
118 /*
119  * Flush all cookies. This is really only called upon shutdown.
120  */
121 void
122 olsr_delete_all_cookies(void)
123 {
124   int ci_index;
125
126   /*
127    * Walk the full index range and kill 'em all.
128    */
129   for (ci_index = 1; ci_index < COOKIE_ID_MAX; ci_index++) {
130     if (!cookies[ci_index]) {
131       continue;
132     }
133     olsr_free_cookie(cookies[ci_index]);
134   }
135 }
136
137 /*
138  * Set the size for fixed block allocations.
139  * This is only allowed for memory cookies.
140  */
141 void
142 olsr_cookie_set_memory_size(struct olsr_cookie_info *ci, size_t size)
143 {
144   if (!ci) {
145     return;
146   }
147
148   assert(ci->ci_type == OLSR_COOKIE_TYPE_MEMORY);
149   ci->ci_size = size;
150 }
151
152 /*
153  * Basic sanity checking for a passed-in cookie-id.
154  */
155 static bool
156 olsr_cookie_valid(olsr_cookie_t cookie_id)
157 {
158   if ((cookie_id < COOKIE_ID_MAX) && cookies[cookie_id]) {
159     return true;
160   }
161   return false;
162 }
163
164 /*
165  * Increment usage state for a given cookie.
166  */
167 void
168 olsr_cookie_usage_incr(olsr_cookie_t cookie_id)
169 {
170   if (olsr_cookie_valid(cookie_id)) {
171     cookies[cookie_id]->ci_usage++;
172     cookies[cookie_id]->ci_changes++;
173   }
174 }
175
176 /*
177  * Decrement usage state for a given cookie.
178  */
179 void
180 olsr_cookie_usage_decr(olsr_cookie_t cookie_id)
181 {
182   if (olsr_cookie_valid(cookie_id)) {
183     cookies[cookie_id]->ci_usage--;
184     cookies[cookie_id]->ci_changes++;
185   }
186 }
187
188 /*
189  * Return a cookie name.
190  * Mostly used for logging purposes.
191  */
192 char *
193 olsr_cookie_name(olsr_cookie_t cookie_id)
194 {
195   static char unknown[] = "unknown";
196
197   if (olsr_cookie_valid(cookie_id)) {
198     return (cookies[cookie_id])->ci_name;
199   }
200
201   return unknown;
202 }
203
204 /*
205  * Allocate a fixed amount of memory based on a passed in cookie type.
206  */
207 void *
208 olsr_cookie_malloc(struct olsr_cookie_info *ci)
209 {
210   void *ptr;
211   struct olsr_cookie_mem_brand *branding;
212   struct list_node *free_list_node;
213   bool reuse = false;
214
215   /*
216    * Check first if we have reusable memory.
217    */
218   if (!ci->ci_free_list_usage) {
219
220     /*
221      * No reusable memory block on the free_list.
222      */
223     ptr = calloc(1, ci->ci_size + sizeof(struct olsr_cookie_mem_brand));
224
225     if (!ptr) {
226       const char *const err_msg = strerror(errno);
227       OLSR_PRINTF(1, "OUT OF MEMORY: %s\n", err_msg);
228       olsr_syslog(OLSR_LOG_ERR, "olsrd: out of memory!: %s\n", err_msg);
229       olsr_exit(ci->ci_name, EXIT_FAILURE);
230     }
231   } else {
232
233     /*
234      * There is a memory block on the free list.
235      * Carve it out of the list, and clean.
236      */
237     free_list_node = ci->ci_free_list.next;
238     list_remove(free_list_node);
239     ptr = (void *)free_list_node;
240     memset(ptr, 0, ci->ci_size);
241     ci->ci_free_list_usage--;
242     reuse = true;
243   }
244
245   /*
246    * Now brand mark the end of the memory block with a short signature
247    * indicating presence of a cookie. This will be checked against
248    * When the block is freed to detect corruption.
249    */
250   branding = (struct olsr_cookie_mem_brand *)ARM_NOWARN_ALIGN(((unsigned char *)ptr + ci->ci_size));
251   memcpy(&branding->cmb_sig, "cookie", 6);
252   branding->cmb_id = ci->ci_id;
253
254   /* Stats keeping */
255   olsr_cookie_usage_incr(ci->ci_id);
256
257 #if 0
258   OLSR_PRINTF(1, "MEMORY: alloc %s, %p, %u bytes%s\n", ci->ci_name, ptr, ci->ci_size, reuse ? ", reuse" : "");
259 #endif
260
261   return ptr;
262 }
263
264 /*
265  * Free a memory block owned by a given cookie.
266  * Run some corruption checks.
267  */
268 void
269 olsr_cookie_free(struct olsr_cookie_info *ci, void *ptr)
270 {
271   struct olsr_cookie_mem_brand *branding;
272   struct list_node *free_list_node;
273   bool reuse = false;
274
275   branding = (struct olsr_cookie_mem_brand *)ARM_NOWARN_ALIGN(((unsigned char *)ptr + ci->ci_size));
276
277   /*
278    * Verify if there has been a memory overrun, or
279    * the wrong owner is trying to free this.
280    */
281   assert(!memcmp(&branding->cmb_sig, "cookie", 6) && branding->cmb_id == ci->ci_id);
282
283   /* Kill the brand */
284   memset(branding, 0, sizeof(*branding));
285
286   /*
287    * Rather than freeing the memory right away, try to reuse at a later
288    * point. Keep at least ten percent of the active used blocks or at least
289    * ten blocks on the free list.
290    */
291   if ((ci->ci_free_list_usage < COOKIE_FREE_LIST_THRESHOLD) || (ci->ci_free_list_usage < ci->ci_usage / COOKIE_FREE_LIST_THRESHOLD)) {
292
293     free_list_node = (struct list_node *)ptr;
294     list_node_init(free_list_node);
295     list_add_before(&ci->ci_free_list, free_list_node);
296     ci->ci_free_list_usage++;
297     reuse = true;
298
299   } else {
300
301     /*
302      * No interest in reusing memory.
303      */
304     free(ptr);
305   }
306
307   /* Stats keeping */
308   olsr_cookie_usage_decr(ci->ci_id);
309
310 #if 0
311   OLSR_PRINTF(1, "MEMORY: free %s, %p, %u bytes%s\n", ci->ci_name, ptr, ci->ci_size, reuse ? ", reuse" : "");
312 #endif
313
314 }
315
316 /*
317  * Local Variables:
318  * c-basic-offset: 2
319  * indent-tabs-mode: nil
320  * End:
321  */