Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9f9ab17 authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'batadv-next-for-davem-20160812' of git://git.open-mesh.org/linux-merge



Simon Wunderlich says:

====================
This feature patchset includes the following changes (mostly
chronological order):

 - bump version strings, by Simon Wunderlich

 - kerneldoc clean up, by Sven Eckelmann

 - enable RTNL automatic loading and according documentation
   changes, by Sven Eckelmann (2 patches)

 - fix/improve interface removal and associated locking, by
   Sven Eckelmann (3 patches)

 - clean up unused variables, by Linus Luessing

 - implement Gateway selection code for B.A.T.M.A.N. V by
   Antonio Quartulli (4 patches)

 - rewrite TQ comparison by Markus Pargmann

 - fix Cocinelle warnings on bool vs integers (by Fenguang Wu/Intels
   kbuild test robot) and bitwise arithmetic operations (by Linus
   Luessing)

 - rewrite packet creation for forwarding for readability and to avoid
   reference count mistakes, by Linus Luessing

 - use kmem_cache for translation table, which results in more efficient
   storing of translation table entries, by Sven Eckelmann

 - rewrite/clarify reference handling for send_skb_unicast, by Sven
   Eckelmann

 - fix debug messages when updating routes, by Sven Eckelmann
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 85be21bd b5dcbad2
Loading
Loading
Loading
Loading
+11 −6
Original line number Original line Diff line number Diff line
@@ -43,10 +43,15 @@ new interfaces to verify the compatibility. There is no need to
reload the module if you plug your USB wifi adapter into your ma-
reload the module if you plug your USB wifi adapter into your ma-
chine after batman advanced was initially loaded.
chine after batman advanced was initially loaded.


To activate a  given  interface  simply  write  "bat0"  into  its
The batman-adv soft-interface can be created using  the  iproute2
"mesh_iface" file inside the batman_adv subfolder:
tool "ip"


# echo bat0 > /sys/class/net/eth0/batman_adv/mesh_iface
# ip link add name bat0 type batadv

To  activate a  given  interface  simply  attach it to the "bat0"
interface

# ip link set dev eth0 master bat0


Repeat  this step for all interfaces you wish to add.  Now batman
Repeat  this step for all interfaces you wish to add.  Now batman
starts using/broadcasting on this/these interface(s).
starts using/broadcasting on this/these interface(s).
@@ -56,10 +61,10 @@ By reading the "iface_status" file you can check its status:
# cat /sys/class/net/eth0/batman_adv/iface_status
# cat /sys/class/net/eth0/batman_adv/iface_status
# active
# active


To deactivate an interface you have  to  write  "none"  into  its
To  deactivate  an  interface  you  have   to  detach it from the
"mesh_iface" file:
"bat0" interface:


# echo none > /sys/class/net/eth0/batman_adv/mesh_iface
# ip link set dev eth0 nomaster




All  mesh  wide  settings  can be found in batman's own interface
All  mesh  wide  settings  can be found in batman's own interface
+276 −71
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@


#include "bat_algo.h"
#include "bat_algo.h"
#include "bitarray.h"
#include "bitarray.h"
#include "gateway_client.h"
#include "hard-interface.h"
#include "hard-interface.h"
#include "hash.h"
#include "hash.h"
#include "log.h"
#include "log.h"
@@ -528,36 +529,25 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{
{
	struct net_device *soft_iface;
	struct net_device *soft_iface;
	struct batadv_priv *bat_priv;
	struct batadv_hard_iface *primary_if = NULL;


	if (!forw_packet->if_incoming) {
	if (!forw_packet->if_incoming) {
		pr_err("Error - can't forward packet: incoming iface not specified\n");
		pr_err("Error - can't forward packet: incoming iface not specified\n");
		goto out;
		return;
	}
	}


	soft_iface = forw_packet->if_incoming->soft_iface;
	soft_iface = forw_packet->if_incoming->soft_iface;
	bat_priv = netdev_priv(soft_iface);


	if (WARN_ON(!forw_packet->if_outgoing))
	if (WARN_ON(!forw_packet->if_outgoing))
		goto out;
		return;


	if (WARN_ON(forw_packet->if_outgoing->soft_iface != soft_iface))
	if (WARN_ON(forw_packet->if_outgoing->soft_iface != soft_iface))
		goto out;
		return;


	if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
	if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
		goto out;
		return;

	primary_if = batadv_primary_if_get_selected(bat_priv);
	if (!primary_if)
		goto out;


	/* only for one specific outgoing interface */
	/* only for one specific outgoing interface */
	batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);
	batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);

