Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next
[cascardo/linux.git] / net / batman-adv / originator.c
index f50553a..8ab1434 100644 (file)
 #include "routing.h"
 #include "gateway_client.h"
 #include "hard-interface.h"
-#include "unicast.h"
 #include "soft-interface.h"
 #include "bridge_loop_avoidance.h"
 #include "network-coding.h"
+#include "fragmentation.h"
 
 /* hash class keys */
 static struct lock_class_key batadv_orig_hash_lock_class_key;
@@ -36,7 +36,7 @@ static struct lock_class_key batadv_orig_hash_lock_class_key;
 static void batadv_purge_orig(struct work_struct *work);
 
 /* returns 1 if they are the same originator */
-static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
+int batadv_compare_orig(const struct hlist_node *node, const void *data2)
 {
        const void *data1 = container_of(node, struct batadv_orig_node,
                                         hash_entry);
@@ -44,6 +44,88 @@ static int batadv_compare_orig(const struct hlist_node *node, const void *data2)
        return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
 }
 
+/**
+ * batadv_orig_node_vlan_get - get an orig_node_vlan object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns the vlan object identified by vid and belonging to orig_node or NULL
+ * if it does not exist.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node,
+                         unsigned short vid)
+{
+       struct batadv_orig_node_vlan *vlan = NULL, *tmp;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(tmp, &orig_node->vlan_list, list) {
+               if (tmp->vid != vid)
+                       continue;
+
+               if (!atomic_inc_not_zero(&tmp->refcount))
+                       continue;
+
+               vlan = tmp;
+
+               break;
+       }
+       rcu_read_unlock();
+
+       return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_new - search and possibly create an orig_node_vlan
+ *  object
+ * @orig_node: the originator serving the VLAN
+ * @vid: the VLAN identifier
+ *
+ * Returns NULL in case of failure or the vlan object identified by vid and
+ * belonging to orig_node otherwise. The object is created and added to the list
+ * if it does not exist.
+ *
+ * The object is returned with refcounter increased by 1.
+ */
+struct batadv_orig_node_vlan *
+batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node,
+                         unsigned short vid)
+{
+       struct batadv_orig_node_vlan *vlan;
+
+       spin_lock_bh(&orig_node->vlan_list_lock);
+
+       /* first look if an object for this vid already exists */
+       vlan = batadv_orig_node_vlan_get(orig_node, vid);
+       if (vlan)
+               goto out;
+
+       vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
+       if (!vlan)
+               goto out;
+
+       atomic_set(&vlan->refcount, 2);
+       vlan->vid = vid;
+
+       list_add_rcu(&vlan->list, &orig_node->vlan_list);
+
+out:
+       spin_unlock_bh(&orig_node->vlan_list_lock);
+
+       return vlan;
+}
+
+/**
+ * batadv_orig_node_vlan_free_ref - decrement the refcounter and possibly free
+ *  the originator-vlan object
+ * @orig_vlan: the originator-vlan object to release
+ */
+void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan)
+{
+       if (atomic_dec_and_test(&orig_vlan->refcount))
+               kfree_rcu(orig_vlan, rcu);
+}
+
 int batadv_originator_init(struct batadv_priv *bat_priv)
 {
        if (bat_priv->orig_hash)
@@ -90,11 +172,20 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node)
        return router;
 }
 
+/**
+ * batadv_neigh_node_new - create and init a new neigh_node object
+ * @hard_iface: the interface where the neighbour is connected to
+ * @neigh_addr: the mac address of the neighbour interface
+ * @orig_node: originator object representing the neighbour
+ *
+ * Allocates a new neigh_node object and initialises all the generic fields.
+ * Returns the new object or NULL on failure.
+ */
 struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
-                     const uint8_t *neigh_addr)
+                     const uint8_t *neigh_addr,
+                     struct batadv_orig_node *orig_node)
 {
-       struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_neigh_node *neigh_node;
 
        neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC);
@@ -104,15 +195,14 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
        INIT_HLIST_NODE(&neigh_node->list);
 
        memcpy(neigh_node->addr, neigh_addr, ETH_ALEN);
-       spin_lock_init(&neigh_node->lq_update_lock);
+       neigh_node->if_incoming = hard_iface;
+       neigh_node->orig_node = orig_node;
+
+       INIT_LIST_HEAD(&neigh_node->bonding_list);
 
        /* extra reference for return */
        atomic_set(&neigh_node->refcount, 2);
 
