diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/udp.c | 31 | ||||
-rw-r--r-- | net/ipv6/udplite.c | 1 |
2 files changed, 25 insertions, 7 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index ccee7244ca0f..1d9790e43dfc 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -97,24 +97,40 @@ static struct sock *__udp6_lib_lookup(struct net *net, | |||
97 | struct in6_addr *daddr, __be16 dport, | 97 | struct in6_addr *daddr, __be16 dport, |
98 | int dif, struct udp_table *udptable) | 98 | int dif, struct udp_table *udptable) |
99 | { | 99 | { |
100 | struct sock *sk, *result = NULL; | 100 | struct sock *sk, *result; |
101 | struct hlist_node *node; | 101 | struct hlist_node *node; |
102 | unsigned short hnum = ntohs(dport); | 102 | unsigned short hnum = ntohs(dport); |
103 | unsigned int hash = udp_hashfn(net, hnum); | 103 | unsigned int hash = udp_hashfn(net, hnum); |
104 | struct udp_hslot *hslot = &udptable->hash[hash]; | 104 | struct udp_hslot *hslot = &udptable->hash[hash]; |
105 | int score, badness = -1; | 105 | int score, badness; |
106 | 106 | ||
107 | spin_lock(&hslot->lock); | 107 | rcu_read_lock(); |
108 | sk_for_each(sk, node, &hslot->head) { | 108 | begin: |
109 | result = NULL; | ||
110 | badness = -1; | ||
111 | sk_for_each_rcu(sk, node, &hslot->head) { | ||
112 | /* | ||
113 | * lockless reader, and SLAB_DESTROY_BY_RCU items: | ||
114 | * We must check this item was not moved to another chain | ||
115 | */ | ||
116 | if (udp_hashfn(net, sk->sk_hash) != hash) | ||
117 | goto begin; | ||
109 | score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif); | 118 | score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif); |
110 | if (score > badness) { | 119 | if (score > badness) { |
111 | result = sk; | 120 | result = sk; |
112 | badness = score; | 121 | badness = score; |
113 | } | 122 | } |
114 | } | 123 | } |
115 | if (result) | 124 | if (result) { |
116 | sock_hold(result); | 125 | if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt))) |
117 | spin_unlock(&hslot->lock); | 126 | result = NULL; |
127 | else if (unlikely(compute_score(result, net, hnum, saddr, sport, | ||
128 | daddr, dport, dif) < badness)) { | ||
129 | sock_put(result); | ||
130 | goto begin; | ||
131 | } | ||
132 | } | ||
133 | rcu_read_unlock(); | ||
118 | return result; | 134 | return result; |
119 | } | 135 | } |
120 | 136 | ||
@@ -1062,6 +1078,7 @@ struct proto udpv6_prot = { | |||
1062 | .sysctl_wmem = &sysctl_udp_wmem_min, | 1078 | .sysctl_wmem = &sysctl_udp_wmem_min, |
1063 | .sysctl_rmem = &sysctl_udp_rmem_min, | 1079 | .sysctl_rmem = &sysctl_udp_rmem_min, |
1064 | .obj_size = sizeof(struct udp6_sock), | 1080 | .obj_size = sizeof(struct udp6_sock), |
1081 | .slab_flags = SLAB_DESTROY_BY_RCU, | ||
1065 | .h.udp_table = &udp_table, | 1082 | .h.udp_table = &udp_table, |
1066 | #ifdef CONFIG_COMPAT | 1083 | #ifdef CONFIG_COMPAT |
1067 | .compat_setsockopt = compat_udpv6_setsockopt, | 1084 | .compat_setsockopt = compat_udpv6_setsockopt, |
diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index f1e892a99e05..ba162a824585 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c | |||
@@ -49,6 +49,7 @@ struct proto udplitev6_prot = { | |||
49 | .unhash = udp_lib_unhash, | 49 | .unhash = udp_lib_unhash, |
50 | .get_port = udp_v6_get_port, | 50 | .get_port = udp_v6_get_port, |
51 | .obj_size = sizeof(struct udp6_sock), | 51 | .obj_size = sizeof(struct udp6_sock), |
52 | .slab_flags = SLAB_DESTROY_BY_RCU, | ||
52 | .h.udp_table = &udplite_table, | 53 | .h.udp_table = &udplite_table, |
53 | #ifdef CONFIG_COMPAT | 54 | #ifdef CONFIG_COMPAT |
54 | .compat_setsockopt = compat_udpv6_setsockopt, | 55 | .compat_setsockopt = compat_udpv6_setsockopt, |