batman-adv: protect bonding with rcu locks
[deliverable/linux.git] / net / batman-adv / originator.c
CommitLineData
c6c8fea2 1/*
64afe353 2 * Copyright (C) 2009-2011 B.A.T.M.A.N. contributors:
c6c8fea2
SE
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/* increase the reference counter for this originator */
23
24#include "main.h"
25#include "originator.h"
26#include "hash.h"
27#include "translation-table.h"
28#include "routing.h"
29#include "gateway_client.h"
30#include "hard-interface.h"
31#include "unicast.h"
32#include "soft-interface.h"
33
34static void purge_orig(struct work_struct *work);
35
36static void start_purge_timer(struct bat_priv *bat_priv)
37{
38 INIT_DELAYED_WORK(&bat_priv->orig_work, purge_orig);
39 queue_delayed_work(bat_event_workqueue, &bat_priv->orig_work, 1 * HZ);
40}
41
42int originator_init(struct bat_priv *bat_priv)
43{
44 if (bat_priv->orig_hash)
45 return 1;
46
47 spin_lock_bh(&bat_priv->orig_hash_lock);
48 bat_priv->orig_hash = hash_new(1024);
49
50 if (!bat_priv->orig_hash)
51 goto err;
52
53 spin_unlock_bh(&bat_priv->orig_hash_lock);
54 start_purge_timer(bat_priv);
55 return 1;
56
57err:
58 spin_unlock_bh(&bat_priv->orig_hash_lock);
59 return 0;
60}
61
a8e7f4bc
ML
62void neigh_node_free_ref(struct kref *refcount)
63{
64 struct neigh_node *neigh_node;
65
66 neigh_node = container_of(refcount, struct neigh_node, refcount);
67 kfree(neigh_node);
68}
69
f987ed6e
ML
70static void neigh_node_free_rcu(struct rcu_head *rcu)
71{
72 struct neigh_node *neigh_node;
73
74 neigh_node = container_of(rcu, struct neigh_node, rcu);
75 kref_put(&neigh_node->refcount, neigh_node_free_ref);
76}
77
a4c135c5
SW
78void neigh_node_free_rcu_bond(struct rcu_head *rcu)
79{
80 struct neigh_node *neigh_node;
81
82 neigh_node = container_of(rcu, struct neigh_node, rcu_bond);
83 kref_put(&neigh_node->refcount, neigh_node_free_ref);
84}
85
a8e7f4bc
ML
86struct neigh_node *create_neighbor(struct orig_node *orig_node,
87 struct orig_node *orig_neigh_node,
88 uint8_t *neigh,
89 struct batman_if *if_incoming)
c6c8fea2
SE
90{
91 struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
92 struct neigh_node *neigh_node;
93
94 bat_dbg(DBG_BATMAN, bat_priv,
95 "Creating new last-hop neighbor of originator\n");
96
97 neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC);
98 if (!neigh_node)
99 return NULL;
100
9591a79f 101 INIT_HLIST_NODE(&neigh_node->list);
a4c135c5 102 INIT_LIST_HEAD(&neigh_node->bonding_list);
c6c8fea2
SE
103
104 memcpy(neigh_node->addr, neigh, ETH_ALEN);
105 neigh_node->orig_node = orig_neigh_node;
106 neigh_node->if_incoming = if_incoming;
a8e7f4bc 107 kref_init(&neigh_node->refcount);
c6c8fea2 108
f987ed6e
ML
109 spin_lock_bh(&orig_node->neigh_list_lock);
110 hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
111 spin_unlock_bh(&orig_node->neigh_list_lock);
c6c8fea2
SE
112 return neigh_node;
113}
114
16b1aba8 115void orig_node_free_ref(struct kref *refcount)
c6c8fea2 116{
9591a79f 117 struct hlist_node *node, *node_tmp;
a4c135c5 118 struct neigh_node *neigh_node, *tmp_neigh_node;
16b1aba8
ML
119 struct orig_node *orig_node;
120
121 orig_node = container_of(refcount, struct orig_node, refcount);
c6c8fea2 122
f987ed6e
ML
123 spin_lock_bh(&orig_node->neigh_list_lock);
124
a4c135c5
SW
125 /* for all bonding members ... */
126 list_for_each_entry_safe(neigh_node, tmp_neigh_node,
127 &orig_node->bond_list, bonding_list) {
128 list_del_rcu(&neigh_node->bonding_list);
129 call_rcu(&neigh_node->rcu_bond, neigh_node_free_rcu_bond);
130 }
131
c6c8fea2 132 /* for all neighbors towards this originator ... */
9591a79f
ML
133 hlist_for_each_entry_safe(neigh_node, node, node_tmp,
134 &orig_node->neigh_list, list) {
f987ed6e
ML
135 hlist_del_rcu(&neigh_node->list);
136 call_rcu(&neigh_node->rcu, neigh_node_free_rcu);
c6c8fea2
SE
137 }
138
f987ed6e
ML
139 spin_unlock_bh(&orig_node->neigh_list_lock);
140
c6c8fea2 141 frag_list_free(&orig_node->frag_list);
16b1aba8
ML
142 hna_global_del_orig(orig_node->bat_priv, orig_node,
143 "originator timed out");
c6c8fea2
SE
144
145 kfree(orig_node->bcast_own);
146 kfree(orig_node->bcast_own_sum);
147 kfree(orig_node);
148}
149
150void originator_free(struct bat_priv *bat_priv)
151{
16b1aba8
ML
152 struct hashtable_t *hash = bat_priv->orig_hash;
153 struct hlist_node *walk, *safe;
154 struct hlist_head *head;
155 struct element_t *bucket;
156 spinlock_t *list_lock; /* spinlock to protect write access */
157 struct orig_node *orig_node;
158 int i;
159
160 if (!hash)
c6c8fea2
SE
161 return;
162
163 cancel_delayed_work_sync(&bat_priv->orig_work);
164
165 spin_lock_bh(&bat_priv->orig_hash_lock);
c6c8fea2 166 bat_priv->orig_hash = NULL;
16b1aba8
ML
167
168 for (i = 0; i < hash->size; i++) {
169 head = &hash->table[i];
170 list_lock = &hash->list_locks[i];
171
172 spin_lock_bh(list_lock);
173 hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) {
174 orig_node = bucket->data;
175
176 hlist_del_rcu(walk);
177 call_rcu(&bucket->rcu, bucket_free_rcu);
178 kref_put(&orig_node->refcount, orig_node_free_ref);
179 }
180 spin_unlock_bh(list_lock);
181 }
182
183 hash_destroy(hash);
c6c8fea2
SE
184 spin_unlock_bh(&bat_priv->orig_hash_lock);
185}
186
16b1aba8
ML
187static void bucket_free_orig_rcu(struct rcu_head *rcu)
188{
189 struct element_t *bucket;
190 struct orig_node *orig_node;
191
192 bucket = container_of(rcu, struct element_t, rcu);
193 orig_node = bucket->data;
194
195 kref_put(&orig_node->refcount, orig_node_free_ref);
196 kfree(bucket);
197}
198
c6c8fea2
SE
199/* this function finds or creates an originator entry for the given
200 * address if it does not exits */
201struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
202{
203 struct orig_node *orig_node;
204 int size;
205 int hash_added;
206
fb778ea1 207 rcu_read_lock();
c6c8fea2
SE
208 orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
209 compare_orig, choose_orig,
210 addr));
fb778ea1 211 rcu_read_unlock();
c6c8fea2 212
16b1aba8
ML
213 if (orig_node) {
214 kref_get(&orig_node->refcount);
c6c8fea2 215 return orig_node;
16b1aba8 216 }
c6c8fea2
SE
217
218 bat_dbg(DBG_BATMAN, bat_priv,
219 "Creating new originator: %pM\n", addr);
220
221 orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC);
222 if (!orig_node)
223 return NULL;
224
9591a79f 225 INIT_HLIST_HEAD(&orig_node->neigh_list);
a4c135c5 226 INIT_LIST_HEAD(&orig_node->bond_list);
2ae2daf6 227 spin_lock_init(&orig_node->ogm_cnt_lock);
f987ed6e 228 spin_lock_init(&orig_node->neigh_list_lock);
16b1aba8 229 kref_init(&orig_node->refcount);
c6c8fea2 230
16b1aba8 231 orig_node->bat_priv = bat_priv;
c6c8fea2
SE
232 memcpy(orig_node->orig, addr, ETH_ALEN);
233 orig_node->router = NULL;
234 orig_node->hna_buff = NULL;
235 orig_node->bcast_seqno_reset = jiffies - 1
236 - msecs_to_jiffies(RESET_PROTECTION_MS);
237 orig_node->batman_seqno_reset = jiffies - 1
238 - msecs_to_jiffies(RESET_PROTECTION_MS);
239
a4c135c5
SW
240 atomic_set(&orig_node->bond_candidates, 0);
241
c6c8fea2
SE
242 size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS;
243
244 orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
245 if (!orig_node->bcast_own)
246 goto free_orig_node;
247
248 size = bat_priv->num_ifaces * sizeof(uint8_t);
249 orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
250
251 INIT_LIST_HEAD(&orig_node->frag_list);
252 orig_node->last_frag_packet = 0;
253
254 if (!orig_node->bcast_own_sum)
255 goto free_bcast_own;
256
257 hash_added = hash_add(bat_priv->orig_hash, compare_orig, choose_orig,
258 orig_node);
259 if (hash_added < 0)
260 goto free_bcast_own_sum;
261
16b1aba8
ML
262 /* extra reference for return */
263 kref_get(&orig_node->refcount);
c6c8fea2
SE
264 return orig_node;
265free_bcast_own_sum:
266 kfree(orig_node->bcast_own_sum);
267free_bcast_own:
268 kfree(orig_node->bcast_own);
269free_orig_node:
270 kfree(orig_node);
271 return NULL;
272}
273
274static bool purge_orig_neighbors(struct bat_priv *bat_priv,
275 struct orig_node *orig_node,
276 struct neigh_node **best_neigh_node)
277{
9591a79f 278 struct hlist_node *node, *node_tmp;
c6c8fea2
SE
279 struct neigh_node *neigh_node;
280 bool neigh_purged = false;
281
282 *best_neigh_node = NULL;
283
f987ed6e
ML
284 spin_lock_bh(&orig_node->neigh_list_lock);
285
c6c8fea2 286 /* for all neighbors towards this originator ... */
9591a79f
ML
287 hlist_for_each_entry_safe(neigh_node, node, node_tmp,
288 &orig_node->neigh_list, list) {
c6c8fea2
SE
289
290 if ((time_after(jiffies,
291 neigh_node->last_valid + PURGE_TIMEOUT * HZ)) ||
292 (neigh_node->if_incoming->if_status == IF_INACTIVE) ||
1a241a57 293 (neigh_node->if_incoming->if_status == IF_NOT_IN_USE) ||
c6c8fea2
SE
294 (neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) {
295
1a241a57
ML
296 if ((neigh_node->if_incoming->if_status ==
297 IF_INACTIVE) ||
298 (neigh_node->if_incoming->if_status ==
299 IF_NOT_IN_USE) ||
300 (neigh_node->if_incoming->if_status ==
301 IF_TO_BE_REMOVED))
c6c8fea2
SE
302 bat_dbg(DBG_BATMAN, bat_priv,
303 "neighbor purge: originator %pM, "
304 "neighbor: %pM, iface: %s\n",
305 orig_node->orig, neigh_node->addr,
306 neigh_node->if_incoming->net_dev->name);
307 else
308 bat_dbg(DBG_BATMAN, bat_priv,
309 "neighbor timeout: originator %pM, "
310 "neighbor: %pM, last_valid: %lu\n",
311 orig_node->orig, neigh_node->addr,
312 (neigh_node->last_valid / HZ));
313
314 neigh_purged = true;
9591a79f 315
f987ed6e 316 hlist_del_rcu(&neigh_node->list);
a4c135c5 317 bonding_candidate_del(orig_node, neigh_node);
f987ed6e 318 call_rcu(&neigh_node->rcu, neigh_node_free_rcu);
c6c8fea2
SE
319 } else {
320 if ((!*best_neigh_node) ||
321 (neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
322 *best_neigh_node = neigh_node;
323 }
324 }
f987ed6e
ML
325
326 spin_unlock_bh(&orig_node->neigh_list_lock);
c6c8fea2
SE
327 return neigh_purged;
328}
329
330static bool purge_orig_node(struct bat_priv *bat_priv,
331 struct orig_node *orig_node)
332{
333 struct neigh_node *best_neigh_node;
334
335 if (time_after(jiffies,
336 orig_node->last_valid + 2 * PURGE_TIMEOUT * HZ)) {
337
338 bat_dbg(DBG_BATMAN, bat_priv,
339 "Originator timeout: originator %pM, last_valid %lu\n",
340 orig_node->orig, (orig_node->last_valid / HZ));
341 return true;
342 } else {
343 if (purge_orig_neighbors(bat_priv, orig_node,
344 &best_neigh_node)) {
345 update_routes(bat_priv, orig_node,
346 best_neigh_node,
347 orig_node->hna_buff,
348 orig_node->hna_buff_len);
c6c8fea2
SE
349 }
350 }
351
352 return false;
353}
354
355static void _purge_orig(struct bat_priv *bat_priv)
356{
357 struct hashtable_t *hash = bat_priv->orig_hash;
358 struct hlist_node *walk, *safe;
359 struct hlist_head *head;
360 struct element_t *bucket;
fb778ea1 361 spinlock_t *list_lock; /* spinlock to protect write access */
c6c8fea2
SE
362 struct orig_node *orig_node;
363 int i;
364
365 if (!hash)
366 return;
367
368 spin_lock_bh(&bat_priv->orig_hash_lock);
369
370 /* for all origins... */
371 for (i = 0; i < hash->size; i++) {
372 head = &hash->table[i];
fb778ea1 373 list_lock = &hash->list_locks[i];
c6c8fea2 374
fb778ea1 375 spin_lock_bh(list_lock);
c6c8fea2
SE
376 hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) {
377 orig_node = bucket->data;
378
379 if (purge_orig_node(bat_priv, orig_node)) {
380 if (orig_node->gw_flags)
381 gw_node_delete(bat_priv, orig_node);
fb778ea1 382 hlist_del_rcu(walk);
16b1aba8 383 call_rcu(&bucket->rcu, bucket_free_orig_rcu);
fb778ea1 384 continue;
c6c8fea2
SE
385 }
386
387 if (time_after(jiffies, orig_node->last_frag_packet +
388 msecs_to_jiffies(FRAG_TIMEOUT)))
389 frag_list_free(&orig_node->frag_list);
390 }
fb778ea1 391 spin_unlock_bh(list_lock);
c6c8fea2
SE
392 }
393
394 spin_unlock_bh(&bat_priv->orig_hash_lock);
395
396 gw_node_purge(bat_priv);
397 gw_election(bat_priv);
398
399 softif_neigh_purge(bat_priv);
400}
401
402static void purge_orig(struct work_struct *work)
403{
404 struct delayed_work *delayed_work =
405 container_of(work, struct delayed_work, work);
406 struct bat_priv *bat_priv =
407 container_of(delayed_work, struct bat_priv, orig_work);
408
409 _purge_orig(bat_priv);
410 start_purge_timer(bat_priv);
411}
412
413void purge_orig_ref(struct bat_priv *bat_priv)
414{
415 _purge_orig(bat_priv);
416}
417
418int orig_seq_print_text(struct seq_file *seq, void *offset)
419{
420 struct net_device *net_dev = (struct net_device *)seq->private;
421 struct bat_priv *bat_priv = netdev_priv(net_dev);
422 struct hashtable_t *hash = bat_priv->orig_hash;
9591a79f 423 struct hlist_node *walk, *node;
c6c8fea2
SE
424 struct hlist_head *head;
425 struct element_t *bucket;
426 struct orig_node *orig_node;
427 struct neigh_node *neigh_node;
428 int batman_count = 0;
429 int last_seen_secs;
430 int last_seen_msecs;
431 int i;
432
433 if ((!bat_priv->primary_if) ||
434 (bat_priv->primary_if->if_status != IF_ACTIVE)) {
435 if (!bat_priv->primary_if)
436 return seq_printf(seq, "BATMAN mesh %s disabled - "
437 "please specify interfaces to enable it\n",
438 net_dev->name);
439
440 return seq_printf(seq, "BATMAN mesh %s "
441 "disabled - primary interface not active\n",
442 net_dev->name);
443 }
444
445 seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
446 SOURCE_VERSION, REVISION_VERSION_STR,
447 bat_priv->primary_if->net_dev->name,
448 bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
449 seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
450 "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
451 "outgoingIF", "Potential nexthops");
452
453 spin_lock_bh(&bat_priv->orig_hash_lock);
454
455 for (i = 0; i < hash->size; i++) {
456 head = &hash->table[i];
457
fb778ea1
ML
458 rcu_read_lock();
459 hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
c6c8fea2
SE
460 orig_node = bucket->data;
461
462 if (!orig_node->router)
463 continue;
464
465 if (orig_node->router->tq_avg == 0)
466 continue;
467
468 last_seen_secs = jiffies_to_msecs(jiffies -
469 orig_node->last_valid) / 1000;
470 last_seen_msecs = jiffies_to_msecs(jiffies -
471 orig_node->last_valid) % 1000;
472
473 neigh_node = orig_node->router;
474 seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:",
475 orig_node->orig, last_seen_secs,
476 last_seen_msecs, neigh_node->tq_avg,
477 neigh_node->addr,
478 neigh_node->if_incoming->net_dev->name);
479
f987ed6e
ML
480 hlist_for_each_entry_rcu(neigh_node, node,
481 &orig_node->neigh_list, list) {
c6c8fea2
SE
482 seq_printf(seq, " %pM (%3i)", neigh_node->addr,
483 neigh_node->tq_avg);
484 }
485
486 seq_printf(seq, "\n");
487 batman_count++;
488 }
fb778ea1 489 rcu_read_unlock();
c6c8fea2
SE
490 }
491
492 spin_unlock_bh(&bat_priv->orig_hash_lock);
493
494 if ((batman_count == 0))
495 seq_printf(seq, "No batman nodes in range ...\n");
496
497 return 0;
498}
499
500static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
501{
502 void *data_ptr;
503
504 data_ptr = kmalloc(max_if_num * sizeof(unsigned long) * NUM_WORDS,
505 GFP_ATOMIC);
506 if (!data_ptr) {
507 pr_err("Can't resize orig: out of memory\n");
508 return -1;
509 }
510
511 memcpy(data_ptr, orig_node->bcast_own,
512 (max_if_num - 1) * sizeof(unsigned long) * NUM_WORDS);
513 kfree(orig_node->bcast_own);
514 orig_node->bcast_own = data_ptr;
515
516 data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
517 if (!data_ptr) {
518 pr_err("Can't resize orig: out of memory\n");
519 return -1;
520 }
521
522 memcpy(data_ptr, orig_node->bcast_own_sum,
523 (max_if_num - 1) * sizeof(uint8_t));
524 kfree(orig_node->bcast_own_sum);
525 orig_node->bcast_own_sum = data_ptr;
526
527 return 0;
528}
529
530int orig_hash_add_if(struct batman_if *batman_if, int max_if_num)
531{
532 struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface);
533 struct hashtable_t *hash = bat_priv->orig_hash;
534 struct hlist_node *walk;
535 struct hlist_head *head;
536 struct element_t *bucket;
537 struct orig_node *orig_node;
2ae2daf6 538 int i, ret;
c6c8fea2
SE
539
540 /* resize all orig nodes because orig_node->bcast_own(_sum) depend on
541 * if_num */
542 spin_lock_bh(&bat_priv->orig_hash_lock);
543
544 for (i = 0; i < hash->size; i++) {
545 head = &hash->table[i];
546
fb778ea1
ML
547 rcu_read_lock();
548 hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
c6c8fea2
SE
549 orig_node = bucket->data;
550
2ae2daf6
ML
551 spin_lock_bh(&orig_node->ogm_cnt_lock);
552 ret = orig_node_add_if(orig_node, max_if_num);
553 spin_unlock_bh(&orig_node->ogm_cnt_lock);
554
555 if (ret == -1)
c6c8fea2
SE
556 goto err;
557 }
fb778ea1 558 rcu_read_unlock();
c6c8fea2
SE
559 }
560
561 spin_unlock_bh(&bat_priv->orig_hash_lock);
562 return 0;
563
564err:
fb778ea1 565 rcu_read_unlock();
c6c8fea2
SE
566 spin_unlock_bh(&bat_priv->orig_hash_lock);
567 return -ENOMEM;
568}
569
570static int orig_node_del_if(struct orig_node *orig_node,
571 int max_if_num, int del_if_num)
572{
573 void *data_ptr = NULL;
574 int chunk_size;
575
576 /* last interface was removed */
577 if (max_if_num == 0)
578 goto free_bcast_own;
579
580 chunk_size = sizeof(unsigned long) * NUM_WORDS;
581 data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
582 if (!data_ptr) {
583 pr_err("Can't resize orig: out of memory\n");
584 return -1;
585 }
586
587 /* copy first part */
588 memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size);
589
590 /* copy second part */
591 memcpy(data_ptr + del_if_num * chunk_size,
592 orig_node->bcast_own + ((del_if_num + 1) * chunk_size),
593 (max_if_num - del_if_num) * chunk_size);
594
595free_bcast_own:
596 kfree(orig_node->bcast_own);
597 orig_node->bcast_own = data_ptr;
598
599 if (max_if_num == 0)
600 goto free_own_sum;
601
602 data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
603 if (!data_ptr) {
604 pr_err("Can't resize orig: out of memory\n");
605 return -1;
606 }
607
608 memcpy(data_ptr, orig_node->bcast_own_sum,
609 del_if_num * sizeof(uint8_t));
610
611 memcpy(data_ptr + del_if_num * sizeof(uint8_t),
612 orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)),
613 (max_if_num - del_if_num) * sizeof(uint8_t));
614
615free_own_sum:
616 kfree(orig_node->bcast_own_sum);
617 orig_node->bcast_own_sum = data_ptr;
618
619 return 0;
620}
621
622int orig_hash_del_if(struct batman_if *batman_if, int max_if_num)
623{
624 struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface);
625 struct hashtable_t *hash = bat_priv->orig_hash;
626 struct hlist_node *walk;
627 struct hlist_head *head;
628 struct element_t *bucket;
629 struct batman_if *batman_if_tmp;
630 struct orig_node *orig_node;
631 int i, ret;
632
633 /* resize all orig nodes because orig_node->bcast_own(_sum) depend on
634 * if_num */
635 spin_lock_bh(&bat_priv->orig_hash_lock);
636
637 for (i = 0; i < hash->size; i++) {
638 head = &hash->table[i];
639
fb778ea1
ML
640 rcu_read_lock();
641 hlist_for_each_entry_rcu(bucket, walk, head, hlist) {
c6c8fea2
SE
642 orig_node = bucket->data;
643
2ae2daf6 644 spin_lock_bh(&orig_node->ogm_cnt_lock);
c6c8fea2
SE
645 ret = orig_node_del_if(orig_node, max_if_num,
646 batman_if->if_num);
2ae2daf6 647 spin_unlock_bh(&orig_node->ogm_cnt_lock);
c6c8fea2
SE
648
649 if (ret == -1)
650 goto err;
651 }
fb778ea1 652 rcu_read_unlock();
c6c8fea2
SE
653 }
654
655 /* renumber remaining batman interfaces _inside_ of orig_hash_lock */
656 rcu_read_lock();
657 list_for_each_entry_rcu(batman_if_tmp, &if_list, list) {
658 if (batman_if_tmp->if_status == IF_NOT_IN_USE)
659 continue;
660
661 if (batman_if == batman_if_tmp)
662 continue;
663
664 if (batman_if->soft_iface != batman_if_tmp->soft_iface)
665 continue;
666
667 if (batman_if_tmp->if_num > batman_if->if_num)
668 batman_if_tmp->if_num--;
669 }
670 rcu_read_unlock();
671
672 batman_if->if_num = -1;
673 spin_unlock_bh(&bat_priv->orig_hash_lock);
674 return 0;
675
676err:
fb778ea1 677 rcu_read_unlock();
c6c8fea2
SE
678 spin_unlock_bh(&bat_priv->orig_hash_lock);
679 return -ENOMEM;
680}
This page took 0.08434 seconds and 5 git commands to generate.