-       batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                  "Creating new neighbor %pM on interface %s\n", neigh_addr,
-                  hard_iface->net_dev->name);
-
 out:
        return neigh_node;
 }
@@ -146,13 +236,15 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
        /* Free nc_nodes */
        batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
 
-       batadv_frag_list_free(&orig_node->frag_list);
-       batadv_tt_global_del_orig(orig_node->bat_priv, orig_node,
+       batadv_frag_purge_orig(orig_node, NULL);
+
+       batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, -1,
                                  "originator timed out");
 
+       if (orig_node->bat_priv->bat_algo_ops->bat_orig_free)
+               orig_node->bat_priv->bat_algo_ops->bat_orig_free(orig_node);
+
        kfree(orig_node->tt_buff);
-       kfree(orig_node->bcast_own);
-       kfree(orig_node->bcast_own_sum);
        kfree(orig_node);
 }
 
@@ -210,20 +302,22 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
        batadv_hash_destroy(hash);
 }
 
-/* this function finds or creates an originator entry for the given
- * address if it does not exits
+/**
+ * batadv_orig_node_new - creates a new orig_node
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the mac address of the originator
+ *
+ * Creates a new originator object and initialise all the generic fields.
+ * The new object is not added to the originator list.
+ * Returns the newly created object or NULL on failure.
  */
-struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
+struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
                                              const uint8_t *addr)
 {
        struct batadv_orig_node *orig_node;
-       int size;
-       int hash_added;
+       struct batadv_orig_node_vlan *vlan;
        unsigned long reset_time;
-
-       orig_node = batadv_orig_hash_find(bat_priv, addr);
-       if (orig_node)
-               return orig_node;
+       int i;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Creating new originator: %pM\n", addr);
@@ -234,10 +328,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
 
        INIT_HLIST_HEAD(&orig_node->neigh_list);
        INIT_LIST_HEAD(&orig_node->bond_list);
-       spin_lock_init(&orig_node->ogm_cnt_lock);
+       INIT_LIST_HEAD(&orig_node->vlan_list);
        spin_lock_init(&orig_node->bcast_seqno_lock);
        spin_lock_init(&orig_node->neigh_list_lock);
        spin_lock_init(&orig_node->tt_buff_lock);
+       spin_lock_init(&orig_node->tt_lock);
+       spin_lock_init(&orig_node->vlan_list_lock);
 
        batadv_nc_init_orig(orig_node);
 
@@ -249,43 +345,32 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
        memcpy(orig_node->orig, addr, ETH_ALEN);
        batadv_dat_init_orig_node_addr(orig_node);
        orig_node->router = NULL;
-       orig_node->tt_crc = 0;
        atomic_set(&orig_node->last_ttvn, 0);
        orig_node->tt_buff = NULL;
        orig_node->tt_buff_len = 0;
-       atomic_set(&orig_node->tt_size, 0);
        reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS);
        orig_node->bcast_seqno_reset = reset_time;
        orig_node->batman_seqno_reset = reset_time;
 
        atomic_set(&orig_node->bond_candidates, 0);
 
-       size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS;
-
-       orig_node->bcast_own = kzalloc(size, GFP_ATOMIC);
-       if (!orig_node->bcast_own)
+       /* create a vlan object for the "untagged" LAN */
+       vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS);
+       if (!vlan)
                goto free_orig_node;
+       /* batadv_orig_node_vlan_new() increases the refcounter.
+        * Immediately release vlan since it is not needed anymore in this
+        * context
+        */
+       batadv_orig_node_vlan_free_ref(vlan);
 
-       size = bat_priv->num_ifaces * sizeof(uint8_t);
-       orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC);
-
-       INIT_LIST_HEAD(&orig_node->frag_list);
-       orig_node->last_frag_packet = 0;
-
-       if (!orig_node->bcast_own_sum)
-               goto free_bcast_own;
-
-       hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
-                                    batadv_choose_orig, orig_node,
-                                    &orig_node->hash_entry);
-       if (hash_added != 0)
-               goto free_bcast_own_sum;
+       for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) {
+               INIT_HLIST_HEAD(&orig_node->fragments[i].head);
+               spin_lock_init(&orig_node->fragments[i].lock);
+               orig_node->fragments[i].size = 0;
+       }
 
        return orig_node;
