18
18
#include <linux/if_ether.h>
19
19
#include <linux/jiffies.h>
20
20
#include <linux/kref.h>
21
+ #include <linux/list.h>
21
22
#include <linux/minmax.h>
22
23
#include <linux/netdevice.h>
23
24
#include <linux/nl80211.h>
26
27
#include <linux/rcupdate.h>
27
28
#include <linux/rtnetlink.h>
28
29
#include <linux/skbuff.h>
30
+ #include <linux/slab.h>
29
31
#include <linux/stddef.h>
30
32
#include <linux/string.h>
31
33
#include <linux/types.h>
41
43
#include "routing.h"
42
44
#include "send.h"
43
45
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
+
44
58
/**
45
59
* batadv_v_elp_start_timer() - restart timer for ELP periodic work
46
60
* @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)
59
73
/**
60
74
* batadv_v_elp_get_throughput() - get the throughput towards a neighbour
61
75
* @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).
62
78
*
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
65
80
*/
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 )
67
83
{
68
84
struct batadv_hard_iface * hard_iface = neigh -> if_incoming ;
85
+ struct net_device * soft_iface = hard_iface -> soft_iface ;
69
86
struct ethtool_link_ksettings link_settings ;
70
87
struct net_device * real_netdev ;
71
88
struct station_info sinfo ;
72
89
u32 throughput ;
73
90
int ret ;
74
91
92
+ /* don't query throughput when no longer associated with any
93
+ * batman-adv interface
94
+ */
95
+ if (!soft_iface )
96
+ return false;
97
+
75
98
/* if the user specified a customised value for this interface, then
76
99
* return it directly
77
100
*/
78
101
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
+ }
81
106
82
107
/* if this is a wireless device, then ask its throughput through
83
108
* cfg80211 API
@@ -104,27 +129,39 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
104
129
* possible to delete this neighbor. For now set
105
130
* the throughput metric to 0.
106
131
*/
107
- return 0 ;
132
+ * pthroughput = 0 ;
133
+ return true;
108
134
}
109
135
if (ret )
110
136
goto default_throughput ;
111
137
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
+ }
114
142
115
143
/* try to estimate the expected throughput based on reported tx
116
144
* rates
117
145
*/
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
+ }
120
150
121
151
goto default_throughput ;
122
152
}
123
153
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
+
124
162
/* if not a wifi interface, check if this device provides data via
125
163
* ethtool (e.g. an Ethernet adapter)
126
164
*/
127
- rtnl_lock ();
128
165
ret = __ethtool_get_link_ksettings (hard_iface -> net_dev , & link_settings );
129
166
rtnl_unlock ();
130
167
if (ret == 0 ) {
@@ -135,13 +172,15 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
135
172
hard_iface -> bat_v .flags &= ~BATADV_FULL_DUPLEX ;
136
173
137
174
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
+ }
140
179
}
141
180
142
181
default_throughput :
143
182
if (!(hard_iface -> bat_v .flags & BATADV_WARNING_DEFAULT )) {
144
- batadv_info (hard_iface -> soft_iface ,
183
+ batadv_info (soft_iface ,
145
184
"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" ,
146
185
hard_iface -> net_dev -> name ,
147
186
BATADV_THROUGHPUT_DEFAULT_VALUE / 10 ,
@@ -150,31 +189,26 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)
150
189
}
151
190
152
191
/* 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;
154
194
}
155
195
156
196
/**
157
197
* batadv_v_elp_throughput_metric_update() - worker updating the throughput
158
198
* metric of a single hop neighbour
159
- * @work : the work queue item
199
+ * @neigh : the neighbour to probe
160
200
*/
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 )
162
203
{
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 ;
170
206
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 ;
173
210
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 );
178
212
}
179
213
180
214
/**
@@ -248,14 +282,16 @@ batadv_v_elp_wifi_neigh_probe(struct batadv_hardif_neigh_node *neigh)
248
282
*/
249
283
static void batadv_v_elp_periodic_work (struct work_struct * work )
250
284
{
285
+ struct batadv_v_metric_queue_entry * metric_entry ;
286
+ struct batadv_v_metric_queue_entry * metric_safe ;
251
287
struct batadv_hardif_neigh_node * hardif_neigh ;
252
288
struct batadv_hard_iface * hard_iface ;
253
289
struct batadv_hard_iface_bat_v * bat_v ;
254
290
struct batadv_elp_packet * elp_packet ;
291
+ struct list_head metric_queue ;
255
292
struct batadv_priv * bat_priv ;
256
293
struct sk_buff * skb ;
257
294
u32 elp_interval ;
258
- bool ret ;
259
295
260
296
bat_v = container_of (work , struct batadv_hard_iface_bat_v , elp_wq .work );
261
297
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)
291
327
292
328
atomic_inc (& hard_iface -> bat_v .elp_seqno );
293
329
330
+ INIT_LIST_HEAD (& metric_queue );
331
+
294
332
/* The throughput metric is updated on each sent packet. This way, if a
295
333
* node is dead and no longer sends packets, batman-adv is still able to
296
334
* react timely to its death.
@@ -315,16 +353,28 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
315
353
316
354
/* Reading the estimated throughput from cfg80211 is a task that
317
355
* 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.
319
358
*/
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 ) {
324
361
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 );
325
367
}
326
368
rcu_read_unlock ();
327
369
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
+
328
378
restart_timer :
329
379
batadv_v_elp_start_timer (hard_iface );
330
380
out :
0 commit comments