aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntonio Quartulli <antonio@open-mesh.com>2015-11-10 12:50:51 -0500
committerAntonio Quartulli <a@unstable.cc>2016-02-29 03:25:06 -0500
commitc833484e5f3872a38fe232c663586069d5ad9645 (patch)
treed4ab62093f50a411de1fb0ed6bef90634fbe61b1
parent95d392784dd0a51e4216e075f04a68c922745985 (diff)
batman-adv: ELP - compute the metric based on the estimated throughput
In case of wireless interface retrieve the throughput by querying cfg80211. To perform this call a separate work must be scheduled because the function may sleep and this is not allowed within an RCU protected context (RCU in this case is used to iterate over all the neighbours). Use ethtool to retrieve information about an Ethernet link like HALF/FULL_DUPLEX and advertised bandwidth (e.g. 100/10Mbps). The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection. Signed-off-by: Antonio Quartulli <antonio@open-mesh.com> Signed-off-by: Marek Lindner <mareklindner@neomailbox.ch>
-rw-r--r--net/batman-adv/Kconfig2
-rw-r--r--net/batman-adv/bat_v.c3
-rw-r--r--net/batman-adv/bat_v_elp.c140
-rw-r--r--net/batman-adv/bat_v_elp.h2
-rw-r--r--net/batman-adv/bat_v_ogm.c2
-rw-r--r--net/batman-adv/main.h1
-rw-r--r--net/batman-adv/types.h16
7 files changed, 164 insertions, 2 deletions
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig
index 5c148a8489da..e651dc927bfd 100644
--- a/net/batman-adv/Kconfig
+++ b/net/batman-adv/Kconfig
@@ -17,7 +17,7 @@ config BATMAN_ADV
17 17
18config BATMAN_ADV_BATMAN_V 18config BATMAN_ADV_BATMAN_V
19 bool "B.A.T.M.A.N. V protocol (experimental)" 19 bool "B.A.T.M.A.N. V protocol (experimental)"
20 depends on BATMAN_ADV 20 depends on BATMAN_ADV && CFG80211
21 default n 21 default n
22 help 22 help
23 This option enables the B.A.T.M.A.N. V protocol, the successor 23 This option enables the B.A.T.M.A.N. V protocol, the successor
diff --git a/net/batman-adv/bat_v.c b/net/batman-adv/bat_v.c
index d9cb5c4922c1..ff31f2af9cfe 100644
--- a/net/batman-adv/bat_v.c
+++ b/net/batman-adv/bat_v.c
@@ -21,6 +21,7 @@
21#include <linux/atomic.h> 21#include <linux/atomic.h>
22#include <linux/cache.h> 22#include <linux/cache.h>
23#include <linux/init.h> 23#include <linux/init.h>
24#include <linux/workqueue.h>
24 25
25#include "bat_v_elp.h" 26#include "bat_v_elp.h"
26#include "bat_v_ogm.h" 27#include "bat_v_ogm.h"
@@ -65,6 +66,8 @@ static void
65batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh) 66batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh)
66{ 67{
67 ewma_throughput_init(&hardif_neigh->bat_v.throughput); 68 ewma_throughput_init(&hardif_neigh->bat_v.throughput);
69 INIT_WORK(&hardif_neigh->bat_v.metric_work,
70 batadv_v_elp_throughput_metric_update);
68} 71}
69 72
70static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface) 73static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 1e4d13cc267f..8376730ebd69 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -22,19 +22,23 @@
22#include <linux/byteorder/generic.h> 22#include <linux/byteorder/generic.h>
23#include <linux/errno.h> 23#include <linux/errno.h>
24#include <linux/etherdevice.h> 24#include <linux/etherdevice.h>
25#include <linux/ethtool.h>
25#include <linux/fs.h> 26#include <linux/fs.h>
26#include <linux/if_ether.h> 27#include <linux/if_ether.h>
27#include <linux/jiffies.h> 28#include <linux/jiffies.h>
28#include <linux/kernel.h> 29#include <linux/kernel.h>
30#include <linux/kref.h>
29#include <linux/netdevice.h> 31#include <linux/netdevice.h>
30#include <linux/random.h> 32#include <linux/random.h>
31#include <linux/rculist.h> 33#include <linux/rculist.h>
32#include <linux/rcupdate.h> 34#include <linux/rcupdate.h>
35#include <linux/rtnetlink.h>
33#include <linux/skbuff.h> 36#include <linux/skbuff.h>
34#include <linux/stddef.h> 37#include <linux/stddef.h>
35#include <linux/string.h> 38#include <linux/string.h>
36#include <linux/types.h> 39#include <linux/types.h>
37#include <linux/workqueue.h> 40#include <linux/workqueue.h>
41#include <net/cfg80211.h>
38 42
39#include "bat_algo.h" 43#include "bat_algo.h"
40#include "bat_v_ogm.h" 44#include "bat_v_ogm.h"
@@ -60,6 +64,107 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
60} 64}
61 65
62/** 66/**
67 * batadv_v_elp_get_throughput - get the throughput towards a neighbour
68 * @neigh: the neighbour for which the throughput has to be obtained
69 *
70 * Return: The throughput towards the given neighbour in multiples of 100kpbs
71 * (a value of '1' equals to 0.1Mbps, '10' equals 1Mbps, etc).
72 */
73static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
74{
75 struct batadv_hard_iface *hard_iface = neigh->if_incoming;
76 struct ethtool_link_ksettings link_settings;
77 struct station_info sinfo;
78 u32 throughput;
79 int ret;
80
81 /* if the user specified a customised value for this interface, then
82 * return it directly
83 */
84 throughput = atomic_read(&hard_iface->bat_v.throughput_override);
85 if (throughput != 0)
86 return throughput;
87
88 /* if this is a wireless device, then ask its throughput through
89 * cfg80211 API
90 */
91 if (batadv_is_wifi_netdev(hard_iface->net_dev)) {
92 if (hard_iface->net_dev->ieee80211_ptr) {
93 ret = cfg80211_get_station(hard_iface->net_dev,
94 neigh->addr, &sinfo);
95 if (ret == -ENOENT) {
96 /* Node is not associated anymore! It would be
97 * possible to delete this neighbor. For now set
98 * the throughput metric to 0.
99 */
100 return 0;
101 }
102 if (!ret)
103 return sinfo.expected_throughput / 100;
104 }
105
106 /* unsupported WiFi driver version */
107 goto default_throughput;
108 }
109
110 /* if not a wifi interface, check if this device provides data via
111 * ethtool (e.g. an Ethernet adapter)
112 */
113 memset(&link_settings, 0, sizeof(link_settings));
114 rtnl_lock();
115 ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings);
116 rtnl_unlock();
117 if (ret == 0) {
118 /* link characteristics might change over time */
119 if (link_settings.base.duplex == DUPLEX_FULL)
120 hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
121 else
122 hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
123
124 throughput = link_settings.base.speed;
125 if (throughput && (throughput != SPEED_UNKNOWN))
126 return throughput * 10;
127 }
128
129default_throughput:
130 if (!(hard_iface->bat_v.flags & BATADV_WARNING_DEFAULT)) {
131 batadv_info(hard_iface->soft_iface,
132 "WiFi driver or ethtool info does not provide information about link speeds on interface %s, therefore defaulting to hardcoded throughput values of %u.%1u Mbps. Consider overriding the throughput manually or checking your driver.\n",
133 hard_iface->net_dev->name,
134 BATADV_THROUGHPUT_DEFAULT_VALUE / 10,
135 BATADV_THROUGHPUT_DEFAULT_VALUE % 10);
136 hard_iface->bat_v.flags |= BATADV_WARNING_DEFAULT;
137 }
138
139 /* if none of the above cases apply, return the base_throughput */
140 return BATADV_THROUGHPUT_DEFAULT_VALUE;
141}
142
143/**
144 * batadv_v_elp_throughput_metric_update - worker updating the throughput metric
145 * of a single hop neighbour
146 * @work: the work queue item
147 */
148void batadv_v_elp_throughput_metric_update(struct work_struct *work)
149{
150 struct batadv_hardif_neigh_node_bat_v *neigh_bat_v;
151 struct batadv_hardif_neigh_node *neigh;
152
153 neigh_bat_v = container_of(work, struct batadv_hardif_neigh_node_bat_v,
154 metric_work);
155 neigh = container_of(neigh_bat_v, struct batadv_hardif_neigh_node,
156 bat_v);
157
158 ewma_throughput_add(&neigh->bat_v.throughput,
159 batadv_v_elp_get_throughput(neigh));
160
161 /* decrement refcounter to balance increment performed before scheduling
162 * this task
163 */
164 batadv_hardif_neigh_put(neigh);
165}
166
167/**
63 * batadv_v_elp_periodic_work - ELP periodic task per interface 168 * batadv_v_elp_periodic_work - ELP periodic task per interface
64 * @work: work queue item 169 * @work: work queue item
65 * 170 *
@@ -67,6 +172,7 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
67 */ 172 */
68static void batadv_v_elp_periodic_work(struct work_struct *work) 173static void batadv_v_elp_periodic_work(struct work_struct *work)
69{ 174{
175 struct batadv_hardif_neigh_node *hardif_neigh;
70 struct batadv_hard_iface *hard_iface; 176 struct batadv_hard_iface *hard_iface;
71 struct batadv_hard_iface_bat_v *bat_v; 177 struct batadv_hard_iface_bat_v *bat_v;
72 struct batadv_elp_packet *elp_packet; 178 struct batadv_elp_packet *elp_packet;
@@ -108,6 +214,31 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
108 214
109 atomic_inc(&hard_iface->bat_v.elp_seqno); 215 atomic_inc(&hard_iface->bat_v.elp_seqno);
110 216
217 /* The throughput metric is updated on each sent packet. This way, if a
218 * node is dead and no longer sends packets, batman-adv is still able to
219 * react timely to its death.
220 *
221 * The throughput metric is updated by following these steps:
222 * 1) if the hard_iface is wifi => send a number of unicast ELPs for
223 * probing/sampling to each neighbor
224 * 2) update the throughput metric value of each neighbor (note that the
225 * value retrieved in this step might be 100ms old because the
226 * probing packets at point 1) could still be in the HW queue)
227 */
228 rcu_read_lock();
229 hlist_for_each_entry_rcu(hardif_neigh, &hard_iface->neigh_list, list) {
230 if (!kref_get_unless_zero(&hardif_neigh->refcount))
231 continue;
232
233 /* Reading the estimated throughput from cfg80211 is a task that
234 * may sleep and that is not allowed in an rcu protected
235 * context. Therefore schedule a task for that.
236 */
237 queue_work(batadv_event_workqueue,
238 &hardif_neigh->bat_v.metric_work);
239 }
240 rcu_read_unlock();
241
111restart_timer: 242restart_timer:
112 batadv_v_elp_start_timer(hard_iface); 243 batadv_v_elp_start_timer(hard_iface);
113out: 244out:
@@ -146,6 +277,15 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface)
146 atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno); 277 atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno);
147 atomic_set(&hard_iface->bat_v.elp_interval, 500); 278 atomic_set(&hard_iface->bat_v.elp_interval, 500);
148 279
280 /* assume full-duplex by default */
281 hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
282
283 /* warn the user (again) if there is no throughput data is available */
284 hard_iface->bat_v.flags &= ~BATADV_WARNING_DEFAULT;
285
286 if (batadv_is_wifi_netdev(hard_iface->net_dev))
287 hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
288
149 INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, 289 INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq,
150 batadv_v_elp_periodic_work); 290 batadv_v_elp_periodic_work);
151 batadv_v_elp_start_timer(hard_iface); 291 batadv_v_elp_start_timer(hard_iface);
diff --git a/net/batman-adv/bat_v_elp.h b/net/batman-adv/bat_v_elp.h
index 5a7bc398a9ef..e95f1bca0785 100644
--- a/net/batman-adv/bat_v_elp.h
+++ b/net/batman-adv/bat_v_elp.h
@@ -21,11 +21,13 @@
21#define _NET_BATMAN_ADV_BAT_V_ELP_H_ 21#define _NET_BATMAN_ADV_BAT_V_ELP_H_
22 22
23struct sk_buff; 23struct sk_buff;
24struct work_struct;
24 25
25int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface); 26int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface);
26void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface); 27void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface);
27void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface); 28void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface);
28int batadv_v_elp_packet_recv(struct sk_buff *skb, 29int batadv_v_elp_packet_recv(struct sk_buff *skb,
29 struct batadv_hard_iface *if_incoming); 30 struct batadv_hard_iface *if_incoming);
31void batadv_v_elp_throughput_metric_update(struct work_struct *work);
30 32
31#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ 33#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index c8000962a266..d9bcbe6e7d65 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -339,7 +339,7 @@ static u32 batadv_v_forward_penalty(struct batadv_priv *bat_priv,
339 */ 339 */
340 if ((throughput > 10) && 340 if ((throughput > 10) &&
341 (if_incoming == if_outgoing) && 341 (if_incoming == if_outgoing) &&
342 (batadv_is_wifi_netdev(if_incoming->net_dev))) 342 !(if_incoming->bat_v.flags & BATADV_FULL_DUPLEX))
343 return throughput / 2; 343 return throughput / 2;
344 344
345 /* hop penalty of 255 equals 100% */ 345 /* hop penalty of 255 equals 100% */
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index 2e795446208f..5c68bf2618c7 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -62,6 +62,7 @@
62#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1 62#define BATADV_TQ_TOTAL_BIDRECT_LIMIT 1
63 63
64/* B.A.T.M.A.N. V */ 64/* B.A.T.M.A.N. V */
65#define BATADV_THROUGHPUT_DEFAULT_VALUE 10 /* 1 Mbps */
65#define BATADV_ELP_MAX_AGE 64 66#define BATADV_ELP_MAX_AGE 64
66#define BATADV_OGM_MAX_ORIGDIFF 5 67#define BATADV_OGM_MAX_ORIGDIFF 5
67#define BATADV_OGM_MAX_AGE 64 68#define BATADV_OGM_MAX_AGE 64
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index c56bb8835e60..9abfb3e73c34 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -87,12 +87,25 @@ struct batadv_hard_iface_bat_iv {
87}; 87};
88 88
89/** 89/**
90 * enum batadv_v_hard_iface_flags - interface flags useful to B.A.T.M.A.N. V
91 * @BATADV_FULL_DUPLEX: tells if the connection over this link is full-duplex
92 * @BATADV_WARNING_DEFAULT: tells whether we have warned the user that no
93 * throughput data is available for this interface and that default values are
94 * assumed.
95 */
96enum batadv_v_hard_iface_flags {
97 BATADV_FULL_DUPLEX = BIT(0),
98 BATADV_WARNING_DEFAULT = BIT(1),
99};
100
101/**
90 * struct batadv_hard_iface_bat_v - per hard-interface B.A.T.M.A.N. V data 102 * struct batadv_hard_iface_bat_v - per hard-interface B.A.T.M.A.N. V data
91 * @elp_interval: time interval between two ELP transmissions 103 * @elp_interval: time interval between two ELP transmissions
92 * @elp_seqno: current ELP sequence number 104 * @elp_seqno: current ELP sequence number
93 * @elp_skb: base skb containing the ELP message to send 105 * @elp_skb: base skb containing the ELP message to send
94 * @elp_wq: workqueue used to schedule ELP transmissions 106 * @elp_wq: workqueue used to schedule ELP transmissions
95 * @throughput_override: throughput override to disable link auto-detection 107 * @throughput_override: throughput override to disable link auto-detection
108 * @flags: interface specific flags
96 */ 109 */
97struct batadv_hard_iface_bat_v { 110struct batadv_hard_iface_bat_v {
98 atomic_t elp_interval; 111 atomic_t elp_interval;
@@ -100,6 +113,7 @@ struct batadv_hard_iface_bat_v {
100 struct sk_buff *elp_skb; 113 struct sk_buff *elp_skb;
101 struct delayed_work elp_wq; 114 struct delayed_work elp_wq;
102 atomic_t throughput_override; 115 atomic_t throughput_override;
116 u8 flags;
103}; 117};
104 118
105/** 119/**
@@ -378,12 +392,14 @@ DECLARE_EWMA(throughput, 1024, 8)
378 * @elp_interval: time interval between two ELP transmissions 392 * @elp_interval: time interval between two ELP transmissions
379 * @elp_latest_seqno: latest and best known ELP sequence number 393 * @elp_latest_seqno: latest and best known ELP sequence number
380 * @last_unicast_tx: when the last unicast packet has been sent to this neighbor 394 * @last_unicast_tx: when the last unicast packet has been sent to this neighbor
395 * @metric_work: work queue callback item for metric update
381 */ 396 */
382struct batadv_hardif_neigh_node_bat_v { 397struct batadv_hardif_neigh_node_bat_v {
383 struct ewma_throughput throughput; 398 struct ewma_throughput throughput;
384 u32 elp_interval; 399 u32 elp_interval;
385 u32 elp_latest_seqno; 400 u32 elp_latest_seqno;
386 unsigned long last_unicast_tx; 401 unsigned long last_unicast_tx;
402 struct work_struct metric_work;
387}; 403};
388 404
389/** 405/**