]>
Commit | Line | Data |
---|---|---|
7c598465 GKH |
1 | From 580da35a31f91a594f3090b7a2c39b85cb051a12 Mon Sep 17 00:00:00 2001 |
2 | From: Eric Dumazet <eric.dumazet@gmail.com> | |
3 | Date: Tue, 29 Nov 2011 22:31:23 +0100 | |
4 | Subject: IB: Fix RCU lockdep splats | |
5 | ||
6 | From: Eric Dumazet <eric.dumazet@gmail.com> | |
7 | ||
8 | commit 580da35a31f91a594f3090b7a2c39b85cb051a12 upstream. | |
9 | ||
10 | Commit f2c31e32b37 ("net: fix NULL dereferences in check_peer_redir()") | |
11 | forgot to take care of infiniband uses of dst neighbours. | |
12 | ||
13 | Many thanks to Marc Aurele who provided a nice bug report and feedback. | |
14 | ||
15 | Reported-by: Marc Aurele La France <tsi@ualberta.ca> | |
16 | Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> | |
17 | Cc: David Miller <davem@davemloft.net> | |
18 | Signed-off-by: Roland Dreier <roland@purestorage.com> | |
19 | ||
20 | --- | |
21 | drivers/infiniband/core/addr.c | 9 ++++++--- | |
22 | drivers/infiniband/hw/cxgb3/iwch_cm.c | 4 ++++ | |
23 | drivers/infiniband/hw/cxgb4/cm.c | 4 ++++ | |
24 | drivers/infiniband/hw/mlx4/qp.c | 2 +- | |
25 | drivers/infiniband/hw/nes/nes_cm.c | 6 ++++-- | |
26 | drivers/infiniband/ulp/ipoib/ipoib_main.c | 18 +++++++++++------- | |
27 | drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 6 ++++-- | |
28 | 7 files changed, 34 insertions(+), 15 deletions(-) | |
29 | ||
30 | --- a/drivers/infiniband/core/addr.c | |
31 | +++ b/drivers/infiniband/core/addr.c | |
32 | @@ -215,7 +215,9 @@ static int addr4_resolve(struct sockaddr | |
33 | ||
34 | neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); | |
35 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { | |
36 | + rcu_read_lock(); | |
37 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); | |
38 | + rcu_read_unlock(); | |
39 | ret = -ENODATA; | |
40 | if (neigh) | |
41 | goto release; | |
42 | @@ -273,15 +275,16 @@ static int addr6_resolve(struct sockaddr | |
43 | goto put; | |
44 | } | |
45 | ||
46 | + rcu_read_lock(); | |
47 | neigh = dst_get_neighbour(dst); | |
48 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { | |
49 | if (neigh) | |
50 | neigh_event_send(neigh, NULL); | |
51 | ret = -ENODATA; | |
52 | - goto put; | |
53 | + } else { | |
54 | + ret = rdma_copy_addr(addr, dst->dev, neigh->ha); | |
55 | } | |
56 | - | |
57 | - ret = rdma_copy_addr(addr, dst->dev, neigh->ha); | |
58 | + rcu_read_unlock(); | |
59 | put: | |
60 | dst_release(dst); | |
61 | return ret; | |
62 | --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c | |
63 | +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c | |
64 | @@ -1365,8 +1365,10 @@ static int pass_accept_req(struct t3cdev | |
65 | goto reject; | |
66 | } | |
67 | dst = &rt->dst; | |
68 | + rcu_read_lock(); | |
69 | neigh = dst_get_neighbour(dst); | |
70 | l2t = t3_l2t_get(tdev, neigh, neigh->dev); | |
71 | + rcu_read_unlock(); | |
72 | if (!l2t) { | |
73 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", | |
74 | __func__); | |
75 | @@ -1936,10 +1938,12 @@ int iwch_connect(struct iw_cm_id *cm_id, | |
76 | } | |
77 | ep->dst = &rt->dst; | |
78 | ||
79 | + rcu_read_lock(); | |
80 | neigh = dst_get_neighbour(ep->dst); | |
81 | ||
82 | /* get a l2t entry */ | |
83 | ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev); | |
84 | + rcu_read_unlock(); | |
85 | if (!ep->l2t) { | |
86 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); | |
87 | err = -ENOMEM; | |
88 | --- a/drivers/infiniband/hw/cxgb4/cm.c | |
89 | +++ b/drivers/infiniband/hw/cxgb4/cm.c | |
90 | @@ -1358,6 +1358,7 @@ static int pass_accept_req(struct c4iw_d | |
91 | goto reject; | |
92 | } | |
93 | dst = &rt->dst; | |
94 | + rcu_read_lock(); | |
95 | neigh = dst_get_neighbour(dst); | |
96 | if (neigh->dev->flags & IFF_LOOPBACK) { | |
97 | pdev = ip_dev_find(&init_net, peer_ip); | |
98 | @@ -1384,6 +1385,7 @@ static int pass_accept_req(struct c4iw_d | |
99 | rss_qid = dev->rdev.lldi.rxq_ids[ | |
100 | cxgb4_port_idx(neigh->dev) * step]; | |
101 | } | |
102 | + rcu_read_unlock(); | |
103 | if (!l2t) { | |
104 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", | |
105 | __func__); | |
106 | @@ -1909,6 +1911,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, | |
107 | } | |
108 | ep->dst = &rt->dst; | |
109 | ||
110 | + rcu_read_lock(); | |
111 | neigh = dst_get_neighbour(ep->dst); | |
112 | ||
113 | /* get a l2t entry */ | |
114 | @@ -1945,6 +1948,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, | |
115 | ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ | |
116 | cxgb4_port_idx(neigh->dev) * step]; | |
117 | } | |
118 | + rcu_read_unlock(); | |
119 | if (!ep->l2t) { | |
120 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); | |
121 | err = -ENOMEM; | |
122 | --- a/drivers/infiniband/hw/mlx4/qp.c | |
123 | +++ b/drivers/infiniband/hw/mlx4/qp.c | |
124 | @@ -1309,7 +1309,7 @@ static int build_mlx_header(struct mlx4_ | |
125 | int is_eth; | |
126 | int is_vlan = 0; | |
127 | int is_grh; | |
128 | - u16 vlan; | |
129 | + u16 vlan = 0; | |
130 | ||
131 | send_size = 0; | |
132 | for (i = 0; i < wr->num_sge; ++i) | |
133 | --- a/drivers/infiniband/hw/nes/nes_cm.c | |
134 | +++ b/drivers/infiniband/hw/nes/nes_cm.c | |
135 | @@ -1150,9 +1150,11 @@ static int nes_addr_resolve_neigh(struct | |
136 | neigh_release(neigh); | |
137 | } | |
138 | ||
139 | - if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) | |
140 | + if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) { | |
141 | + rcu_read_lock(); | |
142 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); | |
143 | - | |
144 | + rcu_read_unlock(); | |
145 | + } | |
146 | ip_rt_put(rt); | |
147 | return rc; | |
148 | } | |
149 | --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c | |
150 | +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c | |
151 | @@ -555,6 +555,7 @@ static int path_rec_start(struct net_dev | |
152 | return 0; | |
153 | } | |
154 | ||
155 | +/* called with rcu_read_lock */ | |
156 | static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) | |
157 | { | |
158 | struct ipoib_dev_priv *priv = netdev_priv(dev); | |
159 | @@ -636,6 +637,7 @@ err_drop: | |
160 | spin_unlock_irqrestore(&priv->lock, flags); | |
161 | } | |
162 | ||
163 | +/* called with rcu_read_lock */ | |
164 | static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev) | |
165 | { | |
166 | struct ipoib_dev_priv *priv = netdev_priv(skb->dev); | |
167 | @@ -720,13 +722,14 @@ static int ipoib_start_xmit(struct sk_bu | |
168 | struct neighbour *n = NULL; | |
169 | unsigned long flags; | |
170 | ||
171 | + rcu_read_lock(); | |
172 | if (likely(skb_dst(skb))) | |
173 | n = dst_get_neighbour(skb_dst(skb)); | |
174 | ||
175 | if (likely(n)) { | |
176 | if (unlikely(!*to_ipoib_neigh(n))) { | |
177 | ipoib_path_lookup(skb, dev); | |
178 | - return NETDEV_TX_OK; | |
179 | + goto unlock; | |
180 | } | |
181 | ||
182 | neigh = *to_ipoib_neigh(n); | |
183 | @@ -749,17 +752,17 @@ static int ipoib_start_xmit(struct sk_bu | |
184 | ipoib_neigh_free(dev, neigh); | |
185 | spin_unlock_irqrestore(&priv->lock, flags); | |
186 | ipoib_path_lookup(skb, dev); | |
187 | - return NETDEV_TX_OK; | |
188 | + goto unlock; | |
189 | } | |
190 | ||
191 | if (ipoib_cm_get(neigh)) { | |
192 | if (ipoib_cm_up(neigh)) { | |
193 | ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); | |
194 | - return NETDEV_TX_OK; | |
195 | + goto unlock; | |
196 | } | |
197 | } else if (neigh->ah) { | |
198 | ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha)); | |
199 | - return NETDEV_TX_OK; | |
200 | + goto unlock; | |
201 | } | |
202 | ||
203 | if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { | |
204 | @@ -793,13 +796,14 @@ static int ipoib_start_xmit(struct sk_bu | |
205 | phdr->hwaddr + 4); | |
206 | dev_kfree_skb_any(skb); | |
207 | ++dev->stats.tx_dropped; | |
208 | - return NETDEV_TX_OK; | |
209 | + goto unlock; | |
210 | } | |
211 | ||
212 | unicast_arp_send(skb, dev, phdr); | |
213 | } | |
214 | } | |
215 | - | |
216 | +unlock: | |
217 | + rcu_read_unlock(); | |
218 | return NETDEV_TX_OK; | |
219 | } | |
220 | ||
221 | @@ -837,7 +841,7 @@ static int ipoib_hard_header(struct sk_b | |
222 | dst = skb_dst(skb); | |
223 | n = NULL; | |
224 | if (dst) | |
225 | - n = dst_get_neighbour(dst); | |
226 | + n = dst_get_neighbour_raw(dst); | |
227 | if ((!dst || !n) && daddr) { | |
228 | struct ipoib_pseudoheader *phdr = | |
229 | (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); | |
230 | --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c | |
231 | +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c | |
232 | @@ -265,7 +265,7 @@ static int ipoib_mcast_join_finish(struc | |
233 | ||
234 | skb->dev = dev; | |
235 | if (dst) | |
236 | - n = dst_get_neighbour(dst); | |
237 | + n = dst_get_neighbour_raw(dst); | |
238 | if (!dst || !n) { | |
239 | /* put pseudoheader back on for next time */ | |
240 | skb_push(skb, sizeof (struct ipoib_pseudoheader)); | |
241 | @@ -721,6 +721,8 @@ out: | |
242 | if (mcast && mcast->ah) { | |
243 | struct dst_entry *dst = skb_dst(skb); | |
244 | struct neighbour *n = NULL; | |
245 | + | |
246 | + rcu_read_lock(); | |
247 | if (dst) | |
248 | n = dst_get_neighbour(dst); | |
249 | if (n && !*to_ipoib_neigh(n)) { | |
250 | @@ -733,7 +735,7 @@ out: | |
251 | list_add_tail(&neigh->list, &mcast->neigh_list); | |
252 | } | |
253 | } | |
254 | - | |
255 | + rcu_read_unlock(); | |
256 | spin_unlock_irqrestore(&priv->lock, flags); | |
257 | ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); | |
258 | return; |