Merge pull request #624 from ecsv/batadv-for-18.06
[feed/routing.git] / batman-adv / patches / 0039-batman-adv-Avoid-OGM-workqueue-synchronous-cancel-de.patch
1 From: Sven Eckelmann <sven@narfation.org>
2 Date: Sun, 13 Oct 2019 21:03:07 +0200
3 Subject: batman-adv: Avoid OGM workqueue synchronous cancel deadlock
4
5 batadv_forw_packet_list_free can be called when an interface is being
6 disabled. Under this circumstance, the rntl_lock will be held and while it
7 calls cancel_delayed_work_sync.
8
9 cancel_delayed_work_sync will stop the execution of the current context
10 when the work item is currently processed. It can now happen that the
11 cancel_delayed_work_sync was called when rtnl_lock was already called in
12 batadv_iv_send_outstanding_bat_ogm_packet or when it was in the process of
13 calling it. In this case, batadv_iv_send_outstanding_bat_ogm_packet waits
14 for the lock and cancel_delayed_work_sync (which holds the rtnl_lock) is
15 waiting for batadv_iv_send_outstanding_bat_ogm_packet to finish.
16
17 This can only be avoided by not using (conflicting) blocking locks while
18 cancel_delayed_work_sync is called. It also has the benefit that the
19 ogm scheduling functionality can avoid unnecessary delays which can be
20 introduced by a global lock.
21
22 Fixes: 9b8ceef26c69 ("batman-adv: Avoid free/alloc race when handling OGM buffer")
23 Signed-off-by: Sven Eckelmann <sven@narfation.org>
24
25 Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/d3be478f1aa27b47f61c4a62e18eb063d47c9168
26
27 diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
28 index 5b2ef12cfabb24ccbe2c1848cfff4d1ded9bd0b0..f5941837c3ad463f276cffdb25f9b6cd87af0e92 100644
29 --- a/net/batman-adv/bat_iv_ogm.c
30 +++ b/net/batman-adv/bat_iv_ogm.c
31 @@ -35,6 +35,7 @@
32 #include <linux/kref.h>
33 #include <linux/list.h>
34 #include <linux/lockdep.h>
35 +#include <linux/mutex.h>
36 #include <linux/netdevice.h>
37 #include <linux/netlink.h>
38 #include <linux/pkt_sched.h>
39 @@ -42,7 +43,6 @@
40 #include <linux/random.h>
41 #include <linux/rculist.h>
42 #include <linux/rcupdate.h>
43 -#include <linux/rtnetlink.h>
44 #include <linux/seq_file.h>
45 #include <linux/skbuff.h>
46 #include <linux/slab.h>
47 @@ -380,7 +380,7 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
48 unsigned char *ogm_buff;
49 u32 random_seqno;
50
51 - ASSERT_RTNL();
52 + mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
53
54 /* randomize initial seqno to avoid collision */
55 get_random_bytes(&random_seqno, sizeof(random_seqno));
56 @@ -388,8 +388,10 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
57
58 hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
59 ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
60 - if (!ogm_buff)
61 + if (!ogm_buff) {
62 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
63 return -ENOMEM;
64 + }
65
66 hard_iface->bat_iv.ogm_buff = ogm_buff;
67
68 @@ -401,41 +403,59 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
69 batadv_ogm_packet->reserved = 0;
70 batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE;
71
72 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
73 +
74 return 0;
75 }
76
77 static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
78 {
79 - ASSERT_RTNL();
80 + mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
81
82 kfree(hard_iface->bat_iv.ogm_buff);
83 hard_iface->bat_iv.ogm_buff = NULL;
84 +
85 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
86 }
87
88 static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
89 {
90 struct batadv_ogm_packet *batadv_ogm_packet;
91 - unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
92 + void *ogm_buff;
93
94 - ASSERT_RTNL();
95 + mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
96
97 - batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
98 + ogm_buff = hard_iface->bat_iv.ogm_buff;
99 + if (!ogm_buff)
100 + goto unlock;
101 +
102 + batadv_ogm_packet = ogm_buff;
103 ether_addr_copy(batadv_ogm_packet->orig,
104 hard_iface->net_dev->dev_addr);
105 ether_addr_copy(batadv_ogm_packet->prev_sender,
106 hard_iface->net_dev->dev_addr);
107 +
108 +unlock:
109 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
110 }
111
112 static void
113 batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
114 {
115 struct batadv_ogm_packet *batadv_ogm_packet;
116 - unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
117 + void *ogm_buff;
118
119 - ASSERT_RTNL();
120 + mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
121
122 - batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
123 + ogm_buff = hard_iface->bat_iv.ogm_buff;
124 + if (!ogm_buff)
125 + goto unlock;
126 +
127 + batadv_ogm_packet = ogm_buff;
128 batadv_ogm_packet->ttl = BATADV_TTL;
129 +
130 +unlock:
131 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
132 }
133
134 /* when do we schedule our own ogm to be sent */
135 @@ -933,7 +953,11 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface)
136 }
137 }
138
139 -static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
140 +/**
141 + * batadv_iv_ogm_schedule_buff() - schedule submission of hardif ogm buffer
142 + * @hard_iface: interface whose ogm buffer should be transmitted
143 + */
144 +static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
145 {
146 struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
147 unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
148 @@ -944,11 +968,7 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
149 u16 tvlv_len = 0;
150 unsigned long send_time;
151
152 - ASSERT_RTNL();
153 -
154 - if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
155 - hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
156 - return;
157 + lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex);
158
159 /* the interface gets activated here to avoid race conditions between
160 * the moment of activating the interface in
161 @@ -1016,6 +1036,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
162 batadv_hardif_put(primary_if);
163 }
164
165 +static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
166 +{
167 + if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
168 + hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
169 + return;
170 +
171 + mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
172 + batadv_iv_ogm_schedule_buff(hard_iface);
173 + mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex);
174 +}
175 +
176 /**
177 * batadv_iv_ogm_orig_update() - use OGM to update corresponding data in an
178 * originator
179 @@ -1802,12 +1833,16 @@ static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset,
180 batadv_orig_node_put(orig_node);
181 }
182
183 -static void
184 -batadv_iv_send_outstanding_forw_packet(struct batadv_forw_packet *forw_packet)
185 +static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
186 {
187 + struct delayed_work *delayed_work;
188 + struct batadv_forw_packet *forw_packet;
189 struct batadv_priv *bat_priv;
190 bool dropped = false;
191
192 + delayed_work = to_delayed_work(work);
193 + forw_packet = container_of(delayed_work, struct batadv_forw_packet,
194 + delayed_work);
195 bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface);
196
197 if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) {
198 @@ -1836,20 +1871,6 @@ batadv_iv_send_outstanding_forw_packet(struct batadv_forw_packet *forw_packet)
199 batadv_forw_packet_free(forw_packet, dropped);
200 }
201
202 -static void batadv_iv_send_outstanding_bat_ogm_packet(struct work_struct *work)
203 -{
204 - struct delayed_work *delayed_work;
205 - struct batadv_forw_packet *forw_packet;
206 -
207 - delayed_work = to_delayed_work(work);
208 - forw_packet = container_of(delayed_work, struct batadv_forw_packet,
209 - delayed_work);
210 -
211 - rtnl_lock();
212 - batadv_iv_send_outstanding_forw_packet(forw_packet);
213 - rtnl_unlock();
214 -}
215 -
216 static int batadv_iv_ogm_receive(struct sk_buff *skb,
217 struct batadv_hard_iface *if_incoming)
218 {
219 diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
220 index 36f0962040d16af4f9ed82629ff03ce85c83ed57..c4e0435c952db87c89727633c184320820812cda 100644
221 --- a/net/batman-adv/hard-interface.c
222 +++ b/net/batman-adv/hard-interface.c
223 @@ -29,6 +29,7 @@
224 #include <linux/kernel.h>
225 #include <linux/kref.h>
226 #include <linux/list.h>
227 +#include <linux/mutex.h>
228 #include <linux/netdevice.h>
229 #include <linux/printk.h>
230 #include <linux/rculist.h>
231 @@ -933,6 +934,7 @@ batadv_hardif_add_interface(struct net_device *net_dev)
232 INIT_LIST_HEAD(&hard_iface->list);
233 INIT_HLIST_HEAD(&hard_iface->neigh_list);
234
235 + mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
236 spin_lock_init(&hard_iface->neigh_list_lock);
237 kref_init(&hard_iface->refcount);
238
239 diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
240 index 44c423447fe163eb3b9df5ec5cf229bed6b8d65b..85f52dc42e17f7ed550f13048a2e2bd9d372196b 100644
241 --- a/net/batman-adv/types.h
242 +++ b/net/batman-adv/types.h
243 @@ -83,14 +83,17 @@ enum batadv_dhcp_recipient {
244 * struct batadv_hard_iface_bat_iv - per hard-interface B.A.T.M.A.N. IV data
245 */
246 struct batadv_hard_iface_bat_iv {
247 - /** @ogm_buff: buffer holding the OGM packet. rtnl protected */
248 + /** @ogm_buff: buffer holding the OGM packet */
249 unsigned char *ogm_buff;
250
251 - /** @ogm_buff_len: length of the OGM packet buffer. rtnl protected */
252 + /** @ogm_buff_len: length of the OGM packet buffer */
253 int ogm_buff_len;
254
255 /** @ogm_seqno: OGM sequence number - used to identify each OGM */
256 atomic_t ogm_seqno;
257 +
258 + /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */
259 + struct mutex ogm_buff_mutex;
260 };
261
262 /**