out:
	if (primary_if)
		batadv_hardif_put(primary_if);
}
}


/**
/**
@@ -685,19 +675,12 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
	struct batadv_forw_packet *forw_packet_aggr;
	struct batadv_forw_packet *forw_packet_aggr;
	unsigned char *skb_buff;
	unsigned char *skb_buff;
	unsigned int skb_size;
	unsigned int skb_size;
	atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;


	/* own packet should always be scheduled */
	forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing,
	if (!own_packet) {
						    queue_left, bat_priv);
		if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
			batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
				   "batman packet queue full\n");
			return;
		}
	}

	forw_packet_aggr = kmalloc(sizeof(*forw_packet_aggr), GFP_ATOMIC);
	if (!forw_packet_aggr)
	if (!forw_packet_aggr)
		goto out_nomem;
		return;


	if (atomic_read(&bat_priv->aggregated_ogms) &&
	if (atomic_read(&bat_priv->aggregated_ogms) &&
	    packet_len < BATADV_MAX_AGGREGATION_BYTES)
	    packet_len < BATADV_MAX_AGGREGATION_BYTES)
@@ -708,8 +691,11 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
	skb_size += ETH_HLEN;
	skb_size += ETH_HLEN;


	forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size);
	forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size);
	if (!forw_packet_aggr->skb)
	if (!forw_packet_aggr->skb) {
		goto out_free_forw_packet;
		batadv_forw_packet_free(forw_packet_aggr);
		return;
	}

	forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
	forw_packet_aggr->skb->priority = TC_PRIO_CONTROL;
	skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
	skb_reserve(forw_packet_aggr->skb, ETH_HLEN);


@@ -717,12 +703,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
	forw_packet_aggr->packet_len = packet_len;
	forw_packet_aggr->packet_len = packet_len;
	memcpy(skb_buff, packet_buff, packet_len);
	memcpy(skb_buff, packet_buff, packet_len);


	kref_get(&if_incoming->refcount);
	kref_get(&if_outgoing->refcount);
	forw_packet_aggr->own = own_packet;
	forw_packet_aggr->own = own_packet;
	forw_packet_aggr->if_incoming = if_incoming;
	forw_packet_aggr->if_outgoing = if_outgoing;
	forw_packet_aggr->num_packets = 0;
	forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS;
	forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS;
	forw_packet_aggr->send_time = send_time;
	forw_packet_aggr->send_time = send_time;


@@ -741,13 +722,6 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
	queue_delayed_work(batadv_event_workqueue,
	queue_delayed_work(batadv_event_workqueue,
			   &forw_packet_aggr->delayed_work,
			   &forw_packet_aggr->delayed_work,
			   send_time - jiffies);
			   send_time - jiffies);

	return;
out_free_forw_packet:
	kfree(forw_packet_aggr);
out_nomem:
	if (!own_packet)
		atomic_inc(&bat_priv->batman_queue_left);
}
}


/* aggregate a new packet into the existing ogm packet */
/* aggregate a new packet into the existing ogm packet */
@@ -1830,10 +1804,6 @@ static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
		batadv_iv_ogm_schedule(forw_packet->if_incoming);
		batadv_iv_ogm_schedule(forw_packet->if_incoming);


out:
out:
	/* don't count own packet */
	if (!forw_packet->own)
		atomic_inc(&bat_priv->batman_queue_left);

	batadv_forw_packet_free(forw_packet);
	batadv_forw_packet_free(forw_packet);
}
}


