diff options
-rw-r--r-- | net/ipv6/sit.c | 45 |
1 files changed, 27 insertions, 18 deletions
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 8cdcc2ad048c..b6b16264b305 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c | |||
@@ -77,8 +77,17 @@ struct sit_net { | |||
77 | struct net_device *fb_tunnel_dev; | 77 | struct net_device *fb_tunnel_dev; |
78 | }; | 78 | }; |
79 | 79 | ||
80 | static DEFINE_RWLOCK(ipip6_lock); | 80 | /* |
81 | * Locking : hash tables are protected by RCU and a spinlock | ||
82 | */ | ||
83 | static DEFINE_SPINLOCK(ipip6_lock); | ||
84 | |||
85 | #define for_each_ip_tunnel_rcu(start) \ | ||
86 | for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) | ||
81 | 87 | ||
88 | /* | ||
89 | * Must be invoked with rcu_read_lock | ||
90 | */ | ||
82 | static struct ip_tunnel * ipip6_tunnel_lookup(struct net *net, | 91 | static struct ip_tunnel * ipip6_tunnel_lookup(struct net *net, |
83 | struct net_device *dev, __be32 remote, __be32 local) | 92 | struct net_device *dev, __be32 remote, __be32 local) |
84 | { | 93 | { |
@@ -87,26 +96,26 @@ static struct ip_tunnel * ipip6_tunnel_lookup(struct net *net, | |||
87 | struct ip_tunnel *t; | 96 | struct ip_tunnel *t; |
88 | struct sit_net *sitn = net_generic(net, sit_net_id); | 97 | struct sit_net *sitn = net_generic(net, sit_net_id); |
89 | 98 | ||
90 | for (t = sitn->tunnels_r_l[h0^h1]; t; t = t->next) { | 99 | for_each_ip_tunnel_rcu(sitn->tunnels_r_l[h0 ^ h1]) { |
91 | if (local == t->parms.iph.saddr && | 100 | if (local == t->parms.iph.saddr && |
92 | remote == t->parms.iph.daddr && | 101 | remote == t->parms.iph.daddr && |
93 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && | 102 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && |
94 | (t->dev->flags & IFF_UP)) | 103 | (t->dev->flags & IFF_UP)) |
95 | return t; | 104 | return t; |
96 | } | 105 | } |
97 | for (t = sitn->tunnels_r[h0]; t; t = t->next) { | 106 | for_each_ip_tunnel_rcu(sitn->tunnels_r[h0]) { |
98 | if (remote == t->parms.iph.daddr && | 107 | if (remote == t->parms.iph.daddr && |
99 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && | 108 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && |
100 | (t->dev->flags & IFF_UP)) | 109 | (t->dev->flags & IFF_UP)) |
101 | return t; | 110 | return t; |
102 | } | 111 | } |
103 | for (t = sitn->tunnels_l[h1]; t; t = t->next) { | 112 | for_each_ip_tunnel_rcu(sitn->tunnels_l[h1]) { |
104 | if (local == t->parms.iph.saddr && | 113 | if (local == t->parms.iph.saddr && |
105 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && | 114 | (!dev || !t->parms.link || dev->iflink == t->parms.link) && |
106 | (t->dev->flags & IFF_UP)) | 115 | (t->dev->flags & IFF_UP)) |
107 | return t; | 116 | return t; |
108 | } | 117 | } |
109 | t = sitn->tunnels_wc[0]; | 118 | t = rcu_dereference(sitn->tunnels_wc[0]); |
110 | if ((t != NULL) && (t->dev->flags & IFF_UP)) | 119 | if ((t != NULL) && (t->dev->flags & IFF_UP)) |
111 | return t; | 120 | return t; |
112 | return NULL; | 121 | return NULL; |
@@ -143,9 +152,9 @@ static void ipip6_tunnel_unlink(struct sit_net *sitn, struct ip_tunnel *t) | |||
143 | 152 | ||
144 | for (tp = ipip6_bucket(sitn, t); *tp; tp = &(*tp)->next) { | 153 | for (tp = ipip6_bucket(sitn, t); *tp; tp = &(*tp)->next) { |
145 | if (t == *tp) { | 154 | if (t == *tp) { |
146 | write_lock_bh(&ipip6_lock); | 155 | spin_lock_bh(&ipip6_lock); |
147 | *tp = t->next; | 156 | *tp = t->next; |
148 | write_unlock_bh(&ipip6_lock); | 157 | spin_unlock_bh(&ipip6_lock); |
149 | break; | 158 | break; |
150 | } | 159 | } |
151 | } | 160 | } |
@@ -155,10 +164,10 @@ static void ipip6_tunnel_link(struct sit_net *sitn, struct ip_tunnel *t) | |||
155 | { | 164 | { |
156 | struct ip_tunnel **tp = ipip6_bucket(sitn, t); | 165 | struct ip_tunnel **tp = ipip6_bucket(sitn, t); |
157 | 166 | ||
167 | spin_lock_bh(&ipip6_lock); | ||
158 | t->next = *tp; | 168 | t->next = *tp; |
159 | write_lock_bh(&ipip6_lock); | 169 | rcu_assign_pointer(*tp, t); |
160 | *tp = t; | 170 | spin_unlock_bh(&ipip6_lock); |
161 | write_unlock_bh(&ipip6_lock); | ||
162 | } | 171 | } |
163 | 172 | ||
164 | static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn) | 173 | static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn) |
@@ -447,9 +456,9 @@ static void ipip6_tunnel_uninit(struct net_device *dev) | |||
447 | struct sit_net *sitn = net_generic(net, sit_net_id); | 456 | struct sit_net *sitn = net_generic(net, sit_net_id); |
448 | 457 | ||
449 | if (dev == sitn->fb_tunnel_dev) { | 458 | if (dev == sitn->fb_tunnel_dev) { |
450 | write_lock_bh(&ipip6_lock); | 459 | spin_lock_bh(&ipip6_lock); |
451 | sitn->tunnels_wc[0] = NULL; | 460 | sitn->tunnels_wc[0] = NULL; |
452 | write_unlock_bh(&ipip6_lock); | 461 | spin_unlock_bh(&ipip6_lock); |
453 | dev_put(dev); | 462 | dev_put(dev); |
454 | } else { | 463 | } else { |
455 | ipip6_tunnel_unlink(sitn, netdev_priv(dev)); | 464 | ipip6_tunnel_unlink(sitn, netdev_priv(dev)); |
@@ -502,7 +511,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info) | |||
502 | 511 | ||
503 | err = -ENOENT; | 512 | err = -ENOENT; |
504 | 513 | ||
505 | read_lock(&ipip6_lock); | 514 | rcu_read_lock(); |
506 | t = ipip6_tunnel_lookup(dev_net(skb->dev), | 515 | t = ipip6_tunnel_lookup(dev_net(skb->dev), |
507 | skb->dev, | 516 | skb->dev, |
508 | iph->daddr, | 517 | iph->daddr, |
@@ -520,7 +529,7 @@ static int ipip6_err(struct sk_buff *skb, u32 info) | |||
520 | t->err_count = 1; | 529 | t->err_count = 1; |
521 | t->err_time = jiffies; | 530 | t->err_time = jiffies; |
522 | out: | 531 | out: |
523 | read_unlock(&ipip6_lock); | 532 | rcu_read_unlock(); |
524 | return err; | 533 | return err; |
525 | } | 534 | } |
526 | 535 | ||
@@ -540,7 +549,7 @@ static int ipip6_rcv(struct sk_buff *skb) | |||
540 | 549 | ||
541 | iph = ip_hdr(skb); | 550 | iph = ip_hdr(skb); |
542 | 551 | ||
543 | read_lock(&ipip6_lock); | 552 | rcu_read_lock(); |
544 | tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, | 553 | tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev, |
545 | iph->saddr, iph->daddr); | 554 | iph->saddr, iph->daddr); |
546 | if (tunnel != NULL) { | 555 | if (tunnel != NULL) { |
@@ -554,7 +563,7 @@ static int ipip6_rcv(struct sk_buff *skb) | |||
554 | if ((tunnel->dev->priv_flags & IFF_ISATAP) && | 563 | if ((tunnel->dev->priv_flags & IFF_ISATAP) && |
555 | !isatap_chksrc(skb, iph, tunnel)) { | 564 | !isatap_chksrc(skb, iph, tunnel)) { |
556 | tunnel->dev->stats.rx_errors++; | 565 | tunnel->dev->stats.rx_errors++; |
557 | read_unlock(&ipip6_lock); | 566 | rcu_read_unlock(); |
558 | kfree_skb(skb); | 567 | kfree_skb(skb); |
559 | return 0; | 568 | return 0; |
560 | } | 569 | } |
@@ -565,12 +574,12 @@ static int ipip6_rcv(struct sk_buff *skb) | |||
565 | nf_reset(skb); | 574 | nf_reset(skb); |
566 | ipip6_ecn_decapsulate(iph, skb); | 575 | ipip6_ecn_decapsulate(iph, skb); |
567 | netif_rx(skb); | 576 | netif_rx(skb); |
568 | read_unlock(&ipip6_lock); | 577 | rcu_read_unlock(); |
569 | return 0; | 578 | return 0; |
570 | } | 579 | } |
571 | 580 | ||
572 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); | 581 | icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); |
573 | read_unlock(&ipip6_lock); | 582 | rcu_read_unlock(); |
574 | out: | 583 | out: |
575 | kfree_skb(skb); | 584 | kfree_skb(skb); |
576 | return 0; | 585 | return 0; |