123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /* Copyright (C) 2012-2013 B.A.T.M.A.N. contributors:
- *
- * Martin Hundebøll, Jeppe Ledet-Pedersen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- */
- #include <linux/debugfs.h>
- #include "main.h"
- #include "network-coding.h"
- #include "originator.h"
- #include "hard-interface.h"
- static void batadv_nc_worker(struct work_struct *work);
- /**
- * batadv_nc_start_timer - initialise the nc periodic worker
- * @bat_priv: the bat priv with all the soft interface information
- */
- static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
- {
- queue_delayed_work(batadv_event_workqueue, &bat_priv->nc.work,
- msecs_to_jiffies(10));
- }
- /**
- * batadv_nc_init - initialise coding hash table and start house keeping
- * @bat_priv: the bat priv with all the soft interface information
- */
- int batadv_nc_init(struct batadv_priv *bat_priv)
- {
- INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
- batadv_nc_start_timer(bat_priv);
- return 0;
- }
- /**
- * batadv_nc_init_bat_priv - initialise the nc specific bat_priv variables
- * @bat_priv: the bat priv with all the soft interface information
- */
- void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
- {
- atomic_set(&bat_priv->network_coding, 1);
- bat_priv->nc.min_tq = 200;
- }
- /**
- * batadv_nc_init_orig - initialise the nc fields of an orig_node
- * @orig_node: the orig_node which is going to be initialised
- */
- void batadv_nc_init_orig(struct batadv_orig_node *orig_node)
- {
- INIT_LIST_HEAD(&orig_node->in_coding_list);
- INIT_LIST_HEAD(&orig_node->out_coding_list);
- spin_lock_init(&orig_node->in_coding_list_lock);
- spin_lock_init(&orig_node->out_coding_list_lock);
- }
- /**
- * batadv_nc_node_free_rcu - rcu callback to free an nc node and remove
- * its refcount on the orig_node
- * @rcu: rcu pointer of the nc node
- */
- static void batadv_nc_node_free_rcu(struct rcu_head *rcu)
- {
- struct batadv_nc_node *nc_node;
- nc_node = container_of(rcu, struct batadv_nc_node, rcu);
- batadv_orig_node_free_ref(nc_node->orig_node);
- kfree(nc_node);
- }
- /**
- * batadv_nc_node_free_ref - decrements the nc node refcounter and possibly
- * frees it
- * @nc_node: the nc node to free
- */
- static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node)
- {
- if (atomic_dec_and_test(&nc_node->refcount))
- call_rcu(&nc_node->rcu, batadv_nc_node_free_rcu);
- }
- /**
- * batadv_nc_to_purge_nc_node - checks whether an nc node has to be purged
- * @bat_priv: the bat priv with all the soft interface information
- * @nc_node: the nc node to check
- *
- * Returns true if the entry has to be purged now, false otherwise
- */
- static bool batadv_nc_to_purge_nc_node(struct batadv_priv *bat_priv,
- struct batadv_nc_node *nc_node)
- {
- if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
- return true;
- return batadv_has_timed_out(nc_node->last_seen, BATADV_NC_NODE_TIMEOUT);
- }
- /**
- * batadv_nc_purge_orig_nc_nodes - go through list of nc nodes and purge stale
- * entries
- * @bat_priv: the bat priv with all the soft interface information
- * @list: list of nc nodes
- * @lock: nc node list lock
- * @to_purge: function in charge to decide whether an entry has to be purged or
- * not. This function takes the nc node as argument and has to return
- * a boolean value: true if the entry has to be deleted, false
- * otherwise
- */
- static void
- batadv_nc_purge_orig_nc_nodes(struct batadv_priv *bat_priv,
- struct list_head *list,
- spinlock_t *lock,
- bool (*to_purge)(struct batadv_priv *,
- struct batadv_nc_node *))
- {
- struct batadv_nc_node *nc_node, *nc_node_tmp;
- /* For each nc_node in list */
- spin_lock_bh(lock);
- list_for_each_entry_safe(nc_node, nc_node_tmp, list, list) {
- /* if an helper function has been passed as parameter,
- * ask it if the entry has to be purged or not
- */
- if (to_purge && !to_purge(bat_priv, nc_node))
- continue;
- batadv_dbg(BATADV_DBG_NC, bat_priv,
- "Removing nc_node %pM -> %pM\n",
- nc_node->addr, nc_node->orig_node->orig);
- list_del_rcu(&nc_node->list);
- batadv_nc_node_free_ref(nc_node);
- }
- spin_unlock_bh(lock);
- }
- /**
- * batadv_nc_purge_orig - purges all nc node data attached of the given
- * originator
- * @bat_priv: the bat priv with all the soft interface information
- * @orig_node: orig_node with the nc node entries to be purged
- * @to_purge: function in charge to decide whether an entry has to be purged or
- * not. This function takes the nc node as argument and has to return
- * a boolean value: true is the entry has to be deleted, false
- * otherwise
- */
- void batadv_nc_purge_orig(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- bool (*to_purge)(struct batadv_priv *,
- struct batadv_nc_node *))
- {
- /* Check ingoing nc_node's of this orig_node */
- batadv_nc_purge_orig_nc_nodes(bat_priv, &orig_node->in_coding_list,
- &orig_node->in_coding_list_lock,
- to_purge);
- /* Check outgoing nc_node's of this orig_node */
- batadv_nc_purge_orig_nc_nodes(bat_priv, &orig_node->out_coding_list,
- &orig_node->out_coding_list_lock,
- to_purge);
- }
- /**
- * batadv_nc_purge_orig_hash - traverse entire originator hash to check if they
- * have timed out nc nodes
- * @bat_priv: the bat priv with all the soft interface information
- */
- static void batadv_nc_purge_orig_hash(struct batadv_priv *bat_priv)
- {
- struct batadv_hashtable *hash = bat_priv->orig_hash;
- struct hlist_head *head;
- struct batadv_orig_node *orig_node;
- uint32_t i;
- if (!hash)
- return;
- /* For each orig_node */
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry)
- batadv_nc_purge_orig(bat_priv, orig_node,
- batadv_nc_to_purge_nc_node);
- rcu_read_unlock();
- }
- }
- /**
- * batadv_nc_worker - periodic task for house keeping related to network coding
- * @work: kernel work struct
- */
- static void batadv_nc_worker(struct work_struct *work)
- {
- struct delayed_work *delayed_work;
- struct batadv_priv_nc *priv_nc;
- struct batadv_priv *bat_priv;
- delayed_work = container_of(work, struct delayed_work, work);
- priv_nc = container_of(delayed_work, struct batadv_priv_nc, work);
- bat_priv = container_of(priv_nc, struct batadv_priv, nc);
- batadv_nc_purge_orig_hash(bat_priv);
- /* Schedule a new check */
- batadv_nc_start_timer(bat_priv);
- }
- /**
- * batadv_can_nc_with_orig - checks whether the given orig node is suitable for
- * coding or not
- * @bat_priv: the bat priv with all the soft interface information
- * @orig_node: neighboring orig node which may be used as nc candidate
- * @ogm_packet: incoming ogm packet also used for the checks
- *
- * Returns true if:
- * 1) The OGM must have the most recent sequence number.
- * 2) The TTL must be decremented by one and only one.
- * 3) The OGM must be received from the first hop from orig_node.
- * 4) The TQ value of the OGM must be above bat_priv->nc.min_tq.
- */
- static bool batadv_can_nc_with_orig(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- struct batadv_ogm_packet *ogm_packet)
- {
- if (orig_node->last_real_seqno != ogm_packet->seqno)
- return false;
- if (orig_node->last_ttl != ogm_packet->header.ttl + 1)
- return false;
- if (!batadv_compare_eth(ogm_packet->orig, ogm_packet->prev_sender))
- return false;
- if (ogm_packet->tq < bat_priv->nc.min_tq)
- return false;
- return true;
- }
- /**
- * batadv_nc_find_nc_node - search for an existing nc node and return it
- * @orig_node: orig node originating the ogm packet
- * @orig_neigh_node: neighboring orig node from which we received the ogm packet
- * (can be equal to orig_node)
- * @in_coding: traverse incoming or outgoing network coding list
- *
- * Returns the nc_node if found, NULL otherwise.
- */
- static struct batadv_nc_node
- *batadv_nc_find_nc_node(struct batadv_orig_node *orig_node,
- struct batadv_orig_node *orig_neigh_node,
- bool in_coding)
- {
- struct batadv_nc_node *nc_node, *nc_node_out = NULL;
- struct list_head *list;
- if (in_coding)
- list = &orig_neigh_node->in_coding_list;
- else
- list = &orig_neigh_node->out_coding_list;
- /* Traverse list of nc_nodes to orig_node */
- rcu_read_lock();
- list_for_each_entry_rcu(nc_node, list, list) {
- if (!batadv_compare_eth(nc_node->addr, orig_node->orig))
- continue;
- if (!atomic_inc_not_zero(&nc_node->refcount))
- continue;
- /* Found a match */
- nc_node_out = nc_node;
- break;
- }
- rcu_read_unlock();
- return nc_node_out;
- }
- /**
- * batadv_nc_get_nc_node - retrieves an nc node or creates the entry if it was
- * not found
- * @bat_priv: the bat priv with all the soft interface information
- * @orig_node: orig node originating the ogm packet
- * @orig_neigh_node: neighboring orig node from which we received the ogm packet
- * (can be equal to orig_node)
- * @in_coding: traverse incoming or outgoing network coding list
- *
- * Returns the nc_node if found or created, NULL in case of an error.
- */
- static struct batadv_nc_node
- *batadv_nc_get_nc_node(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- struct batadv_orig_node *orig_neigh_node,
- bool in_coding)
- {
- struct batadv_nc_node *nc_node;
- spinlock_t *lock; /* Used to lock list selected by "int in_coding" */
- struct list_head *list;
- /* Check if nc_node is already added */
- nc_node = batadv_nc_find_nc_node(orig_node, orig_neigh_node, in_coding);
- /* Node found */
- if (nc_node)
- return nc_node;
- nc_node = kzalloc(sizeof(*nc_node), GFP_ATOMIC);
- if (!nc_node)
- return NULL;
- if (!atomic_inc_not_zero(&orig_neigh_node->refcount))
- goto free;
- /* Initialize nc_node */
- INIT_LIST_HEAD(&nc_node->list);
- memcpy(nc_node->addr, orig_node->orig, ETH_ALEN);
- nc_node->orig_node = orig_neigh_node;
- atomic_set(&nc_node->refcount, 2);
- /* Select ingoing or outgoing coding node */
- if (in_coding) {
- lock = &orig_neigh_node->in_coding_list_lock;
- list = &orig_neigh_node->in_coding_list;
- } else {
- lock = &orig_neigh_node->out_coding_list_lock;
- list = &orig_neigh_node->out_coding_list;
- }
- batadv_dbg(BATADV_DBG_NC, bat_priv, "Adding nc_node %pM -> %pM\n",
- nc_node->addr, nc_node->orig_node->orig);
- /* Add nc_node to orig_node */
- spin_lock_bh(lock);
- list_add_tail_rcu(&nc_node->list, list);
- spin_unlock_bh(lock);
- return nc_node;
- free:
- kfree(nc_node);
- return NULL;
- }
- /**
- * batadv_nc_update_nc_node - updates stored incoming and outgoing nc node structs
- * (best called on incoming OGMs)
- * @bat_priv: the bat priv with all the soft interface information
- * @orig_node: orig node originating the ogm packet
- * @orig_neigh_node: neighboring orig node from which we received the ogm packet
- * (can be equal to orig_node)
- * @ogm_packet: incoming ogm packet
- * @is_single_hop_neigh: orig_node is a single hop neighbor
- */
- void batadv_nc_update_nc_node(struct batadv_priv *bat_priv,
- struct batadv_orig_node *orig_node,
- struct batadv_orig_node *orig_neigh_node,
- struct batadv_ogm_packet *ogm_packet,
- int is_single_hop_neigh)
- {
- struct batadv_nc_node *in_nc_node = NULL, *out_nc_node = NULL;
- /* Check if network coding is enabled */
- if (!atomic_read(&bat_priv->network_coding))
- goto out;
- /* accept ogms from 'good' neighbors and single hop neighbors */
- if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) &&
- !is_single_hop_neigh)
- goto out;
- /* Add orig_node as in_nc_node on hop */
- in_nc_node = batadv_nc_get_nc_node(bat_priv, orig_node,
- orig_neigh_node, true);
- if (!in_nc_node)
- goto out;
- in_nc_node->last_seen = jiffies;
- /* Add hop as out_nc_node on orig_node */
- out_nc_node = batadv_nc_get_nc_node(bat_priv, orig_neigh_node,
- orig_node, false);
- if (!out_nc_node)
- goto out;
- out_nc_node->last_seen = jiffies;
- out:
- if (in_nc_node)
- batadv_nc_node_free_ref(in_nc_node);
- if (out_nc_node)
- batadv_nc_node_free_ref(out_nc_node);
- }
- /**
- * batadv_nc_free - clean up network coding memory
- * @bat_priv: the bat priv with all the soft interface information
- */
- void batadv_nc_free(struct batadv_priv *bat_priv)
- {
- cancel_delayed_work_sync(&bat_priv->nc.work);
- }
- /**
- * batadv_nc_nodes_seq_print_text - print the nc node information
- * @seq: seq file to print on
- * @offset: not used
- */
- int batadv_nc_nodes_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 batadv_hard_iface *primary_if;
- struct hlist_head *head;
- struct batadv_orig_node *orig_node;
- struct batadv_nc_node *nc_node;
- int i;
- primary_if = batadv_seq_print_text_primary_if_get(seq);
- if (!primary_if)
- goto out;
- /* Traverse list of originators */
- for (i = 0; i < hash->size; i++) {
- head = &hash->table[i];
- /* For each orig_node in this bin */
- rcu_read_lock();
- hlist_for_each_entry_rcu(orig_node, head, hash_entry) {
- seq_printf(seq, "Node: %pM\n", orig_node->orig);
- seq_printf(seq, " Ingoing: ");
- /* For each in_nc_node to this orig_node */
- list_for_each_entry_rcu(nc_node,
- &orig_node->in_coding_list,
- list)
- seq_printf(seq, "%pM ",
- nc_node->addr);
- seq_printf(seq, "\n");
- seq_printf(seq, " Outgoing: ");
- /* For out_nc_node to this orig_node */
- list_for_each_entry_rcu(nc_node,
- &orig_node->out_coding_list,
- list)
- seq_printf(seq, "%pM ",
- nc_node->addr);
- seq_printf(seq, "\n\n");
- }
- rcu_read_unlock();
- }
- out:
- if (primary_if)
- batadv_hardif_free_ref(primary_if);
- return 0;
- }
- /**
- * batadv_nc_init_debugfs - create nc folder and related files in debugfs
- * @bat_priv: the bat priv with all the soft interface information
- */
- int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
- {
- struct dentry *nc_dir, *file;
- nc_dir = debugfs_create_dir("nc", bat_priv->debug_dir);
- if (!nc_dir)
- goto out;
- file = debugfs_create_u8("min_tq", S_IRUGO | S_IWUSR, nc_dir,
- &bat_priv->nc.min_tq);
- if (!file)
- goto out;
- return 0;
- out:
- return -ENOMEM;
- }
|