@@ -2029,35 +1999,40 @@ static void batadv_iv_neigh_print(struct batadv_priv *bat_priv,
}
}


/**
/**
 * batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
 * batadv_iv_ogm_neigh_diff - calculate tq difference of two neighbors
 * @neigh1: the first neighbor object of the comparison
 * @neigh1: the first neighbor object of the comparison
 * @if_outgoing1: outgoing interface for the first neighbor
 * @if_outgoing1: outgoing interface for the first neighbor
 * @neigh2: the second neighbor object of the comparison
 * @neigh2: the second neighbor object of the comparison
 * @if_outgoing2: outgoing interface for the second neighbor
 * @if_outgoing2: outgoing interface for the second neighbor
 * @diff: pointer to integer receiving the calculated difference
 *
 *
 * Return: a value less, equal to or greater than 0 if the metric via neigh1 is
 * The content of *@diff is only valid when this function returns true.
 * lower, the same as or higher than the metric via neigh2
 * It is less, equal to or greater than 0 if the metric via neigh1 is lower,
 * the same as or higher than the metric via neigh2
 *
 * Return: true when the difference could be calculated, false otherwise
 */
 */
static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
static bool batadv_iv_ogm_neigh_diff(struct batadv_neigh_node *neigh1,
				     struct batadv_hard_iface *if_outgoing1,
				     struct batadv_hard_iface *if_outgoing1,
				     struct batadv_neigh_node *neigh2,
				     struct batadv_neigh_node *neigh2,
				   struct batadv_hard_iface *if_outgoing2)
				     struct batadv_hard_iface *if_outgoing2,
				     int *diff)
{
{
	struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo;
	struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo;
	u8 tq1, tq2;
	u8 tq1, tq2;
	int diff;
	bool ret = true;


	neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
	neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
	neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
	neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);


	if (!neigh1_ifinfo || !neigh2_ifinfo) {
	if (!neigh1_ifinfo || !neigh2_ifinfo) {
		diff = 0;
		ret = false;
		goto out;
		goto out;
	}
	}


	tq1 = neigh1_ifinfo->bat_iv.tq_avg;
	tq1 = neigh1_ifinfo->bat_iv.tq_avg;
	tq2 = neigh2_ifinfo->bat_iv.tq_avg;
	tq2 = neigh2_ifinfo->bat_iv.tq_avg;
	diff = tq1 - tq2;
	*diff = (int)tq1 - (int)tq2;


out:
out:
	if (neigh1_ifinfo)
	if (neigh1_ifinfo)
@@ -2065,6 +2040,32 @@ static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
	if (neigh2_ifinfo)
	if (neigh2_ifinfo)
		batadv_neigh_ifinfo_put(neigh2_ifinfo);
		batadv_neigh_ifinfo_put(neigh2_ifinfo);


	return ret;
}

/**
 * batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors
 * @neigh1: the first neighbor object of the comparison
 * @if_outgoing1: outgoing interface for the first neighbor
 * @neigh2: the second neighbor object of the comparison
 * @if_outgoing2: outgoing interface for the second neighbor
 *
 * Return: a value less, equal to or greater than 0 if the metric via neigh1 is
 * lower, the same as or higher than the metric via neigh2
 */
static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1,
				   struct batadv_hard_iface *if_outgoing1,
				   struct batadv_neigh_node *neigh2,
				   struct batadv_hard_iface *if_outgoing2)
{
	bool ret;
	int diff;

	ret = batadv_iv_ogm_neigh_diff(neigh1, if_outgoing1, neigh2,
				       if_outgoing2, &diff);
	if (!ret)
		return 0;

	return diff;
	return diff;
}
}