-free_bcast_own_sum:
-       kfree(orig_node->bcast_own_sum);
-free_bcast_own:
-       kfree(orig_node->bcast_own);
 free_orig_node:
        kfree(orig_node);
        return NULL;
@@ -294,15 +379,16 @@ free_orig_node:
 static bool
 batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
                            struct batadv_orig_node *orig_node,
-                           struct batadv_neigh_node **best_neigh_node)
+                           struct batadv_neigh_node **best_neigh)
 {
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct hlist_node *node_tmp;
        struct batadv_neigh_node *neigh_node;
        bool neigh_purged = false;
        unsigned long last_seen;
        struct batadv_hard_iface *if_incoming;
 
-       *best_neigh_node = NULL;
+       *best_neigh = NULL;
 
        spin_lock_bh(&orig_node->neigh_list_lock);
 
@@ -335,9 +421,12 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
                        batadv_bonding_candidate_del(orig_node, neigh_node);
                        batadv_neigh_node_free_ref(neigh_node);
                } else {
-                       if ((!*best_neigh_node) ||
-                           (neigh_node->tq_avg > (*best_neigh_node)->tq_avg))
-                               *best_neigh_node = neigh_node;
+                       /* store the best_neighbour if this is the first
+                        * iteration or if a better neighbor has been found
+                        */
+                       if (!*best_neigh ||
+                           bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0)
+                               *best_neigh = neigh_node;
                }
        }
 
@@ -388,17 +477,14 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv)
                hlist_for_each_entry_safe(orig_node, node_tmp,
                                          head, hash_entry) {
                        if (batadv_purge_orig_node(bat_priv, orig_node)) {
-                               if (orig_node->gw_flags)
-                                       batadv_gw_node_delete(bat_priv,
-                                                             orig_node);
+                               batadv_gw_node_delete(bat_priv, orig_node);
                                hlist_del_rcu(&orig_node->hash_entry);
                                batadv_orig_node_free_ref(orig_node);
                                continue;
                        }
 
-                       if (batadv_has_timed_out(orig_node->last_frag_packet,
-                                                BATADV_FRAG_TIMEOUT))
-                               batadv_frag_list_free(&orig_node->frag_list);
+                       batadv_frag_purge_orig(orig_node,
+                                              batadv_frag_check_entry);
                }
                spin_unlock_bh(list_lock);
        }
@@ -429,100 +515,26 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
 {
        struct net_device *net_dev = (struct net_device *)seq->private;
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
-       struct batadv_hashtable *hash = bat_priv->orig_hash;
-       struct hlist_head *head;
        struct batadv_hard_iface *primary_if;
-       struct batadv_orig_node *orig_node;
-       struct batadv_neigh_node *neigh_node, *neigh_node_tmp;
-       int batman_count = 0;
-       int last_seen_secs;
-       int last_seen_msecs;
-       unsigned long last_seen_jiffies;
-       uint32_t i;
 
        primary_if = batadv_seq_print_text_primary_if_get(seq);
        if (!primary_if)
-               goto out;
+               return 0;
 
-       seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
+       seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n",
                   BATADV_SOURCE_VERSION, primary_if->net_dev->name,
-                  primary_if->net_dev->dev_addr, net_dev->name);
-       seq_printf(seq, "  %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
-                  "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE,
-                  "Nexthop", "outgoingIF", "Potential nexthops");
-
-       for (i = 0; i < hash->size; i++) {
-               head = &hash->table[i];
-
-               rcu_read_lock();
-               hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       neigh_node = batadv_orig_node_get_router(orig_node);
-                       if (!neigh_node)
-                               continue;
-
-                       if (neigh_node->tq_avg == 0)
-                               goto next;
-
-                       last_seen_jiffies = jiffies - orig_node->last_seen;
-                       last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
-                       last_seen_secs = last_seen_msecs / 1000;
-                       last_seen_msecs = last_seen_msecs % 1000;
-
-                       seq_printf(seq, "%pM %4i.%03is   (%3i) %pM [%10s]:",
-                                  orig_node->orig, last_seen_secs,
-                                  last_seen_msecs, neigh_node->tq_avg,
-                                  neigh_node->addr,
-                                  neigh_node->if_incoming->net_dev->name);
-
-                       hlist_for_each_entry_rcu(neigh_node_tmp,
-                                                &orig_node->neigh_list, list) {
-                               seq_printf(seq, " %pM (%3i)",
-                                          neigh_node_tmp->addr,
-                                          neigh_node_tmp->tq_avg);
-                       }
+                  primary_if->net_dev->dev_addr, net_dev->name,
+                  bat_priv->bat_algo_ops->name);
 
