diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-07-28 00:32:25 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-08-01 03:12:00 -0400 |
commit | 89b0212697e92bc59a021a2338cd8c09f919325c (patch) | |
tree | 633279786602d3677ea5c4959528aa00219afed2 /net/ipv6/ip6_tunnel.c | |
parent | 897dc80b951e996ba4d26c0038e81a505b92aec1 (diff) |
ip6tnl: avoid touching dst refcount in ip6_tnl_xmit2()
Even using percpu stats, we still hit tunnel dst_entry refcount in
ip6_tnl_xmit2()
Since we are in RCU locked section, we can use skb_dst_set_noref() and
avoid these atomic operations, leaving dst shared on cpus.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ip6_tunnel.c')
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 27 |
1 files changed, 14 insertions, 13 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 0bc98886c383..6fb1fb3624bf 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -889,7 +889,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, | |||
889 | struct net_device_stats *stats = &t->dev->stats; | 889 | struct net_device_stats *stats = &t->dev->stats; |
890 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | 890 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); |
891 | struct ipv6_tel_txoption opt; | 891 | struct ipv6_tel_txoption opt; |
892 | struct dst_entry *dst; | 892 | struct dst_entry *dst, *ndst = NULL; |
893 | struct net_device *tdev; | 893 | struct net_device *tdev; |
894 | int mtu; | 894 | int mtu; |
895 | unsigned int max_headroom = sizeof(struct ipv6hdr); | 895 | unsigned int max_headroom = sizeof(struct ipv6hdr); |
@@ -897,19 +897,19 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, | |||
897 | int err = -1; | 897 | int err = -1; |
898 | int pkt_len; | 898 | int pkt_len; |
899 | 899 | ||
900 | if ((dst = ip6_tnl_dst_check(t)) != NULL) | 900 | dst = ip6_tnl_dst_check(t); |
901 | dst_hold(dst); | 901 | if (!dst) { |
902 | else { | 902 | ndst = ip6_route_output(net, NULL, fl6); |
903 | dst = ip6_route_output(net, NULL, fl6); | ||
904 | 903 | ||
905 | if (dst->error) | 904 | if (ndst->error) |
906 | goto tx_err_link_failure; | 905 | goto tx_err_link_failure; |
907 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(fl6), NULL, 0); | 906 | ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(fl6), NULL, 0); |
908 | if (IS_ERR(dst)) { | 907 | if (IS_ERR(ndst)) { |
909 | err = PTR_ERR(dst); | 908 | err = PTR_ERR(ndst); |
910 | dst = NULL; | 909 | ndst = NULL; |
911 | goto tx_err_link_failure; | 910 | goto tx_err_link_failure; |
912 | } | 911 | } |
912 | dst = ndst; | ||
913 | } | 913 | } |
914 | 914 | ||
915 | tdev = dst->dev; | 915 | tdev = dst->dev; |
@@ -955,7 +955,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, | |||
955 | skb = new_skb; | 955 | skb = new_skb; |
956 | } | 956 | } |
957 | skb_dst_drop(skb); | 957 | skb_dst_drop(skb); |
958 | skb_dst_set(skb, dst_clone(dst)); | 958 | skb_dst_set_noref(skb, dst); |
959 | 959 | ||
960 | skb->transport_header = skb->network_header; | 960 | skb->transport_header = skb->network_header; |
961 | 961 | ||
@@ -987,13 +987,14 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, | |||
987 | stats->tx_errors++; | 987 | stats->tx_errors++; |
988 | stats->tx_aborted_errors++; | 988 | stats->tx_aborted_errors++; |
989 | } | 989 | } |
990 | ip6_tnl_dst_store(t, dst); | 990 | if (ndst) |
991 | ip6_tnl_dst_store(t, ndst); | ||
991 | return 0; | 992 | return 0; |
992 | tx_err_link_failure: | 993 | tx_err_link_failure: |
993 | stats->tx_carrier_errors++; | 994 | stats->tx_carrier_errors++; |
994 | dst_link_failure(skb); | 995 | dst_link_failure(skb); |
995 | tx_err_dst_release: | 996 | tx_err_dst_release: |
996 | dst_release(dst); | 997 | dst_release(ndst); |
997 | return err; | 998 | return err; |
998 | } | 999 | } |
999 | 1000 | ||