@@ -2085,36 +2086,235 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1,
			   struct batadv_neigh_node *neigh2,
			   struct batadv_neigh_node *neigh2,
			   struct batadv_hard_iface *if_outgoing2)
			   struct batadv_hard_iface *if_outgoing2)
{
{
	struct batadv_neigh_ifinfo *neigh1_ifinfo, *neigh2_ifinfo;
	u8 tq1, tq2;
	bool ret;
	bool ret;
	int diff;


	neigh1_ifinfo = batadv_neigh_ifinfo_get(neigh1, if_outgoing1);
	ret = batadv_iv_ogm_neigh_diff(neigh1, if_outgoing1, neigh2,
	neigh2_ifinfo = batadv_neigh_ifinfo_get(neigh2, if_outgoing2);
				       if_outgoing2, &diff);
	if (!ret)
		return false;


	/* we can't say that the metric is better */
	ret = diff > -BATADV_TQ_SIMILARITY_THRESHOLD;
	if (!neigh1_ifinfo || !neigh2_ifinfo) {
	return ret;
		ret = false;
}

static void batadv_iv_iface_activate(struct batadv_hard_iface *hard_iface)
{
	/* begin scheduling originator messages on that interface */
	batadv_iv_ogm_schedule(hard_iface);
}

static struct batadv_gw_node *
batadv_iv_gw_get_best_gw_node(struct batadv_priv *bat_priv)
{
	struct batadv_neigh_node *router;
	struct batadv_neigh_ifinfo *router_ifinfo;
	struct batadv_gw_node *gw_node, *curr_gw = NULL;
	u64 max_gw_factor = 0;
	u64 tmp_gw_factor = 0;
	u8 max_tq = 0;
	u8 tq_avg;
	struct batadv_orig_node *orig_node;

	rcu_read_lock();
	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
		orig_node = gw_node->orig_node;
		router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
		if (!router)
			continue;

		router_ifinfo = batadv_neigh_ifinfo_get(router,
							BATADV_IF_DEFAULT);
		if (!router_ifinfo)
			goto next;

		if (!kref_get_unless_zero(&gw_node->refcount))
			goto next;

		tq_avg = router_ifinfo->bat_iv.tq_avg;

		switch (atomic_read(&bat_priv->gw.sel_class)) {
		case 1: /* fast connection */
			tmp_gw_factor = tq_avg * tq_avg;
			tmp_gw_factor *= gw_node->bandwidth_down;
			tmp_gw_factor *= 100 * 100;
			tmp_gw_factor >>= 18;

			if ((tmp_gw_factor > max_gw_factor) ||
			    ((tmp_gw_factor == max_gw_factor) &&
			     (tq_avg > max_tq))) {
				if (curr_gw)
					batadv_gw_node_put(curr_gw);
				curr_gw = gw_node;
				kref_get(&curr_gw->refcount);
			}
			break;

		default: /* 2:  stable connection (use best statistic)
			  * 3:  fast-switch (use best statistic but change as
			  *     soon as a better gateway appears)
			  * XX: late-switch (use best statistic but change as
			  *     soon as a better gateway appears which has
			  *     $routing_class more tq points)
			  */
			if (tq_avg > max_tq) {
				if (curr_gw)
					batadv_gw_node_put(curr_gw);
				curr_gw = gw_node;
				kref_get(&curr_gw->refcount);
			}
			break;
		}

		if (tq_avg > max_tq)
			max_tq = tq_avg;

		if (tmp_gw_factor > max_gw_factor)
			max_gw_factor = tmp_gw_factor;

		batadv_gw_node_put(gw_node);

next:
		batadv_neigh_node_put(router);
		if (router_ifinfo)
			batadv_neigh_ifinfo_put(router_ifinfo);
	}
	rcu_read_unlock();

	return curr_gw;
}

static bool batadv_iv_gw_is_eligible(struct batadv_priv *bat_priv,
				     struct batadv_orig_node *curr_gw_orig,
				     struct batadv_orig_node *orig_node)
{
	struct batadv_neigh_ifinfo *router_orig_ifinfo = NULL;
	struct batadv_neigh_ifinfo *router_gw_ifinfo = NULL;
	struct batadv_neigh_node *router_gw = NULL;
	struct batadv_neigh_node *router_orig = NULL;
	u8 gw_tq_avg, orig_tq_avg;
	bool ret = false;

