Loading include/net/inet_connection_sock.h +5 −1 Original line number Diff line number Diff line Loading @@ -192,8 +192,12 @@ extern struct request_sock *inet_csk_search_req(const struct sock *sk, const __u16 rport, const __u32 raddr, const __u32 laddr); extern int inet_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb); extern int inet_csk_get_port(struct inet_hashinfo *hashinfo, struct sock *sk, unsigned short snum); struct sock *sk, unsigned short snum, int (*bind_conflict)(const struct sock *sk, const struct inet_bind_bucket *tb)); extern struct dst_entry* inet_csk_route_req(struct sock *sk, const struct request_sock *req); Loading net/dccp/ipv4.c +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ EXPORT_SYMBOL_GPL(dccp_hashinfo); static int dccp_v4_get_port(struct sock *sk, const unsigned short snum) { return inet_csk_get_port(&dccp_hashinfo, sk, snum); return inet_csk_get_port(&dccp_hashinfo, sk, snum, inet_csk_bind_conflict); } static void dccp_v4_hash(struct sock *sk) Loading net/ipv4/inet_connection_sock.c +8 −3 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); */ int sysctl_local_port_range[2] = { 1024, 4999 }; static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucket *tb) int inet_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb) { const u32 sk_rcv_saddr = inet_rcv_saddr(sk); struct sock *sk2; Loading @@ -62,11 +63,15 @@ static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucke return node != NULL; } EXPORT_SYMBOL_GPL(inet_csk_bind_conflict); /* Obtain a reference to a local port for the given sock, * if snum is zero it means select any available local port. */ int inet_csk_get_port(struct inet_hashinfo *hashinfo, struct sock *sk, unsigned short snum) struct sock *sk, unsigned short snum, int (*bind_conflict)(const struct sock *sk, const struct inet_bind_bucket *tb)) { struct inet_bind_hashbucket *head; struct hlist_node *node; Loading Loading @@ -125,7 +130,7 @@ int inet_csk_get_port(struct inet_hashinfo *hashinfo, goto success; } else { ret = 1; if (inet_csk_bind_conflict(sk, tb)) if (bind_conflict(sk, tb)) goto fail_unlock; } } Loading net/ipv4/tcp_ipv4.c +2 −1 Original line number Diff line number Diff line Loading @@ -97,7 +97,8 @@ struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { static int tcp_v4_get_port(struct sock *sk, unsigned short snum) { return inet_csk_get_port(&tcp_hashinfo, sk, snum); return inet_csk_get_port(&tcp_hashinfo, sk, snum, inet_csk_bind_conflict); } static void tcp_v4_hash(struct sock *sk) Loading net/ipv6/tcp_ipv6.c +4 −91 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); static struct tcp_func ipv6_mapped; static struct tcp_func ipv6_specific; static inline int tcp_v6_bind_conflict(const struct sock *sk, int inet6_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb) { const struct sock *sk2; Loading @@ -97,97 +97,10 @@ static inline int tcp_v6_bind_conflict(const struct sock *sk, return node != NULL; } /* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. * But it doesn't matter, the recalculation is in the rarest path * this function ever takes. */ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) { struct inet_bind_hashbucket *head; struct inet_bind_bucket *tb; struct hlist_node *node; int ret; local_bh_disable(); if (snum == 0) { int low = sysctl_local_port_range[0]; int high = sysctl_local_port_range[1]; int remaining = (high - low) + 1; int rover = net_random() % (high - low) + low; do { head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)]; spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) if (tb->port == rover) goto next; break; next: spin_unlock(&head->lock); if (++rover > high) rover = low; } while (--remaining > 0); /* Exhausted local port range during search? It is not * possible for us to be holding one of the bind hash * locks if this test triggers, because if 'remaining' * drops to zero, we broke out of the do/while loop at * the top level, not from the 'break;' statement. */ ret = 1; if (unlikely(remaining <= 0)) goto fail; /* OK, here is the one we will use. */ snum = rover; } else { head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)]; spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) if (tb->port == snum) goto tb_found; } tb = NULL; goto tb_not_found; tb_found: if (tb && !hlist_empty(&tb->owners)) { if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN) { goto success; } else { ret = 1; if (tcp_v6_bind_conflict(sk, tb)) goto fail_unlock; } } tb_not_found: ret = 1; if (tb == NULL) { tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, snum); if (tb == NULL) goto fail_unlock; } if (hlist_empty(&tb->owners)) { if (sk->sk_reuse && sk->sk_state != TCP_LISTEN) tb->fastreuse = 1; else tb->fastreuse = 0; } else if (tb->fastreuse && (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) tb->fastreuse = 0; success: if (!inet_csk(sk)->icsk_bind_hash) inet_bind_hash(sk, tb, snum); BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb); ret = 0; fail_unlock: spin_unlock(&head->lock); fail: local_bh_enable(); return ret; return inet_csk_get_port(&tcp_hashinfo, sk, snum, inet6_csk_bind_conflict); } static __inline__ void __tcp_v6_hash(struct sock *sk) Loading Loading
include/net/inet_connection_sock.h +5 −1 Original line number Diff line number Diff line Loading @@ -192,8 +192,12 @@ extern struct request_sock *inet_csk_search_req(const struct sock *sk, const __u16 rport, const __u32 raddr, const __u32 laddr); extern int inet_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb); extern int inet_csk_get_port(struct inet_hashinfo *hashinfo, struct sock *sk, unsigned short snum); struct sock *sk, unsigned short snum, int (*bind_conflict)(const struct sock *sk, const struct inet_bind_bucket *tb)); extern struct dst_entry* inet_csk_route_req(struct sock *sk, const struct request_sock *req); Loading
net/dccp/ipv4.c +2 −1 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ EXPORT_SYMBOL_GPL(dccp_hashinfo); static int dccp_v4_get_port(struct sock *sk, const unsigned short snum) { return inet_csk_get_port(&dccp_hashinfo, sk, snum); return inet_csk_get_port(&dccp_hashinfo, sk, snum, inet_csk_bind_conflict); } static void dccp_v4_hash(struct sock *sk) Loading
net/ipv4/inet_connection_sock.c +8 −3 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); */ int sysctl_local_port_range[2] = { 1024, 4999 }; static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucket *tb) int inet_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb) { const u32 sk_rcv_saddr = inet_rcv_saddr(sk); struct sock *sk2; Loading @@ -62,11 +63,15 @@ static inline int inet_csk_bind_conflict(struct sock *sk, struct inet_bind_bucke return node != NULL; } EXPORT_SYMBOL_GPL(inet_csk_bind_conflict); /* Obtain a reference to a local port for the given sock, * if snum is zero it means select any available local port. */ int inet_csk_get_port(struct inet_hashinfo *hashinfo, struct sock *sk, unsigned short snum) struct sock *sk, unsigned short snum, int (*bind_conflict)(const struct sock *sk, const struct inet_bind_bucket *tb)) { struct inet_bind_hashbucket *head; struct hlist_node *node; Loading Loading @@ -125,7 +130,7 @@ int inet_csk_get_port(struct inet_hashinfo *hashinfo, goto success; } else { ret = 1; if (inet_csk_bind_conflict(sk, tb)) if (bind_conflict(sk, tb)) goto fail_unlock; } } Loading
net/ipv4/tcp_ipv4.c +2 −1 Original line number Diff line number Diff line Loading @@ -97,7 +97,8 @@ struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { static int tcp_v4_get_port(struct sock *sk, unsigned short snum) { return inet_csk_get_port(&tcp_hashinfo, sk, snum); return inet_csk_get_port(&tcp_hashinfo, sk, snum, inet_csk_bind_conflict); } static void tcp_v4_hash(struct sock *sk) Loading
net/ipv6/tcp_ipv6.c +4 −91 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok); static struct tcp_func ipv6_mapped; static struct tcp_func ipv6_specific; static inline int tcp_v6_bind_conflict(const struct sock *sk, int inet6_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb) { const struct sock *sk2; Loading @@ -97,97 +97,10 @@ static inline int tcp_v6_bind_conflict(const struct sock *sk, return node != NULL; } /* Grrr, addr_type already calculated by caller, but I don't want * to add some silly "cookie" argument to this method just for that. * But it doesn't matter, the recalculation is in the rarest path * this function ever takes. */ static int tcp_v6_get_port(struct sock *sk, unsigned short snum) { struct inet_bind_hashbucket *head; struct inet_bind_bucket *tb; struct hlist_node *node; int ret; local_bh_disable(); if (snum == 0) { int low = sysctl_local_port_range[0]; int high = sysctl_local_port_range[1]; int remaining = (high - low) + 1; int rover = net_random() % (high - low) + low; do { head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)]; spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) if (tb->port == rover) goto next; break; next: spin_unlock(&head->lock); if (++rover > high) rover = low; } while (--remaining > 0); /* Exhausted local port range during search? It is not * possible for us to be holding one of the bind hash * locks if this test triggers, because if 'remaining' * drops to zero, we broke out of the do/while loop at * the top level, not from the 'break;' statement. */ ret = 1; if (unlikely(remaining <= 0)) goto fail; /* OK, here is the one we will use. */ snum = rover; } else { head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)]; spin_lock(&head->lock); inet_bind_bucket_for_each(tb, node, &head->chain) if (tb->port == snum) goto tb_found; } tb = NULL; goto tb_not_found; tb_found: if (tb && !hlist_empty(&tb->owners)) { if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN) { goto success; } else { ret = 1; if (tcp_v6_bind_conflict(sk, tb)) goto fail_unlock; } } tb_not_found: ret = 1; if (tb == NULL) { tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, snum); if (tb == NULL) goto fail_unlock; } if (hlist_empty(&tb->owners)) { if (sk->sk_reuse && sk->sk_state != TCP_LISTEN) tb->fastreuse = 1; else tb->fastreuse = 0; } else if (tb->fastreuse && (!sk->sk_reuse || sk->sk_state == TCP_LISTEN)) tb->fastreuse = 0; success: if (!inet_csk(sk)->icsk_bind_hash) inet_bind_hash(sk, tb, snum); BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb); ret = 0; fail_unlock: spin_unlock(&head->lock); fail: local_bh_enable(); return ret; return inet_csk_get_port(&tcp_hashinfo, sk, snum, inet6_csk_bind_conflict); } static __inline__ void __tcp_v6_hash(struct sock *sk) Loading