Blackfin: add support for irqflags tracing
[safe/jmp/linux-2.6] / drivers / staging / batman-adv / translation-table.c
1 /*
2  * Copyright (C) 2007-2009 B.A.T.M.A.N. contributors:
3  *
4  * Marek Lindner, Simon Wunderlich
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA
19  *
20  */
21
22 #include "main.h"
23 #include "translation-table.h"
24 #include "soft-interface.h"
25 #include "types.h"
26 #include "hash.h"
27
28 struct hashtable_t *hna_local_hash;
29 static struct hashtable_t *hna_global_hash;
30 atomic_t hna_local_changed;
31
32 DEFINE_SPINLOCK(hna_local_hash_lock);
33 static DEFINE_SPINLOCK(hna_global_hash_lock);
34
35 static DECLARE_DELAYED_WORK(hna_local_purge_wq, hna_local_purge);
36
37 static void hna_local_start_timer(void)
38 {
39         queue_delayed_work(bat_event_workqueue, &hna_local_purge_wq, 10 * HZ);
40 }
41
42 int hna_local_init(void)
43 {
44         if (hna_local_hash)
45                 return 1;
46
47         hna_local_hash = hash_new(128, compare_orig, choose_orig);
48
49         if (!hna_local_hash)
50                 return 0;
51
52         atomic_set(&hna_local_changed, 0);
53         hna_local_start_timer();
54
55         return 1;
56 }
57
58 void hna_local_add(uint8_t *addr)
59 {
60         struct hna_local_entry *hna_local_entry;
61         struct hna_global_entry *hna_global_entry;
62         struct hashtable_t *swaphash;
63         unsigned long flags;
64
65         spin_lock_irqsave(&hna_local_hash_lock, flags);
66         hna_local_entry =
67                 ((struct hna_local_entry *)hash_find(hna_local_hash, addr));
68         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
69
70         if (hna_local_entry != NULL) {
71                 hna_local_entry->last_seen = jiffies;
72                 return;
73         }
74
75         /* only announce as many hosts as possible in the batman-packet and
76            space in batman_packet->num_hna That also should give a limit to
77            MAC-flooding. */
78         if ((num_hna + 1 > (ETH_DATA_LEN - BAT_PACKET_LEN) / ETH_ALEN) ||
79             (num_hna + 1 > 255)) {
80                 bat_dbg(DBG_ROUTES, "Can't add new local hna entry (%pM): number of local hna entries exceeds packet size \n", addr);
81                 return;
82         }
83
84         bat_dbg(DBG_ROUTES, "Creating new local hna entry: %pM \n",
85                 addr);
86
87         hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC);
88         if (!hna_local_entry)
89                 return;
90
91         memcpy(hna_local_entry->addr, addr, ETH_ALEN);
92         hna_local_entry->last_seen = jiffies;
93
94         /* the batman interface mac address should never be purged */
95         if (compare_orig(addr, soft_device->dev_addr))
96                 hna_local_entry->never_purge = 1;
97         else
98                 hna_local_entry->never_purge = 0;
99
100         spin_lock_irqsave(&hna_local_hash_lock, flags);
101
102         hash_add(hna_local_hash, hna_local_entry);
103         num_hna++;
104         atomic_set(&hna_local_changed, 1);
105
106         if (hna_local_hash->elements * 4 > hna_local_hash->size) {
107                 swaphash = hash_resize(hna_local_hash,
108                                        hna_local_hash->size * 2);
109
110                 if (swaphash == NULL)
111                         printk(KERN_ERR "batman-adv:Couldn't resize local hna hash table \n");
112                 else
113                         hna_local_hash = swaphash;
114         }
115
116         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
117
118         /* remove address from global hash if present */
119         spin_lock_irqsave(&hna_global_hash_lock, flags);
120
121         hna_global_entry =
122                 ((struct hna_global_entry *)hash_find(hna_global_hash, addr));
123
124         if (hna_global_entry != NULL)
125                 _hna_global_del_orig(hna_global_entry, "local hna received");
126
127         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
128 }
129
130 int hna_local_fill_buffer(unsigned char *buff, int buff_len)
131 {
132         struct hna_local_entry *hna_local_entry;
133         HASHIT(hashit);
134         int i = 0;
135         unsigned long flags;
136
137         spin_lock_irqsave(&hna_local_hash_lock, flags);
138
139         while (hash_iterate(hna_local_hash, &hashit)) {
140
141                 if (buff_len < (i + 1) * ETH_ALEN)
142                         break;
143
144                 hna_local_entry = hashit.bucket->data;
145                 memcpy(buff + (i * ETH_ALEN), hna_local_entry->addr, ETH_ALEN);
146
147                 i++;
148         }
149
150         /* if we did not get all new local hnas see you next time  ;-) */
151         if (i == num_hna)
152                 atomic_set(&hna_local_changed, 0);
153
154         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
155
156         return i;
157 }
158
159 int hna_local_fill_buffer_text(unsigned char *buff, int buff_len)
160 {
161         struct hna_local_entry *hna_local_entry;
162         HASHIT(hashit);
163         int bytes_written = 0;
164         unsigned long flags;
165
166         spin_lock_irqsave(&hna_local_hash_lock, flags);
167
168         while (hash_iterate(hna_local_hash, &hashit)) {
169
170                 if (buff_len < bytes_written + ETH_STR_LEN + 4)
171                         break;
172
173                 hna_local_entry = hashit.bucket->data;
174
175                 bytes_written += snprintf(buff + bytes_written, ETH_STR_LEN + 4,
176                                           " * %02x:%02x:%02x:%02x:%02x:%02x\n",
177                                           hna_local_entry->addr[0],
178                                           hna_local_entry->addr[1],
179                                           hna_local_entry->addr[2],
180                                           hna_local_entry->addr[3],
181                                           hna_local_entry->addr[4],
182                                           hna_local_entry->addr[5]);
183         }
184
185         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
186
187         return bytes_written;
188 }
189
190 static void _hna_local_del(void *data)
191 {
192         kfree(data);
193         num_hna--;
194         atomic_set(&hna_local_changed, 1);
195 }
196
197 static void hna_local_del(struct hna_local_entry *hna_local_entry,
198                           char *message)
199 {
200         bat_dbg(DBG_ROUTES, "Deleting local hna entry (%pM): %s \n",
201                 hna_local_entry->addr, message);
202
203         hash_remove(hna_local_hash, hna_local_entry->addr);
204         _hna_local_del(hna_local_entry);
205 }
206
207 void hna_local_remove(uint8_t *addr, char *message)
208 {
209         struct hna_local_entry *hna_local_entry;
210         unsigned long flags;
211
212         spin_lock_irqsave(&hna_local_hash_lock, flags);
213
214         hna_local_entry = (struct hna_local_entry *)
215                 hash_find(hna_local_hash, addr);
216         if (hna_local_entry)
217                 hna_local_del(hna_local_entry, message);
218
219         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
220 }
221
222 void hna_local_purge(struct work_struct *work)
223 {
224         struct hna_local_entry *hna_local_entry;
225         HASHIT(hashit);
226         unsigned long flags;
227         unsigned long timeout;
228
229         spin_lock_irqsave(&hna_local_hash_lock, flags);
230
231         while (hash_iterate(hna_local_hash, &hashit)) {
232                 hna_local_entry = hashit.bucket->data;
233
234                 timeout = hna_local_entry->last_seen +
235                         ((LOCAL_HNA_TIMEOUT / 1000) * HZ);
236                 if ((!hna_local_entry->never_purge) &&
237                     time_after(jiffies, timeout))
238                         hna_local_del(hna_local_entry, "address timed out");
239         }
240
241         spin_unlock_irqrestore(&hna_local_hash_lock, flags);
242         hna_local_start_timer();
243 }
244
245 void hna_local_free(void)
246 {
247         if (!hna_local_hash)
248                 return;
249
250         cancel_delayed_work_sync(&hna_local_purge_wq);
251         hash_delete(hna_local_hash, _hna_local_del);
252         hna_local_hash = NULL;
253 }
254
255 int hna_global_init(void)
256 {
257         if (hna_global_hash)
258                 return 1;
259
260         hna_global_hash = hash_new(128, compare_orig, choose_orig);
261
262         if (!hna_global_hash)
263                 return 0;
264
265         return 1;
266 }
267
268 void hna_global_add_orig(struct orig_node *orig_node,
269                          unsigned char *hna_buff, int hna_buff_len)
270 {
271         struct hna_global_entry *hna_global_entry;
272         struct hna_local_entry *hna_local_entry;
273         struct hashtable_t *swaphash;
274         int hna_buff_count = 0;
275         unsigned long flags;
276         unsigned char *hna_ptr;
277
278         while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) {
279                 spin_lock_irqsave(&hna_global_hash_lock, flags);
280
281                 hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
282                 hna_global_entry = (struct hna_global_entry *)
283                         hash_find(hna_global_hash, hna_ptr);
284
285                 if (hna_global_entry == NULL) {
286                         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
287
288                         hna_global_entry =
289                                 kmalloc(sizeof(struct hna_global_entry),
290                                         GFP_ATOMIC);
291
292                         if (!hna_global_entry)
293                                 break;
294
295                         memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN);
296
297                         bat_dbg(DBG_ROUTES,
298                                 "Creating new global hna entry: %pM (via %pM)\n",
299                                 hna_global_entry->addr, orig_node->orig);
300
301                         spin_lock_irqsave(&hna_global_hash_lock, flags);
302                         hash_add(hna_global_hash, hna_global_entry);
303
304                 }
305
306                 hna_global_entry->orig_node = orig_node;
307                 spin_unlock_irqrestore(&hna_global_hash_lock, flags);
308
309                 /* remove address from local hash if present */
310                 spin_lock_irqsave(&hna_local_hash_lock, flags);
311
312                 hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN);
313                 hna_local_entry = (struct hna_local_entry *)
314                         hash_find(hna_local_hash, hna_ptr);
315
316                 if (hna_local_entry != NULL)
317                         hna_local_del(hna_local_entry, "global hna received");
318
319                 spin_unlock_irqrestore(&hna_local_hash_lock, flags);
320
321                 hna_buff_count++;
322         }
323
324         /* initialize, and overwrite if malloc succeeds */
325         orig_node->hna_buff = NULL;
326         orig_node->hna_buff_len = 0;
327
328         if (hna_buff_len > 0) {
329                 orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC);
330                 if (orig_node->hna_buff) {
331                         memcpy(orig_node->hna_buff, hna_buff, hna_buff_len);
332                         orig_node->hna_buff_len = hna_buff_len;
333                 }
334         }
335
336         spin_lock_irqsave(&hna_global_hash_lock, flags);
337
338         if (hna_global_hash->elements * 4 > hna_global_hash->size) {
339                 swaphash = hash_resize(hna_global_hash,
340                                        hna_global_hash->size * 2);
341
342                 if (swaphash == NULL)
343                         printk(KERN_ERR "batman-adv:Couldn't resize global hna hash table \n");
344                 else
345                         hna_global_hash = swaphash;
346         }
347
348         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
349 }
350
351 int hna_global_fill_buffer_text(unsigned char *buff, int buff_len)
352 {
353         struct hna_global_entry *hna_global_entry;
354         HASHIT(hashit);
355         int bytes_written = 0;
356         unsigned long flags;
357
358         spin_lock_irqsave(&hna_global_hash_lock, flags);
359
360         while (hash_iterate(hna_global_hash, &hashit)) {
361                 if (buff_len < bytes_written + (2 * ETH_STR_LEN) + 10)
362                         break;
363
364                 hna_global_entry = hashit.bucket->data;
365
366                 bytes_written += snprintf(buff + bytes_written,
367                                           (2 * ETH_STR_LEN) + 10,
368                                           " * %02x:%02x:%02x:%02x:%02x:%02x via %02x:%02x:%02x:%02x:%02x:%02x \n",
369                                           hna_global_entry->addr[0],
370                                           hna_global_entry->addr[1],
371                                           hna_global_entry->addr[2],
372                                           hna_global_entry->addr[3],
373                                           hna_global_entry->addr[4],
374                                           hna_global_entry->addr[5],
375                                           hna_global_entry->orig_node->orig[0],
376                                           hna_global_entry->orig_node->orig[1],
377                                           hna_global_entry->orig_node->orig[2],
378                                           hna_global_entry->orig_node->orig[3],
379                                           hna_global_entry->orig_node->orig[4],
380                                           hna_global_entry->orig_node->orig[5]);
381         }
382
383         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
384
385         return bytes_written;
386 }
387
388 void _hna_global_del_orig(struct hna_global_entry *hna_global_entry,
389                           char *message)
390 {
391         bat_dbg(DBG_ROUTES, "Deleting global hna entry %pM (via %pM): %s \n",
392                 hna_global_entry->addr, hna_global_entry->orig_node->orig,
393                 message);
394
395         hash_remove(hna_global_hash, hna_global_entry->addr);
396         kfree(hna_global_entry);
397 }
398
399 void hna_global_del_orig(struct orig_node *orig_node, char *message)
400 {
401         struct hna_global_entry *hna_global_entry;
402         int hna_buff_count = 0;
403         unsigned long flags;
404         unsigned char *hna_ptr;
405
406         if (orig_node->hna_buff_len == 0)
407                 return;
408
409         spin_lock_irqsave(&hna_global_hash_lock, flags);
410
411         while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) {
412                 hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN);
413                 hna_global_entry = (struct hna_global_entry *)
414                         hash_find(hna_global_hash, hna_ptr);
415
416                 if ((hna_global_entry != NULL) &&
417                     (hna_global_entry->orig_node == orig_node))
418                         _hna_global_del_orig(hna_global_entry, message);
419
420                 hna_buff_count++;
421         }
422
423         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
424
425         orig_node->hna_buff_len = 0;
426         kfree(orig_node->hna_buff);
427         orig_node->hna_buff = NULL;
428 }
429
430 static void hna_global_del(void *data)
431 {
432         kfree(data);
433 }
434
435 void hna_global_free(void)
436 {
437         if (!hna_global_hash)
438                 return;
439
440         hash_delete(hna_global_hash, hna_global_del);
441         hna_global_hash = NULL;
442 }
443
444 struct orig_node *transtable_search(uint8_t *addr)
445 {
446         struct hna_global_entry *hna_global_entry;
447         unsigned long flags;
448
449         spin_lock_irqsave(&hna_global_hash_lock, flags);
450         hna_global_entry = (struct hna_global_entry *)
451                 hash_find(hna_global_hash, addr);
452         spin_unlock_irqrestore(&hna_global_hash_lock, flags);
453
454         if (hna_global_entry == NULL)
455                 return NULL;
456
457         return hna_global_entry->orig_node;
458 }