	/* dynamic re-election is performed only on fast or late switch */
	if (atomic_read(&bat_priv->gw.sel_class) <= 2)
		return false;

	router_gw = batadv_orig_router_get(curr_gw_orig, BATADV_IF_DEFAULT);
	if (!router_gw) {
		ret = true;
		goto out;
		goto out;
	}
	}


	tq1 = neigh1_ifinfo->bat_iv.tq_avg;
	router_gw_ifinfo = batadv_neigh_ifinfo_get(router_gw,
	tq2 = neigh2_ifinfo->bat_iv.tq_avg;
						   BATADV_IF_DEFAULT);
	ret = (tq1 - tq2) > -BATADV_TQ_SIMILARITY_THRESHOLD;
	if (!router_gw_ifinfo) {
		ret = true;
		goto out;
	}

	router_orig = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
	if (!router_orig)
		goto out;

	router_orig_ifinfo = batadv_neigh_ifinfo_get(router_orig,
						     BATADV_IF_DEFAULT);
	if (!router_orig_ifinfo)
		goto out;

	gw_tq_avg = router_gw_ifinfo->bat_iv.tq_avg;
	orig_tq_avg = router_orig_ifinfo->bat_iv.tq_avg;


	/* the TQ value has to be better */
	if (orig_tq_avg < gw_tq_avg)
		goto out;

	/* if the routing class is greater than 3 the value tells us how much
	 * greater the TQ value of the new gateway must be
	 */
	if ((atomic_read(&bat_priv->gw.sel_class) > 3) &&
	    (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw.sel_class)))
		goto out;

	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
		   "Restarting gateway selection: better gateway found (tq curr: %i, tq new: %i)\n",
		   gw_tq_avg, orig_tq_avg);

	ret = true;
out:
out:
	if (neigh1_ifinfo)
	if (router_gw_ifinfo)
		batadv_neigh_ifinfo_put(neigh1_ifinfo);
		batadv_neigh_ifinfo_put(router_gw_ifinfo);
	if (neigh2_ifinfo)
	if (router_orig_ifinfo)
		batadv_neigh_ifinfo_put(neigh2_ifinfo);
		batadv_neigh_ifinfo_put(router_orig_ifinfo);
	if (router_gw)
		batadv_neigh_node_put(router_gw);
	if (router_orig)
		batadv_neigh_node_put(router_orig);


	return ret;
	return ret;
}
}


static void batadv_iv_iface_activate(struct batadv_hard_iface *hard_iface)
/* fails if orig_node has no router */
static int batadv_iv_gw_write_buffer_text(struct batadv_priv *bat_priv,
					  struct seq_file *seq,
					  const struct batadv_gw_node *gw_node)
{
{
	/* begin scheduling originator messages on that interface */
	struct batadv_gw_node *curr_gw;
	batadv_iv_ogm_schedule(hard_iface);
	struct batadv_neigh_node *router;
	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
	int ret = -1;

	router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
	if (!router)
		goto out;

	router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
	if (!router_ifinfo)
		goto out;

	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);

	seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n",
		   (curr_gw == gw_node ? "=>" : "  "),
		   gw_node->orig_node->orig,
		   router_ifinfo->bat_iv.tq_avg, router->addr,
		   router->if_incoming->net_dev->name,
		   gw_node->bandwidth_down / 10,
		   gw_node->bandwidth_down % 10,
		   gw_node->bandwidth_up / 10,
		   gw_node->bandwidth_up % 10);
	ret = seq_has_overflowed(seq) ? -1 : 0;

	if (curr_gw)
		batadv_gw_node_put(curr_gw);
out:
	if (router_ifinfo)
		batadv_neigh_ifinfo_put(router_ifinfo);
	if (router)
		batadv_neigh_node_put(router);
	return ret;
}

