batman-adv: add list of unique single hop neighbors per hard-interface
authorMarek Lindner <mareklindner@neomailbox.ch>
Tue, 4 Aug 2015 13:09:55 +0000 (21:09 +0800)
committerAntonio Quartulli <a@unstable.cc>
Tue, 15 Dec 2015 16:21:41 +0000 (00:21 +0800)
Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
Signed-off-by: Antonio Quartulli <antonio@meshcoding.com>
net/batman-adv/hard-interface.c
net/batman-adv/originator.c
net/batman-adv/originator.h
net/batman-adv/types.h

index a58184fdf5fdc496d33527eb99396376c737bc2e..01acccc4d2185806ae6dd37b2f0f091d9a0b92a0 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/rculist.h>
 #include <linux/rtnetlink.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <net/net_namespace.h>
 
@@ -639,9 +640,12 @@ batadv_hardif_add_interface(struct net_device *net_dev)
                goto free_sysfs;
 
        INIT_LIST_HEAD(&hard_iface->list);
+       INIT_HLIST_HEAD(&hard_iface->neigh_list);
        INIT_WORK(&hard_iface->cleanup_work,
                  batadv_hardif_remove_interface_finish);
 
+       spin_lock_init(&hard_iface->neigh_list_lock);
+
        hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
        if (batadv_is_wifi_netdev(net_dev))
                hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
index 7486df9ed48dbcaaf7ab039767ad38777584cd28..a8671c6567424ccf7bcc2ae4da59a27b8183b0ce 100644 (file)
@@ -201,6 +201,47 @@ void batadv_neigh_ifinfo_free_ref(struct batadv_neigh_ifinfo *neigh_ifinfo)
                call_rcu(&neigh_ifinfo->rcu, batadv_neigh_ifinfo_free_rcu);
 }
 
+/**
+ * batadv_hardif_neigh_free_rcu - free the hardif neigh_node
+ * @rcu: rcu pointer of the neigh_node
+ */
+static void batadv_hardif_neigh_free_rcu(struct rcu_head *rcu)
+{
+       struct batadv_hardif_neigh_node *hardif_neigh;
+
+       hardif_neigh = container_of(rcu, struct batadv_hardif_neigh_node, rcu);
+
+       spin_lock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+       hlist_del_init_rcu(&hardif_neigh->list);
+       spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+
+       batadv_hardif_free_ref_now(hardif_neigh->if_incoming);
+       kfree(hardif_neigh);
+}
+
+/**
+ * batadv_hardif_neigh_free_now - decrement the hardif neighbors refcounter
+ *  and possibly free it (without rcu callback)
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+static void
+batadv_hardif_neigh_free_now(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+       if (atomic_dec_and_test(&hardif_neigh->refcount))
+               batadv_hardif_neigh_free_rcu(&hardif_neigh->rcu);
+}
+
+/**
+ * batadv_hardif_neigh_free_ref - decrement the hardif neighbors refcounter
+ *  and possibly free it
+ * @hardif_neigh: hardif neigh neighbor to free
+ */
+void batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh)
+{
+       if (atomic_dec_and_test(&hardif_neigh->refcount))
+               call_rcu(&hardif_neigh->rcu, batadv_hardif_neigh_free_rcu);
+}
+
 /**
  * batadv_neigh_node_free_rcu - free the neigh_node
  * @rcu: rcu pointer of the neigh_node
@@ -209,6 +250,7 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
 {
        struct hlist_node *node_tmp;
        struct batadv_neigh_node *neigh_node;
+       struct batadv_hardif_neigh_node *hardif_neigh;
        struct batadv_neigh_ifinfo *neigh_ifinfo;
        struct batadv_algo_ops *bao;
 
@@ -220,6 +262,14 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu)
                batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo);
        }
 
+       hardif_neigh = batadv_hardif_neigh_get(neigh_node->if_incoming,
+                                              neigh_node->addr);
+       if (hardif_neigh) {
+               /* batadv_hardif_neigh_get() increases refcount too */
+               batadv_hardif_neigh_free_now(hardif_neigh);
+               batadv_hardif_neigh_free_now(hardif_neigh);
+       }
+
        if (bao->bat_neigh_free)
                bao->bat_neigh_free(neigh_node);
 
@@ -478,6 +528,102 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
        return res;
 }
 
