aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-11-08 05:18:30 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-08 23:53:07 -0500
commitfddc17defa22d8caba1cdfb2e22b50bb4b9f35c0 (patch)
tree565a1232275c2abbed3d55950a73f647a66e1289 /net/ipv6
parent5051ebd275de672b807c28d93002c2fb0514a3c9 (diff)
ipv6: udp: optimize unicast RX path
We first locate the (local port) hash chain head If few sockets are in this chain, we proceed with previous lookup algo. If too many sockets are listed, we take a look at the secondary (port, address) hash chain. We choose the shortest chain and proceed with a RCU lookup on the elected chain. But, if we chose (port, address) chain, and fail to find a socket on given address, we must try another lookup on (port, in6addr_any) chain to find sockets not bound to a particular IP. -> No extra cost for typical setups, where the first lookup will probabbly be performed. RCU lookups everywhere, we dont acquire spinlock. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/udp.c112
1 files changed, 109 insertions, 3 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 1e5fadd997b7..f580cf925112 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -146,6 +146,88 @@ static inline int compute_score(struct sock *sk, struct net *net,
146 return score; 146 return score;
147} 147}
148 148
149#define SCORE2_MAX (1 + 1 + 1)
150static inline int compute_score2(struct sock *sk, struct net *net,
151 const struct in6_addr *saddr, __be16 sport,
152 const struct in6_addr *daddr, unsigned short hnum,
153 int dif)
154{
155 int score = -1;
156
157 if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
158 sk->sk_family == PF_INET6) {
159 struct ipv6_pinfo *np = inet6_sk(sk);
160 struct inet_sock *inet = inet_sk(sk);
161
162 if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
163 return -1;
164 score = 0;
165 if (inet->inet_dport) {
166 if (inet->inet_dport != sport)
167 return -1;
168 score++;
169 }
170 if (!ipv6_addr_any(&np->daddr)) {
171 if (!ipv6_addr_equal(&np->daddr, saddr))
172 return -1;
173 score++;
174 }
175 if (sk->sk_bound_dev_if) {
176 if (sk->sk_bound_dev_if != dif)
177 return -1;
178 score++;
179 }
180 }
181 return score;
182}
183
184#define udp_portaddr_for_each_entry_rcu(__sk, node, list) \
185 hlist_nulls_for_each_entry_rcu(__sk, node, list, __sk_common.skc_portaddr_node)
186
187/* called with read_rcu_lock() */
188static struct sock *udp6_lib_lookup2(struct net *net,
189 const struct in6_addr *saddr, __be16 sport,
190 const struct in6_addr *daddr, unsigned int hnum, int dif,
191 struct udp_hslot *hslot2, unsigned int slot2)
192{
193 struct sock *sk, *result;
194 struct hlist_nulls_node *node;
195 int score, badness;
196
197begin:
198 result = NULL;
199 badness = -1;
200 udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) {
201 score = compute_score2(sk, net, saddr, sport,
202 daddr, hnum, dif);
203 if (score > badness) {
204 result = sk;
205 badness = score;
206 if (score == SCORE2_MAX)
207 goto exact_match;
208 }
209 }
210 /*
211 * if the nulls value we got at the end of this lookup is
212 * not the expected one, we must restart lookup.
213 * We probably met an item that was moved to another chain.
214 */
215 if (get_nulls_value(node) != slot2)
216 goto begin;
217
218 if (result) {
219exact_match:
220 if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
221 result = NULL;
222 else if (unlikely(compute_score2(result, net, saddr, sport,
223 daddr, hnum, dif) < badness)) {
224 sock_put(result);
225 goto begin;
226 }
227 }
228 return result;
229}
230
149static struct sock *__udp6_lib_lookup(struct net *net, 231static struct sock *__udp6_lib_lookup(struct net *net,
150 struct in6_addr *saddr, __be16 sport, 232 struct in6_addr *saddr, __be16 sport,
151 struct in6_addr *daddr, __be16 dport, 233 struct in6_addr *daddr, __be16 dport,
@@ -154,11 +236,35 @@ static struct sock *__udp6_lib_lookup(struct net *net,
154 struct sock *sk, *result; 236 struct sock *sk, *result;
155 struct hlist_nulls_node *node; 237 struct hlist_nulls_node *node;
156 unsigned short hnum = ntohs(dport); 238 unsigned short hnum = ntohs(dport);
157 unsigned int hash = udp_hashfn(net, hnum, udptable->mask); 239 unsigned int hash2, slot2, slot = udp_hashfn(net, hnum, udptable->mask);
158 struct udp_hslot *hslot = &udptable->hash[hash]; 240 struct udp_hslot *hslot2, *hslot = &udptable->hash[slot];
159 int score, badness; 241 int score, badness;
160 242
161 rcu_read_lock(); 243 rcu_read_lock();
244 if (hslot->count > 10) {
245 hash2 = udp6_portaddr_hash(net, daddr, hnum);
246 slot2 = hash2 & udptable->mask;
247 hslot2 = &udptable->hash2[slot2];
248 if (hslot->count < hslot2->count)
249 goto begin;
250
251 result = udp6_lib_lookup2(net, saddr, sport,
252 daddr, hnum, dif,
253 hslot2, slot2);
254 if (!result) {
255 hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum);
256 slot2 = hash2 & udptable->mask;
257 hslot2 = &udptable->hash2[slot2];
258 if (hslot->count < hslot2->count)
259 goto begin;
260
261 result = udp6_lib_lookup2(net, &in6addr_any, sport,
262 daddr, hnum, dif,
263 hslot2, slot2);
264 }
265 rcu_read_unlock();
266 return result;
267 }
162begin: 268begin:
163 result = NULL; 269 result = NULL;
164 badness = -1; 270 badness = -1;
@@ -174,7 +280,7 @@ begin:
174 * not the expected one, we must restart lookup. 280 * not the expected one, we must restart lookup.
175 * We probably met an item that was moved to another chain. 281 * We probably met an item that was moved to another chain.
176 */ 282 */
177 if (get_nulls_value(node) != hash) 283 if (get_nulls_value(node) != slot)
178 goto begin; 284 goto begin;
179 285
180 if (result) { 286 if (result) {