static void batadv_iv_gw_print(struct batadv_priv *bat_priv,
			       struct seq_file *seq)
{
	struct batadv_gw_node *gw_node;
	int gw_count = 0;

	seq_puts(seq,
		 "      Gateway      (#/255)           Nexthop [outgoingIF]: advertised uplink bandwidth\n");

	rcu_read_lock();
	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
		/* fails if orig_node has no router */
		if (batadv_iv_gw_write_buffer_text(bat_priv, seq, gw_node) < 0)
			continue;

		gw_count++;
	}
	rcu_read_unlock();

	if (gw_count == 0)
		seq_puts(seq, "No gateways in range ...\n");
}
}


static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
@@ -2137,6 +2337,11 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = {
		.add_if = batadv_iv_ogm_orig_add_if,
		.add_if = batadv_iv_ogm_orig_add_if,
		.del_if = batadv_iv_ogm_orig_del_if,
		.del_if = batadv_iv_ogm_orig_del_if,
	},
	},
	.gw = {
		.get_best_gw_node = batadv_iv_gw_get_best_gw_node,
		.is_eligible = batadv_iv_gw_is_eligible,
		.print = batadv_iv_gw_print,
	},
};
};


int __init batadv_iv_init(void)
int __init batadv_iv_init(void)
+256 −1
Original line number Original line Diff line number Diff line
@@ -21,8 +21,11 @@
#include <linux/atomic.h>
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/bug.h>
#include <linux/cache.h>
#include <linux/cache.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <linux/rculist.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/rcupdate.h>
@@ -34,8 +37,11 @@
#include "bat_algo.h"
#include "bat_algo.h"
#include "bat_v_elp.h"
#include "bat_v_elp.h"
#include "bat_v_ogm.h"
#include "bat_v_ogm.h"
#include "gateway_client.h"
#include "gateway_common.h"
#include "hard-interface.h"
#include "hard-interface.h"
#include "hash.h"
#include "hash.h"
#include "log.h"
#include "originator.h"
#include "originator.h"
#include "packet.h"
#include "packet.h"


@@ -320,6 +326,239 @@ static bool batadv_v_neigh_is_sob(struct batadv_neigh_node *neigh1,
	return ret;
	return ret;
}
}


static ssize_t batadv_v_store_sel_class(struct batadv_priv *bat_priv,
					char *buff, size_t count)
{
	u32 old_class, class;

	if (!batadv_parse_throughput(bat_priv->soft_iface, buff,
				     "B.A.T.M.A.N. V GW selection class",
				     &class))
		return -EINVAL;

	old_class = atomic_read(&bat_priv->gw.sel_class);
	atomic_set(&bat_priv->gw.sel_class, class);

	if (old_class != class)
		batadv_gw_reselect(bat_priv);

	return count;
}

static ssize_t batadv_v_show_sel_class(struct batadv_priv *bat_priv, char *buff)
{
	u32 class = atomic_read(&bat_priv->gw.sel_class);

	return sprintf(buff, "%u.%u MBit\n", class / 10, class % 10);
}

/**
 * batadv_v_gw_throughput_get - retrieve the GW-bandwidth for a given GW
 * @gw_node: the GW to retrieve the metric for
 * @bw: the pointer where the metric will be stored. The metric is computed as
 *  the minimum between the GW advertised throughput and the path throughput to
 *  it in the mesh
 *
 * Return: 0 on success, -1 on failure
 */
static int batadv_v_gw_throughput_get(struct batadv_gw_node *gw_node, u32 *bw)
{
	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
	struct batadv_orig_node *orig_node;
	struct batadv_neigh_node *router;
	int ret = -1;

	orig_node = gw_node->orig_node;
	router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT);
	if (!router)
		goto out;

	router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
	if (!router_ifinfo)
		goto out;

	/* the GW metric is computed as the minimum between the path throughput
	 * to reach the GW itself and the advertised bandwidth.
	 * This gives us an approximation of the effective throughput that the
	 * client can expect via this particular GW node
	 */
	*bw = router_ifinfo->bat_v.throughput;
	*bw = min_t(u32, *bw, gw_node->bandwidth_down);

	ret = 0;
