diff options
-rw-r--r-- | net/ipv4/ip_gre.c | 40 |
1 files changed, 23 insertions, 17 deletions
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 89ff9d5b1500..40f043915235 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c | |||
@@ -156,8 +156,13 @@ struct ipgre_net { | |||
156 | #define tunnels_r tunnels[2] | 156 | #define tunnels_r tunnels[2] |
157 | #define tunnels_l tunnels[1] | 157 | #define tunnels_l tunnels[1] |
158 | #define tunnels_wc tunnels[0] | 158 | #define tunnels_wc tunnels[0] |
159 | /* | ||
160 | * Locking : hash tables are protected by RCU and a spinlock | ||
161 | */ | ||
162 | static DEFINE_SPINLOCK(ipgre_lock); | ||
159 | 163 | ||
160 | static DEFINE_RWLOCK(ipgre_lock); | 164 | #define for_each_ip_tunnel_rcu(start) \ |
165 | for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) | ||
161 | 166 | ||
162 | /* Given src, dst and key, find appropriate for input tunnel. */ | 167 | /* Given src, dst and key, find appropriate for input tunnel. */ |
163 | 168 | ||
@@ -175,7 +180,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev, | |||
175 | ARPHRD_ETHER : ARPHRD_IPGRE; | 180 | ARPHRD_ETHER : ARPHRD_IPGRE; |
176 | int score, cand_score = 4; | 181 | int score, cand_score = 4; |
177 | 182 | ||
178 | for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) { | 183 | for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) { |
179 | if (local != t->parms.iph.saddr || | 184 | if (local != t->parms.iph.saddr || |
180 | remote != t->parms.iph.daddr || | 185 | remote != t->parms.iph.daddr || |
181 | key != t->parms.i_key || | 186 | key != t->parms.i_key || |
@@ -200,7 +205,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev, | |||
200 | } | 205 | } |
201 | } | 206 | } |
202 | 207 | ||
203 | for (t = ign->tunnels_r[h0^h1]; t; t = t->next) { | 208 | for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) { |
204 | if (remote != t->parms.iph.daddr || | 209 | if (remote != t->parms.iph.daddr || |
205 | key != t->parms.i_key || | 210 | key != t->parms.i_key || |
206 | !(t->dev->flags & IFF_UP)) | 211 | !(t->dev->flags & IFF_UP)) |
@@ -224,7 +229,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev, | |||
224 | } | 229 | } |
225 | } | 230 | } |
226 | 231 | ||
227 | for (t = ign->tunnels_l[h1]; t; t = t->next) { | 232 | for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) { |
228 | if ((local != t->parms.iph.saddr && | 233 | if ((local != t->parms.iph.saddr && |
229 | (local != t->parms.iph.daddr || | 234 | (local != t->parms.iph.daddr || |
230 | !ipv4_is_multicast(local))) || | 235 | !ipv4_is_multicast(local))) || |
@@ -250,7 +255,7 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev, | |||
250 | } | 255 | } |
251 | } | 256 | } |
252 | 257 | ||
253 | for (t = ign->tunnels_wc[h1]; t; t = t->next) { | 258 | for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) { |
254 | if (t->parms.i_key != key || | 259 | if (t->parms.i_key != key || |
255 | !(t->dev->flags & IFF_UP)) | 260 | !(t->dev->flags & IFF_UP)) |
256 | continue; | 261 | continue; |
@@ -276,8 +281,9 @@ static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev, | |||
276 | if (cand != NULL) | 281 | if (cand != NULL) |
277 | return cand; | 282 | return cand; |
278 | 283 | ||
279 | if (ign->fb_tunnel_dev->flags & IFF_UP) | 284 | dev = ign->fb_tunnel_dev; |
280 | return netdev_priv(ign->fb_tunnel_dev); | 285 | if (dev->flags & IFF_UP) |
286 | return netdev_priv(dev); | ||
281 | 287 | ||
282 | return NULL; | 288 | return NULL; |
283 | } | 289 | } |
@@ -311,10 +317,10 @@ static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t) | |||
311 | { | 317 | { |
312 | struct ip_tunnel **tp = ipgre_bucket(ign, t); | 318 | struct ip_tunnel **tp = ipgre_bucket(ign, t); |
313 | 319 | ||
320 | spin_lock_bh(&ipgre_lock); | ||
314 | t->next = *tp; | 321 | t->next = *tp; |
315 | write_lock_bh(&ipgre_lock); | 322 | rcu_assign_pointer(*tp, t); |
316 | *tp = t; | 323 | spin_unlock_bh(&ipgre_lock); |
317 | write_unlock_bh(&ipgre_lock); | ||
318 | } | 324 | } |
319 | 325 | ||
320 | static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t) | 326 | static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t) |
@@ -323,9 +329,9 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t) | |||
323 | 329 | ||
324 | for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) { | 330 | for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) { |
325 | if (t == *tp) { | 331 | if (t == *tp) { |
326 | write_lock_bh(&ipgre_lock); | 332 | spin_lock_bh(&ipgre_lock); |
327 | *tp = t->next; | 333 | *tp = t->next; |
328 | write_unlock_bh(&ipgre_lock); | 334 | spin_unlock_bh(&ipgre_lock); |
329 | break; | 335 | break; |
330 | } | 336 | } |
331 | } | 337 | } |
@@ -476,7 +482,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
476 | break; | 482 | break; |
477 | } | 483 | } |
478 | 484 | ||
479 | read_lock(&ipgre_lock); | 485 | rcu_read_lock(); |
480 | t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr, | 486 | t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr, |
481 | flags & GRE_KEY ? | 487 | flags & GRE_KEY ? |
482 | *(((__be32 *)p) + (grehlen / 4) - 1) : 0, | 488 | *(((__be32 *)p) + (grehlen / 4) - 1) : 0, |
@@ -494,7 +500,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
494 | t->err_count = 1; | 500 | t->err_count = 1; |
495 | t->err_time = jiffies; | 501 | t->err_time = jiffies; |
496 | out: | 502 | out: |
497 | read_unlock(&ipgre_lock); | 503 | rcu_read_unlock(); |
498 | return; | 504 | return; |
499 | } | 505 | } |
500 | 506 | ||
@@ -573,7 +579,7 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
573 | 579 | ||
574 | gre_proto = *(__be16 *)(h + 2); | 580 | gre_proto = *(__be16 *)(h + 2); |
575 | 581 | ||
576 | read_lock(&ipgre_lock); | 582 | rcu_read_lock(); |
577 | if ((tunnel = ipgre_tunnel_lookup(skb->dev, | 583 | if ((tunnel = ipgre_tunnel_lookup(skb->dev, |
578 | iph->saddr, iph->daddr, key, | 584 | iph->saddr, iph->daddr, key, |
579 | gre_proto))) { | 585 | gre_proto))) { |
@@ -647,13 +653,13 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
647 | ipgre_ecn_decapsulate(iph, skb); | 653 | ipgre_ecn_decapsulate(iph, skb); |
648 | 654 | ||
649 | netif_rx(skb); | 655 | netif_rx(skb); |
650 | read_unlock(&ipgre_lock); | 656 | rcu_read_unlock(); |
651 | return(0); | 657 | return(0); |
652 | } | 658 | } |
653 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | 659 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
654 | 660 | ||
655 | drop: | 661 | drop: |
656 | read_unlock(&ipgre_lock); | 662 | rcu_read_unlock(); |
657 | drop_nolock: | 663 | drop_nolock: |
658 | kfree_skb(skb); | 664 | kfree_skb(skb); |
659 | return(0); | 665 | return(0); |