Commit 33b62231 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller

[INET]: Generalise tcp_v4_lookup_listener

[acme@toy net-2.6.14]$ grep built-in /tmp/before /tmp/after
/tmp/before: 282560       13122    9312  304994   4a762 net/ipv4/built-in.o
/tmp/after:  282560       13122    9312  304994   4a762 net/ipv4/built-in.o

Will be used in DCCP, not exporting it right now not to get in Adrian
Bunk's exported-but-not-used-on-modules radar 8)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 81849d10
...@@ -16,8 +16,10 @@ ...@@ -16,8 +16,10 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/socket.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/wait.h> #include <linux/wait.h>
...@@ -274,4 +276,38 @@ static inline void inet_unhash(struct inet_hashinfo *hashinfo, struct sock *sk) ...@@ -274,4 +276,38 @@ static inline void inet_unhash(struct inet_hashinfo *hashinfo, struct sock *sk)
if (sk->sk_state == TCP_LISTEN) if (sk->sk_state == TCP_LISTEN)
wake_up(&hashinfo->lhash_wait); wake_up(&hashinfo->lhash_wait);
} }
extern struct sock *__inet_lookup_listener(const struct hlist_head *head,
const u32 daddr,
const unsigned short hnum,
const int dif);
/* Optimize the common listener case. */
static inline struct sock *inet_lookup_listener(struct inet_hashinfo *hashinfo,
const u32 daddr,
const unsigned short hnum,
const int dif)
{
struct sock *sk = NULL;
struct hlist_head *head;
read_lock(&hashinfo->lhash_lock);
head = &hashinfo->listening_hash[inet_lhashfn(hnum)];
if (!hlist_empty(head)) {
const struct inet_sock *inet = inet_sk((sk = __sk_head(head)));
if (inet->num == hnum && !sk->sk_node.next &&
(!inet->rcv_saddr || inet->rcv_saddr == daddr) &&
(sk->sk_family == PF_INET || !ipv6_only_sock(sk)) &&
!sk->sk_bound_dev_if)
goto sherry_cache;
sk = __inet_lookup_listener(head, daddr, hnum, dif);
}
if (sk) {
sherry_cache:
sock_hold(sk);
}
read_unlock(&hashinfo->lhash_lock);
return sk;
}
#endif /* _INET_HASHTABLES_H */ #endif /* _INET_HASHTABLES_H */
...@@ -121,3 +121,44 @@ void inet_listen_wlock(struct inet_hashinfo *hashinfo) ...@@ -121,3 +121,44 @@ void inet_listen_wlock(struct inet_hashinfo *hashinfo)
} }
EXPORT_SYMBOL(inet_listen_wlock); EXPORT_SYMBOL(inet_listen_wlock);
/*
* Don't inline this cruft. Here are some nice properties to exploit here. The
* BSD API does not allow a listening sock to specify the remote port nor the
* remote address for the connection. So always assume those are both
* wildcarded during the search since they can never be otherwise.
*/
struct sock *__inet_lookup_listener(const struct hlist_head *head, const u32 daddr,
const unsigned short hnum, const int dif)
{
struct sock *result = NULL, *sk;
const struct hlist_node *node;
int hiscore = -1;
sk_for_each(sk, node, head) {
const struct inet_sock *inet = inet_sk(sk);
if (inet->num == hnum && !ipv6_only_sock(sk)) {
const __u32 rcv_saddr = inet->rcv_saddr;
int score = sk->sk_family == PF_INET ? 1 : 0;
if (rcv_saddr) {
if (rcv_saddr != daddr)
continue;
score += 2;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score += 2;
}
if (score == 5)
return sk;
if (score > hiscore) {
hiscore = score;
result = sk;
}
}
}
return result;
}
...@@ -238,78 +238,6 @@ void tcp_unhash(struct sock *sk) ...@@ -238,78 +238,6 @@ void tcp_unhash(struct sock *sk)
inet_unhash(&tcp_hashinfo, sk); inet_unhash(&tcp_hashinfo, sk);
} }
/* Don't inline this cruft. Here are some nice properties to
* exploit here. The BSD API does not allow a listening TCP
* to specify the remote port nor the remote address for the
* connection. So always assume those are both wildcarded
* during the search since they can never be otherwise.
*/
static struct sock *__tcp_v4_lookup_listener(struct hlist_head *head,
const u32 daddr,
const unsigned short hnum,
const int dif)
{
struct sock *result = NULL, *sk;
struct hlist_node *node;
int score, hiscore;
hiscore=-1;
sk_for_each(sk, node, head) {
struct inet_sock *inet = inet_sk(sk);
if (inet->num == hnum && !ipv6_only_sock(sk)) {
__u32 rcv_saddr = inet->rcv_saddr;
score = (sk->sk_family == PF_INET ? 1 : 0);
if (rcv_saddr) {
if (rcv_saddr != daddr)
continue;
score+=2;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score+=2;
}
if (score == 5)
return sk;
if (score > hiscore) {
hiscore = score;
result = sk;
}
}
}
return result;
}
/* Optimize the common listener case. */
static inline struct sock *tcp_v4_lookup_listener(const u32 daddr,
const unsigned short hnum,
const int dif)
{
struct sock *sk = NULL;
struct hlist_head *head;
read_lock(&tcp_hashinfo.lhash_lock);
head = &tcp_hashinfo.listening_hash[inet_lhashfn(hnum)];
if (!hlist_empty(head)) {
struct inet_sock *inet = inet_sk((sk = __sk_head(head)));
if (inet->num == hnum && !sk->sk_node.next &&
(!inet->rcv_saddr || inet->rcv_saddr == daddr) &&
(sk->sk_family == PF_INET || !ipv6_only_sock(sk)) &&
!sk->sk_bound_dev_if)
goto sherry_cache;
sk = __tcp_v4_lookup_listener(head, daddr, hnum, dif);
}
if (sk) {
sherry_cache:
sock_hold(sk);
}
read_unlock(&tcp_hashinfo.lhash_lock);
return sk;
}
/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
* *
...@@ -358,7 +286,7 @@ static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport, ...@@ -358,7 +286,7 @@ static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport,
struct sock *sk = __tcp_v4_lookup_established(saddr, sport, struct sock *sk = __tcp_v4_lookup_established(saddr, sport,
daddr, hnum, dif); daddr, hnum, dif);
return sk ? : tcp_v4_lookup_listener(daddr, hnum, dif); return sk ? : inet_lookup_listener(&tcp_hashinfo, daddr, hnum, dif);
} }
inline struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, inline struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr,
...@@ -1641,9 +1569,10 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1641,9 +1569,10 @@ int tcp_v4_rcv(struct sk_buff *skb)
switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk, switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
skb, th, skb->len)) { skb, th, skb->len)) {
case TCP_TW_SYN: { case TCP_TW_SYN: {
struct sock *sk2 = tcp_v4_lookup_listener(skb->nh.iph->daddr, struct sock *sk2 = inet_lookup_listener(&tcp_hashinfo,
ntohs(th->dest), skb->nh.iph->daddr,
tcp_v4_iif(skb)); ntohs(th->dest),
tcp_v4_iif(skb));
if (sk2) { if (sk2) {
tcp_tw_deschedule((struct tcp_tw_bucket *)sk); tcp_tw_deschedule((struct tcp_tw_bucket *)sk);
tcp_tw_put((struct tcp_tw_bucket *)sk); tcp_tw_put((struct tcp_tw_bucket *)sk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment