diff options
Diffstat (limited to 'net/batman-adv/translation-table.c')
-rw-r--r-- | net/batman-adv/translation-table.c | 75 |
1 files changed, 52 insertions, 23 deletions
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 99dd8f75b3ff..eb8490e504e2 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c | |||
@@ -152,6 +152,8 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu) | |||
152 | static void | 152 | static void |
153 | batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) | 153 | batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) |
154 | { | 154 | { |
155 | if (!atomic_dec_and_test(&orig_entry->refcount)) | ||
156 | return; | ||
155 | /* to avoid race conditions, immediately decrease the tt counter */ | 157 | /* to avoid race conditions, immediately decrease the tt counter */ |
156 | atomic_dec(&orig_entry->orig_node->tt_size); | 158 | atomic_dec(&orig_entry->orig_node->tt_size); |
157 | call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); | 159 | call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); |
@@ -625,50 +627,82 @@ static void batadv_tt_changes_list_free(struct batadv_priv *bat_priv) | |||
625 | spin_unlock_bh(&bat_priv->tt_changes_list_lock); | 627 | spin_unlock_bh(&bat_priv->tt_changes_list_lock); |
626 | } | 628 | } |
627 | 629 | ||
628 | /* find out if an orig_node is already in the list of a tt_global_entry. | 630 | /* retrieves the orig_tt_list_entry belonging to orig_node from the |
629 | * returns 1 if found, 0 otherwise | 631 | * batadv_tt_global_entry list |
632 | * | ||
633 | * returns it with an increased refcounter, NULL if not found | ||
630 | */ | 634 | */ |
631 | static bool | 635 | static struct batadv_tt_orig_list_entry * |
632 | batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry, | 636 | batadv_tt_global_orig_entry_find(const struct batadv_tt_global_entry *entry, |
633 | const struct batadv_orig_node *orig_node) | 637 | const struct batadv_orig_node *orig_node) |
634 | { | 638 | { |
635 | struct batadv_tt_orig_list_entry *tmp_orig_entry; | 639 | struct batadv_tt_orig_list_entry *tmp_orig_entry, *orig_entry = NULL; |
636 | const struct hlist_head *head; | 640 | const struct hlist_head *head; |
637 | struct hlist_node *node; | 641 | struct hlist_node *node; |
638 | bool found = false; | ||
639 | 642 | ||
640 | rcu_read_lock(); | 643 | rcu_read_lock(); |
641 | head = &entry->orig_list; | 644 | head = &entry->orig_list; |
642 | hlist_for_each_entry_rcu(tmp_orig_entry, node, head, list) { | 645 | hlist_for_each_entry_rcu(tmp_orig_entry, node, head, list) { |
643 | if (tmp_orig_entry->orig_node == orig_node) { | 646 | if (tmp_orig_entry->orig_node != orig_node) |
644 | found = true; | 647 | continue; |
645 | break; | 648 | if (!atomic_inc_not_zero(&tmp_orig_entry->refcount)) |
646 | } | 649 | continue; |
650 | |||
651 | orig_entry = tmp_orig_entry; | ||
652 | break; | ||
647 | } | 653 | } |
648 | rcu_read_unlock(); | 654 | rcu_read_unlock(); |
655 | |||
656 | return orig_entry; | ||
657 | } | ||
658 | |||
659 | /* find out if an orig_node is already in the list of a tt_global_entry. | ||
660 | * returns true if found, false otherwise | ||
661 | */ | ||
662 | static bool | ||
663 | batadv_tt_global_entry_has_orig(const struct batadv_tt_global_entry *entry, | ||
664 | const struct batadv_orig_node *orig_node) | ||
665 | { | ||
666 | struct batadv_tt_orig_list_entry *orig_entry; | ||
667 | bool found = false; | ||
668 | |||
669 | orig_entry = batadv_tt_global_orig_entry_find(entry, orig_node); | ||
670 | if (orig_entry) { | ||
671 | found = true; | ||
672 | batadv_tt_orig_list_entry_free_ref(orig_entry); | ||
673 | } | ||
674 | |||
649 | return found; | 675 | return found; |
650 | } | 676 | } |
651 | 677 | ||
652 | static void | 678 | static void |
653 | batadv_tt_global_add_orig_entry(struct batadv_tt_global_entry *tt_global_entry, | 679 | batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global, |
654 | struct batadv_orig_node *orig_node, int ttvn) | 680 | struct batadv_orig_node *orig_node, int ttvn) |
655 | { | 681 | { |
656 | struct batadv_tt_orig_list_entry *orig_entry; | 682 | struct batadv_tt_orig_list_entry *orig_entry; |
657 | 683 | ||
684 | orig_entry = batadv_tt_global_orig_entry_find(tt_global, orig_node); | ||
685 | if (orig_entry) | ||
686 | goto out; | ||
687 | |||
658 | orig_entry = kzalloc(sizeof(*orig_entry), GFP_ATOMIC); | 688 | orig_entry = kzalloc(sizeof(*orig_entry), GFP_ATOMIC); |
659 | if (!orig_entry) | 689 | if (!orig_entry) |
660 | return; | 690 | goto out; |
661 | 691 | ||
662 | INIT_HLIST_NODE(&orig_entry->list); | 692 | INIT_HLIST_NODE(&orig_entry->list); |
663 | atomic_inc(&orig_node->refcount); | 693 | atomic_inc(&orig_node->refcount); |
664 | atomic_inc(&orig_node->tt_size); | 694 | atomic_inc(&orig_node->tt_size); |
665 | orig_entry->orig_node = orig_node; | 695 | orig_entry->orig_node = orig_node; |
666 | orig_entry->ttvn = ttvn; | 696 | orig_entry->ttvn = ttvn; |
697 | atomic_set(&orig_entry->refcount, 2); | ||
667 | 698 | ||
668 | spin_lock_bh(&tt_global_entry->list_lock); | 699 | spin_lock_bh(&tt_global->list_lock); |
669 | hlist_add_head_rcu(&orig_entry->list, | 700 | hlist_add_head_rcu(&orig_entry->list, |
670 | &tt_global_entry->orig_list); | 701 | &tt_global->orig_list); |
671 | spin_unlock_bh(&tt_global_entry->list_lock); | 702 | spin_unlock_bh(&tt_global->list_lock); |
703 | out: | ||
704 | if (orig_entry) | ||
705 | batadv_tt_orig_list_entry_free_ref(orig_entry); | ||
672 | } | 706 | } |
673 | 707 | ||
674 | /* caller must hold orig_node refcount */ | 708 | /* caller must hold orig_node refcount */ |
@@ -709,9 +743,6 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, | |||
709 | batadv_tt_global_entry_free_ref(tt_global_entry); | 743 | batadv_tt_global_entry_free_ref(tt_global_entry); |
710 | goto out_remove; | 744 | goto out_remove; |
711 | } | 745 | } |
712 | |||
713 | batadv_tt_global_add_orig_entry(tt_global_entry, orig_node, | ||
714 | ttvn); | ||
715 | } else { | 746 | } else { |
716 | /* there is already a global entry, use this one. */ | 747 | /* there is already a global entry, use this one. */ |
717 | 748 | ||
@@ -728,11 +759,9 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, | |||
728 | tt_global_entry->roam_at = 0; | 759 | tt_global_entry->roam_at = 0; |
729 | } | 760 | } |
730 | 761 | ||
731 | if (!batadv_tt_global_entry_has_orig(tt_global_entry, | ||
732 | orig_node)) | ||
733 | batadv_tt_global_add_orig_entry(tt_global_entry, | ||
734 | orig_node, ttvn); | ||
735 | } | 762 | } |
763 | /* add the new orig_entry (if needed) */ | ||
764 | batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn); | ||
736 | 765 | ||
737 | batadv_dbg(BATADV_DBG_TT, bat_priv, | 766 | batadv_dbg(BATADV_DBG_TT, bat_priv, |
738 | "Creating new global tt entry: %pM (via %pM)\n", | 767 | "Creating new global tt entry: %pM (via %pM)\n", |