out:
	if (router)
		batadv_neigh_node_put(router);
	if (router_ifinfo)
		batadv_neigh_ifinfo_put(router_ifinfo);

	return ret;
}

/**
 * batadv_v_gw_get_best_gw_node - retrieve the best GW node
 * @bat_priv: the bat priv with all the soft interface information
 *
 * Return: the GW node having the best GW-metric, NULL if no GW is known
 */
static struct batadv_gw_node *
batadv_v_gw_get_best_gw_node(struct batadv_priv *bat_priv)
{
	struct batadv_gw_node *gw_node, *curr_gw = NULL;
	u32 max_bw = 0, bw;

	rcu_read_lock();
	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
		if (!kref_get_unless_zero(&gw_node->refcount))
			continue;

		if (batadv_v_gw_throughput_get(gw_node, &bw) < 0)
			goto next;

		if (curr_gw && (bw <= max_bw))
			goto next;

		if (curr_gw)
			batadv_gw_node_put(curr_gw);

		curr_gw = gw_node;
		kref_get(&curr_gw->refcount);
		max_bw = bw;

next:
		batadv_gw_node_put(gw_node);
	}
	rcu_read_unlock();

	return curr_gw;
}

/**
 * batadv_v_gw_is_eligible - check if a originator would be selected as GW
 * @bat_priv: the bat priv with all the soft interface information
 * @curr_gw_orig: originator representing the currently selected GW
 * @orig_node: the originator representing the new candidate
 *
 * Return: true if orig_node can be selected as current GW, false otherwise
 */
static bool batadv_v_gw_is_eligible(struct batadv_priv *bat_priv,
				    struct batadv_orig_node *curr_gw_orig,
				    struct batadv_orig_node *orig_node)
{
	struct batadv_gw_node *curr_gw = NULL, *orig_gw = NULL;
	u32 gw_throughput, orig_throughput, threshold;
	bool ret = false;

	threshold = atomic_read(&bat_priv->gw.sel_class);

	curr_gw = batadv_gw_node_get(bat_priv, curr_gw_orig);
	if (!curr_gw) {
		ret = true;
		goto out;
	}

	if (batadv_v_gw_throughput_get(curr_gw, &gw_throughput) < 0) {
		ret = true;
		goto out;
	}

	orig_gw = batadv_gw_node_get(bat_priv, orig_node);
	if (!orig_node)
		goto out;

	if (batadv_v_gw_throughput_get(orig_gw, &orig_throughput) < 0)
		goto out;

	if (orig_throughput < gw_throughput)
		goto out;

	if ((orig_throughput - gw_throughput) < threshold)
		goto out;

	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
		   "Restarting gateway selection: better gateway found (throughput curr: %u, throughput new: %u)\n",
		   gw_throughput, orig_throughput);

	ret = true;
out:
	if (curr_gw)
		batadv_gw_node_put(curr_gw);
	if (orig_gw)
		batadv_gw_node_put(orig_gw);

	return ret;
}