+/**
+ * batadv_hardif_neigh_create - create a hardif neighbour node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,
+                          const u8 *neigh_addr)
+{
+       struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+       spin_lock_bh(&hard_iface->neigh_list_lock);
+
+       /* check if neighbor hasn't been added in the meantime */
+       hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+       if (hardif_neigh)
+               goto out;
+
+       if (!atomic_inc_not_zero(&hard_iface->refcount))
+               goto out;
+
+       hardif_neigh = kzalloc(sizeof(*hardif_neigh), GFP_ATOMIC);
+       if (!hardif_neigh) {
+               batadv_hardif_free_ref(hard_iface);
+               goto out;
+       }
+
+       INIT_HLIST_NODE(&hardif_neigh->list);
+       ether_addr_copy(hardif_neigh->addr, neigh_addr);
+       hardif_neigh->if_incoming = hard_iface;
+       hardif_neigh->last_seen = jiffies;
+
+       atomic_set(&hardif_neigh->refcount, 1);
+
+       hlist_add_head(&hardif_neigh->list, &hard_iface->neigh_list);
+
+out:
+       spin_unlock_bh(&hard_iface->neigh_list_lock);
+       return hardif_neigh;
+}
+
+/**
+ * batadv_hardif_neigh_get_or_create - retrieve or create a hardif neighbour
+ *  node
+ * @hard_iface: the interface this neighbour is connected to
+ * @neigh_addr: the interface address of the neighbour to retrieve
+ *
+ * Returns the hardif neighbour node if found or created or NULL otherwise.
+ */
+static struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,
+                                 const u8 *neigh_addr)
+{
+       struct batadv_hardif_neigh_node *hardif_neigh = NULL;
+
+       /* first check without locking to avoid the overhead */
+       hardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);
+       if (hardif_neigh)
+               return hardif_neigh;
+
+       return batadv_hardif_neigh_create(hard_iface, neigh_addr);
+}
+
+/**
+ * batadv_hardif_neigh_get - retrieve a hardif neighbour from the list
+ * @hard_iface: the interface where this neighbour is connected to
+ * @neigh_addr: the address of the neighbour
+ *
+ * Looks for and possibly returns a neighbour belonging to this hard interface.
+ * Returns NULL if the neighbour is not found.
+ */
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+                       const u8 *neigh_addr)
+{
+       struct batadv_hardif_neigh_node *tmp_hardif_neigh, *hardif_neigh = NULL;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tmp_hardif_neigh,
+                                &hard_iface->neigh_list, list) {
+               if (!batadv_compare_eth(tmp_hardif_neigh->addr, neigh_addr))
+                       continue;
+
+               if (!atomic_inc_not_zero(&tmp_hardif_neigh->refcount))
+                       continue;
+
+               hardif_neigh = tmp_hardif_neigh;
+               break;
+       }
+       rcu_read_unlock();
+
+       return hardif_neigh;
+}
+
 /**
  * batadv_neigh_node_new - create and init a new neigh_node object
  * @orig_node: originator object representing the neighbour
@@ -493,11 +639,17 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
                      const u8 *neigh_addr)
 {
        struct batadv_neigh_node *neigh_node;
+       struct batadv_hardif_neigh_node *hardif_neigh = NULL;
 
        neigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);
        if (neigh_node)
                goto out;
 
+       hardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,
+                                                        neigh_addr);
+       if (!hardif_neigh)
+               goto out;
+
        neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
        if (!neigh_node)
                goto out;
@@ -523,11 +675,16 @@ batadv_neigh_node_new(struct batadv_orig_node *orig_node,
        hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
        spin_unlock_bh(&orig_node->neigh_list_lock);
 
+       /* increment unique neighbor refcount */
+       atomic_inc(&hardif_neigh->refcount);
+
        batadv_dbg(BATADV_DBG_BATMAN, orig_node->bat_priv,
                   "Creating new neighbor %pM for orig_node %pM on interface %s\n",
                   neigh_addr, orig_node->orig, hard_iface->net_dev->name);
 
 out:
+       if (hardif_neigh)
+               batadv_hardif_neigh_free_ref(hardif_neigh);
        return neigh_node;
 }
 
index fa18f9bf266b05db90a937a30c67c5aab2e1f11a..eae0557fa5fa49ae1f0da34dc373427e176a9b81 100644 (file)
@@ -41,6 +41,11 @@ void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
 void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
 struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
                                              const u8 *addr);
+struct batadv_hardif_neigh_node *
+batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,
+                       const u8 *neigh_addr);
+void
+batadv_hardif_neigh_free_ref(struct batadv_hardif_neigh_node *hardif_neigh);
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_orig_node *orig_node,
                      struct batadv_hard_iface *hard_iface,
index d260efd70499bd62ceb7b86b57684f45a96612e9..71c7d9f1f79fae43df74f4695afb40b48ccb4f54 100644 (file)
@@ -100,6 +100,8 @@ struct batadv_hard_iface_bat_iv {
  * @bat_iv: BATMAN IV specific per hard interface data
  * @cleanup_work: work queue callback item for hard interface deinit
  * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
+ * @neigh_list: list of unique single hop neighbors via this interface
+ * @neigh_list_lock: lock protecting neigh_list
  */
 struct batadv_hard_iface {
        struct list_head list;
@@ -115,6 +117,9 @@ struct batadv_hard_iface {
        struct batadv_hard_iface_bat_iv bat_iv;
        struct work_struct cleanup_work;
        struct dentry *debug_dir;
+       struct hlist_head neigh_list;
+       /* neigh_list_lock protects: neigh_list */
+       spinlock_t neigh_list_lock;
 };
 
 /**
@@ -340,6 +345,23 @@ struct batadv_gw_node {
        struct rcu_head rcu;
 };
 
+/**
+ * batadv_hardif_neigh_node - unique neighbor per hard interface
+ * @list: list node for batadv_hard_iface::neigh_list
+ * @addr: the MAC address of the neighboring interface
+ * @if_incoming: pointer to incoming hard interface
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in a RCU-safe manner
+ */
+struct batadv_hardif_neigh_node {
+       struct hlist_node list;
+       u8 addr[ETH_ALEN];
+       struct batadv_hard_iface *if_incoming;
+       unsigned long last_seen;
+       atomic_t refcount;
+       struct rcu_head rcu;
+};
+
 /**
  * struct batadv_neigh_node - structure for single hops neighbors
  * @list: list node for batadv_orig_node::neigh_list