-                       seq_puts(seq, "\n");
-                       batman_count++;
+       batadv_hardif_free_ref(primary_if);
 
-next:
-                       batadv_neigh_node_free_ref(neigh_node);
-               }
-               rcu_read_unlock();
+       if (!bat_priv->bat_algo_ops->bat_orig_print) {
+               seq_puts(seq,
+                        "No printing function for this routing protocol\n");
+               return 0;
        }
 
-       if (batman_count == 0)
-               seq_puts(seq, "No batman nodes in range ...\n");
-
-out:
-       if (primary_if)
-               batadv_hardif_free_ref(primary_if);
-       return 0;
-}
-
-static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node,
-                                  int max_if_num)
-{
-       void *data_ptr;
-       size_t data_size, old_size;
-
-       data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS;
-       old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS;
-       data_ptr = kmalloc(data_size, GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own, old_size);
-       kfree(orig_node->bcast_own);
-       orig_node->bcast_own = data_ptr;
-
-       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own_sum,
-              (max_if_num - 1) * sizeof(uint8_t));
-       kfree(orig_node->bcast_own_sum);
-       orig_node->bcast_own_sum = data_ptr;
+       bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq);
 
        return 0;
 }
@@ -531,6 +543,7 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        struct batadv_hashtable *hash = bat_priv->orig_hash;
        struct hlist_head *head;
        struct batadv_orig_node *orig_node;
@@ -545,10 +558,10 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
-                       ret = batadv_orig_node_add_if(orig_node, max_if_num);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+                       ret = 0;
+                       if (bao->bat_orig_add_if)
+                               ret = bao->bat_orig_add_if(orig_node,
+                                                          max_if_num);
                        if (ret == -ENOMEM)
                                goto err;
                }
@@ -562,54 +575,6 @@ err:
        return -ENOMEM;
 }
 
-static int batadv_orig_node_del_if(struct batadv_orig_node *orig_node,
-                                  int max_if_num, int del_if_num)
-{
-       void *data_ptr = NULL;
-       int chunk_size;
-
-       /* last interface was removed */
-       if (max_if_num == 0)
-               goto free_bcast_own;
-
-       chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS;
-       data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       /* copy first part */
-       memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size);
-
-       /* copy second part */
-       memcpy((char *)data_ptr + del_if_num * chunk_size,
-              orig_node->bcast_own + ((del_if_num + 1) * chunk_size),
-              (max_if_num - del_if_num) * chunk_size);
-
-free_bcast_own:
-       kfree(orig_node->bcast_own);
-       orig_node->bcast_own = data_ptr;
-
-       if (max_if_num == 0)
-               goto free_own_sum;
-
-       data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC);
-       if (!data_ptr)
-               return -ENOMEM;
-
-       memcpy(data_ptr, orig_node->bcast_own_sum,
-              del_if_num * sizeof(uint8_t));
-
-       memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t),
-              orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)),
-              (max_if_num - del_if_num) * sizeof(uint8_t));
-
-free_own_sum:
-       kfree(orig_node->bcast_own_sum);
-       orig_node->bcast_own_sum = data_ptr;
-
-       return 0;
-}
-
 int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
                            int max_if_num)
 {
@@ -618,6 +583,7 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
        struct hlist_head *head;
        struct batadv_hard_iface *hard_iface_tmp;
        struct batadv_orig_node *orig_node;
+       struct batadv_algo_ops *bao = bat_priv->bat_algo_ops;
        uint32_t i;
        int ret;
 
@@ -629,11 +595,11 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
-                       spin_lock_bh(&orig_node->ogm_cnt_lock);
-                       ret = batadv_orig_node_del_if(orig_node, max_if_num,
-                                                     hard_iface->if_num);
-                       spin_unlock_bh(&orig_node->ogm_cnt_lock);
-
+                       ret = 0;
+                       if (bao->bat_orig_del_if)
+                               ret = bao->bat_orig_del_if(orig_node,
+                                                          max_if_num,
+                                                          hard_iface->if_num);
                        if (ret == -ENOMEM)
                                goto err;
                }