1818#include <linux/if_ether.h>
1919#include <linux/jiffies.h>
2020#include <linux/kref.h>
21+ #include <linux/list.h>
2122#include <linux/minmax.h>
2223#include <linux/netdevice.h>
2324#include <linux/nl80211.h>
2627#include <linux/rcupdate.h>
2728#include <linux/rtnetlink.h>
2829#include <linux/skbuff.h>
30+ #include <linux/slab.h>
2931#include <linux/stddef.h>
3032#include <linux/string.h>
3133#include <linux/types.h>
4143#include "routing.h"
4244#include "send.h"
4345
46+ /**
47+ * struct batadv_v_metric_queue_entry - list of hardif neighbors which require
48+ * and metric update
49+ */
50+ struct batadv_v_metric_queue_entry {
51+ /** @hardif_neigh: hardif neighbor scheduled for metric update */
52+ struct batadv_hardif_neigh_node * hardif_neigh ;
53+
54+ /** @list: list node for metric_queue */
55+ struct list_head list ;
56+ };
57+
4458/**
4559 * batadv_v_elp_start_timer() - restart timer for ELP periodic work
4660 * @hard_iface: the interface for which the timer has to be reset
@@ -59,25 +73,36 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
5973/**
6074 * batadv_v_elp_get_throughput() - get the throughput towards a neighbour
6175 * @neigh: the neighbour for which the throughput has to be obtained
76+ * @pthroughput: calculated throughput towards the given neighbour in multiples
77+ * of 100kpbs (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc).
6278 *
63- * Return: The throughput towards the given neighbour in multiples of 100kpbs
64- * (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc).
79+ * Return: true when value behind @pthroughput was set
6580 */
66- static u32 batadv_v_elp_get_throughput (struct batadv_hardif_neigh_node * neigh )
81+ static bool batadv_v_elp_get_throughput (struct batadv_hardif_neigh_node * neigh ,
82+ u32 * pthroughput )
6783{
6884 struct batadv_hard_iface * hard_iface = neigh -> if_incoming ;
85+ struct net_device * soft_iface = hard_iface -> soft_iface ;
6986 struct ethtool_link_ksettings link_settings ;
7087 struct net_device * real_netdev ;
7188 struct station_info sinfo ;
7289 u32 throughput ;
7390 int ret ;
7491
92+ /* don't query throughput when no longer associated with any
93+ * batman-adv interface
94+ */
95+ if (!soft_iface )
96+ return false;
97+
7598 /* if the user specified a customised value for this interface, then
7699 * return it directly
77100 */
78101 throughput = atomic_read (& hard_iface -> bat_v .throughput_override );
79- if (throughput != 0 )
80- return throughput ;
102+ if (throughput != 0 ) {
103+ * pthroughput = throughput ;
104+ return true;
105+ }
81106
82107 /* if this is a wireless device, then ask its throughput through
83108 * cfg80211 API
@@ -104,27 +129,39 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
104129 * possible to delete this neighbor. For now set
105130 * the throughput metric to 0.
106131 */
107- return 0 ;
132+ * pthroughput = 0 ;
133+ return true;
108134 }
109135 if (ret )
110136 goto default_throughput ;
111137
112- if (sinfo .filled & BIT (NL80211_STA_INFO_EXPECTED_THROUGHPUT ))
113- return sinfo .expected_throughput / 100 ;
138+ if (sinfo .filled & BIT (NL80211_STA_INFO_EXPECTED_THROUGHPUT )) {
139+ * pthroughput = sinfo .expected_throughput / 100 ;
140+ return true;
141+ }
114142
115143 /* try to estimate the expected throughput based on reported tx
116144 * rates
117145 */
118- if (sinfo .filled & BIT (NL80211_STA_INFO_TX_BITRATE ))
119- return cfg80211_calculate_bitrate (& sinfo .txrate ) / 3 ;
146+ if (sinfo .filled & BIT (NL80211_STA_INFO_TX_BITRATE )) {
147+ * pthroughput = cfg80211_calculate_bitrate (& sinfo .txrate ) / 3 ;
148+ return true;
149+ }
120150
121151 goto default_throughput ;
122152 }
123153
154+ /* only use rtnl_trylock because the elp worker will be cancelled while
155+ * the rntl_lock is held. the cancel_delayed_work_sync() would otherwise
156+ * wait forever when the elp work_item was started and it is then also
157+ * trying to rtnl_lock
158+ */
159+ if (!rtnl_trylock ())
160+ return false;
161+
124162 /* if not a wifi interface, check if this device provides data via
125163 * ethtool (e.g. an Ethernet adapter)
126164 */
127- rtnl_lock ();
128165 ret = __ethtool_get_link_ksettings (hard_iface -> net_dev , & link_settings );
129166 rtnl_unlock ();
130167 if (ret == 0 ) {
@@ -135,13 +172,15 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
135172 hard_iface -> bat_v .flags &= ~BATADV_FULL_DUPLEX ;
136173
137174 throughput = link_settings .base .speed ;
138- if (throughput && throughput != SPEED_UNKNOWN )
139- return throughput * 10 ;
175+ if (throughput && throughput != SPEED_UNKNOWN ) {
176+ * pthroughput = throughput * 10 ;
177+ return true;
178+ }
140179 }
141180
142181default_throughput :
143182 if (!(hard_iface -> bat_v .flags & BATADV_WARNING_DEFAULT )) {
144- batadv_info (hard_iface -> soft_iface ,
183+ batadv_info (soft_iface ,
145184 "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" ,
146185 hard_iface -> net_dev -> name ,
147186 BATADV_THROUGHPUT_DEFAULT_VALUE / 10 ,
@@ -150,31 +189,26 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
150189 }
151190
152191 /* if none of the above cases apply, return the base_throughput */
153- return BATADV_THROUGHPUT_DEFAULT_VALUE ;
192+ * pthroughput = BATADV_THROUGHPUT_DEFAULT_VALUE ;
193+ return true;
154194}
155195
156196/**
157197 * batadv_v_elp_throughput_metric_update() - worker updating the throughput
158198 * metric of a single hop neighbour
159- * @work : the work queue item
199+ * @neigh : the neighbour to probe
160200 */
161- void batadv_v_elp_throughput_metric_update (struct work_struct * work )
201+ static void
202+ batadv_v_elp_throughput_metric_update (struct batadv_hardif_neigh_node * neigh )
162203{
163- struct batadv_hardif_neigh_node_bat_v * neigh_bat_v ;
164- struct batadv_hardif_neigh_node * neigh ;
165-
166- neigh_bat_v = container_of (work , struct batadv_hardif_neigh_node_bat_v ,
167- metric_work );
168- neigh = container_of (neigh_bat_v , struct batadv_hardif_neigh_node ,
169- bat_v );
204+ u32 throughput ;
205+ bool valid ;
170206
171- ewma_throughput_add (& neigh -> bat_v .throughput ,
172- batadv_v_elp_get_throughput (neigh ));
207+ valid = batadv_v_elp_get_throughput (neigh , & throughput );
208+ if (!valid )
209+ return ;
173210
174- /* decrement refcounter to balance increment performed before scheduling
175- * this task
176- */
177- batadv_hardif_neigh_put (neigh );
211+ ewma_throughput_add (& neigh -> bat_v .throughput , throughput );
178212}
179213
180214/**
@@ -248,14 +282,16 @@ batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh)
248282 */
249283static void batadv_v_elp_periodic_work (struct work_struct * work )
250284{
285+ struct batadv_v_metric_queue_entry * metric_entry ;
286+ struct batadv_v_metric_queue_entry * metric_safe ;
251287 struct batadv_hardif_neigh_node * hardif_neigh ;
252288 struct batadv_hard_iface * hard_iface ;
253289 struct batadv_hard_iface_bat_v * bat_v ;
254290 struct batadv_elp_packet * elp_packet ;
291+ struct list_head metric_queue ;
255292 struct batadv_priv * bat_priv ;
256293 struct sk_buff * skb ;
257294 u32 elp_interval ;
258- bool ret ;
259295
260296 bat_v = container_of (work , struct batadv_hard_iface_bat_v , elp_wq .work );
261297 hard_iface = container_of (bat_v , struct batadv_hard_iface , bat_v );
@@ -291,6 +327,8 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
291327
292328 atomic_inc (& hard_iface -> bat_v .elp_seqno );
293329
330+ INIT_LIST_HEAD (& metric_queue );
331+
294332 /* The throughput metric is updated on each sent packet. This way, if a
295333 * node is dead and no longer sends packets, batman-adv is still able to
296334 * react timely to its death.
@@ -315,16 +353,28 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
315353
316354 /* Reading the estimated throughput from cfg80211 is a task that
317355 * may sleep and that is not allowed in an rcu protected
318- * context. Therefore schedule a task for that.
356+ * context. Therefore add it to metric_queue and process it
357+ * outside rcu protected context.
319358 */
320- ret = queue_work (batadv_event_workqueue ,
321- & hardif_neigh -> bat_v .metric_work );
322-
323- if (!ret )
359+ metric_entry = kzalloc (sizeof (* metric_entry ), GFP_ATOMIC );
360+ if (!metric_entry ) {
324361 batadv_hardif_neigh_put (hardif_neigh );
362+ continue ;
363+ }
364+
365+ metric_entry -> hardif_neigh = hardif_neigh ;
366+ list_add (& metric_entry -> list , & metric_queue );
325367 }
326368 rcu_read_unlock ();
327369
370+ list_for_each_entry_safe (metric_entry , metric_safe , & metric_queue , list ) {
371+ batadv_v_elp_throughput_metric_update (metric_entry -> hardif_neigh );
372+
373+ batadv_hardif_neigh_put (metric_entry -> hardif_neigh );
374+ list_del (& metric_entry -> list );
375+ kfree (metric_entry );
376+ }
377+
328378restart_timer :
329379 batadv_v_elp_start_timer (hard_iface );
330380out :
0 commit comments