/* fails if orig_node has no router */
static int batadv_v_gw_write_buffer_text(struct batadv_priv *bat_priv,
					 struct seq_file *seq,
					 const struct batadv_gw_node *gw_node)
{
	struct batadv_gw_node *curr_gw;
	struct batadv_neigh_node *router;
	struct batadv_neigh_ifinfo *router_ifinfo = NULL;
	int ret = -1;

	router = batadv_orig_router_get(gw_node->orig_node, BATADV_IF_DEFAULT);
	if (!router)
		goto out;

	router_ifinfo = batadv_neigh_ifinfo_get(router, BATADV_IF_DEFAULT);
	if (!router_ifinfo)
		goto out;

	curr_gw = batadv_gw_get_selected_gw_node(bat_priv);

	seq_printf(seq, "%s %pM (%9u.%1u) %pM [%10s]: %u.%u/%u.%u MBit\n",
		   (curr_gw == gw_node ? "=>" : "  "),
		   gw_node->orig_node->orig,
		   router_ifinfo->bat_v.throughput / 10,
		   router_ifinfo->bat_v.throughput % 10, router->addr,
		   router->if_incoming->net_dev->name,
		   gw_node->bandwidth_down / 10,
		   gw_node->bandwidth_down % 10,
		   gw_node->bandwidth_up / 10,
		   gw_node->bandwidth_up % 10);
	ret = seq_has_overflowed(seq) ? -1 : 0;

	if (curr_gw)
		batadv_gw_node_put(curr_gw);
out:
	if (router_ifinfo)
		batadv_neigh_ifinfo_put(router_ifinfo);
	if (router)
		batadv_neigh_node_put(router);
	return ret;
}

/**
 * batadv_v_gw_print - print the gateway list
 * @bat_priv: the bat priv with all the soft interface information
 * @seq: gateway table seq_file struct
 */
static void batadv_v_gw_print(struct batadv_priv *bat_priv,
			      struct seq_file *seq)
{
	struct batadv_gw_node *gw_node;
	int gw_count = 0;

	seq_puts(seq,
		 "      Gateway        ( throughput)           Nexthop [outgoingIF]: advertised uplink bandwidth\n");

	rcu_read_lock();
	hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) {
		/* fails if orig_node has no router */
		if (batadv_v_gw_write_buffer_text(bat_priv, seq, gw_node) < 0)
			continue;

		gw_count++;
	}
	rcu_read_unlock();

	if (gw_count == 0)
		seq_puts(seq, "No gateways in range ...\n");
}

static struct batadv_algo_ops batadv_batman_v __read_mostly = {
static struct batadv_algo_ops batadv_batman_v __read_mostly = {
	.name = "BATMAN_V",
	.name = "BATMAN_V",
	.iface = {
	.iface = {
@@ -338,6 +577,13 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
	.orig = {
	.orig = {
		.print = batadv_v_orig_print,
		.print = batadv_v_orig_print,
	},
	},
	.gw = {
		.store_sel_class = batadv_v_store_sel_class,
		.show_sel_class = batadv_v_show_sel_class,
		.get_best_gw_node = batadv_v_gw_get_best_gw_node,
		.is_eligible = batadv_v_gw_is_eligible,
		.print = batadv_v_gw_print,
	},
};
};


/**
/**
@@ -363,7 +609,16 @@ void batadv_v_hardif_init(struct batadv_hard_iface *hard_iface)
 */
 */
int batadv_v_mesh_init(struct batadv_priv *bat_priv)
int batadv_v_mesh_init(struct batadv_priv *bat_priv)
{
{
	return batadv_v_ogm_init(bat_priv);
	int ret = 0;

	ret = batadv_v_ogm_init(bat_priv);
	if (ret < 0)
		return ret;

	/* set default throughput difference threshold to 5Mbps */
	atomic_set(&bat_priv->gw.sel_class, 50);

	return 0;
}
}


/**
/**
+1 −1
Original line number Original line Diff line number Diff line
@@ -1148,7 +1148,7 @@ static bool batadv_bla_process_claim(struct batadv_priv *bat_priv,


	/* Let the loopdetect frames on the mesh in any case. */
	/* Let the loopdetect frames on the mesh in any case. */
	if (bla_dst->type == BATADV_CLAIM_TYPE_LOOPDETECT)
	if (bla_dst->type == BATADV_CLAIM_TYPE_LOOPDETECT)
		return 0;
		return false;


	/* check if it is a claim frame. */
	/* check if it is a claim frame. */
	ret = batadv_check_claim_group(bat_priv, primary_if, hw_src, hw_dst,
	ret = batadv_check_claim_group(bat_priv, primary_if, hw_src, hw_dst,
+30 −192

File changed.

Preview size limit exceeded, changes collapsed.

Loading