Skip to content

Commit

Permalink
Merge branch 'tcp_conn_request_unification'
Browse files Browse the repository at this point in the history
Octavian Purdila says:

====================
tcp: remove code duplication in tcp_v[46]_conn_request

This patch series unifies the TCPv4 and TCPv6 connection request flow
in a single new function (tcp_conn_request).

The first 3 patches are small cleanups and fixes found during the code
merge process.

The next patches add new methods in tcp_request_sock_ops to abstract
the IPv4/IPv6 operations and keep the TCP connection request flow
common.

To identify potential performance issues this patch has been tested
by measuring the connection per second rate with nginx and a httperf
like client (to allow for concurrent connection requests - 256 CC were
used during testing) using the loopback interface. A dual-core i5 Ivy
Bridge processor was used and each process was bounded to a different
core to make results consistent.

Results for IPv4, unit is connections per second, higher is better, 20
measurements have been collected:

		before		after
min		27917		27962
max		28262		28366
avg		28094.1		28212.75
stdev		87.35		97.26

Results for IPv6, unit is connections per second, higher is better, 20
measurements have been collected:

		before		after
min		24813		24877
max		25029		25119
avg		24935.5		25017
stdev		64.13		62.93

Changes since v1:

 * add benchmarking datapoints

 * fix a few issues in the last patch (IPv6 related)
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 27, 2014
2 parents c1c27fb + 1fb6f15 commit 9e1a21b
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 331 deletions.
10 changes: 0 additions & 10 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,6 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk)
return inet_sk(__sk)->pinet6;
}

static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *ops)
{
struct request_sock *req = reqsk_alloc(ops);

if (req)
inet_rsk(req)->pktopts = NULL;

return req;
}

static inline struct raw6_sock *raw6_sk(const struct sock *sk)
{
return (struct raw6_sock *)sk;
Expand Down
3 changes: 0 additions & 3 deletions include/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,7 @@ struct tcp_request_sock_ops;

struct tcp_request_sock {
struct inet_request_sock req;
#ifdef CONFIG_TCP_MD5SIG
/* Only used by TCP MD5 Signature so far. */
const struct tcp_request_sock_ops *af_specific;
#endif
struct sock *listener; /* needed for TFO */
u32 rcv_isn;
u32 snt_isn;
Expand Down
6 changes: 4 additions & 2 deletions include/net/inet_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ struct inet_request_sock {
acked : 1,
no_srccheck: 1;
kmemcheck_bitfield_end(flags);
struct ip_options_rcu *opt;
struct sk_buff *pktopts;
union {
struct ip_options_rcu *opt;
struct sk_buff *pktopts;
};
u32 ir_mark;
};

Expand Down
54 changes: 39 additions & 15 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,14 +493,8 @@ static inline u32 tcp_cookie_time(void)

u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
u16 *mssp);
__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss);
#else
static inline __u32 cookie_v4_init_sequence(struct sock *sk,
struct sk_buff *skb,
__u16 *mss)
{
return 0;
}
__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
__u16 *mss);
#endif

__u32 cookie_init_timestamp(struct request_sock *req);
Expand All @@ -516,13 +510,6 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph,
const struct tcphdr *th, u16 *mssp);
__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb,
__u16 *mss);
#else
static inline __u32 cookie_v6_init_sequence(struct sock *sk,
struct sk_buff *skb,
__u16 *mss)
{
return 0;
}
#endif
/* tcp_output.c */

Expand Down Expand Up @@ -1586,6 +1573,11 @@ int tcp4_proc_init(void);
void tcp4_proc_exit(void);
#endif

int tcp_rtx_synack(struct sock *sk, struct request_sock *req);
int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_request_sock_ops *af_ops,
struct sock *sk, struct sk_buff *skb);

/* TCP af-specific functions */
struct tcp_sock_af_ops {
#ifdef CONFIG_TCP_MD5SIG
Expand All @@ -1603,6 +1595,7 @@ struct tcp_sock_af_ops {
};

struct tcp_request_sock_ops {
u16 mss_clamp;
#ifdef CONFIG_TCP_MD5SIG
struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk,
struct request_sock *req);
Expand All @@ -1612,8 +1605,39 @@ struct tcp_request_sock_ops {
const struct request_sock *req,
const struct sk_buff *skb);
#endif
void (*init_req)(struct request_sock *req, struct sock *sk,
struct sk_buff *skb);
#ifdef CONFIG_SYN_COOKIES
__u32 (*cookie_init_seq)(struct sock *sk, const struct sk_buff *skb,
__u16 *mss);
#endif
struct dst_entry *(*route_req)(struct sock *sk, struct flowi *fl,
const struct request_sock *req,
bool *strict);
__u32 (*init_seq)(const struct sk_buff *skb);
int (*send_synack)(struct sock *sk, struct dst_entry *dst,
struct flowi *fl, struct request_sock *req,
u16 queue_mapping, struct tcp_fastopen_cookie *foc);
void (*queue_hash_add)(struct sock *sk, struct request_sock *req,
const unsigned long timeout);
};

#ifdef CONFIG_SYN_COOKIES
static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
struct sock *sk, struct sk_buff *skb,
__u16 *mss)
{
return ops->cookie_init_seq(sk, skb, mss);
}
#else
static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
struct sock *sk, struct sk_buff *skb,
__u16 *mss)
{
return 0;
}
#endif

int tcpv4_offload_init(void);

void tcp_v4_init(void);
Expand Down
2 changes: 1 addition & 1 deletion net/dccp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;

req = inet6_reqsk_alloc(&dccp6_request_sock_ops);
req = inet_reqsk_alloc(&dccp6_request_sock_ops);
if (req == NULL)
goto drop;

Expand Down
3 changes: 2 additions & 1 deletion net/ipv4/syncookies.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
}
EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence);

__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp)
__u32 cookie_v4_init_sequence(struct sock *sk, const struct sk_buff *skb,
__u16 *mssp)
{
const struct iphdr *iph = ip_hdr(skb);
const struct tcphdr *th = tcp_hdr(skb);
Expand Down
148 changes: 148 additions & 0 deletions net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -5877,3 +5877,151 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
return 0;
}
EXPORT_SYMBOL(tcp_rcv_state_process);

static inline void pr_drop_req(struct request_sock *req, __u16 port, int family)
{
struct inet_request_sock *ireq = inet_rsk(req);

if (family == AF_INET)
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
&ireq->ir_rmt_addr, port);
else
LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI6/%u\n"),
&ireq->ir_v6_rmt_addr, port);
}

int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_request_sock_ops *af_ops,
struct sock *sk, struct sk_buff *skb)
{
struct tcp_options_received tmp_opt;
struct request_sock *req;
struct tcp_sock *tp = tcp_sk(sk);
struct dst_entry *dst = NULL;
__u32 isn = TCP_SKB_CB(skb)->when;
bool want_cookie = false, fastopen;
struct flowi fl;
struct tcp_fastopen_cookie foc = { .len = -1 };
int err;


/* TW buckets are converted to open requests without
* limitations, they conserve resources and peer is
* evidently real one.
*/
if ((sysctl_tcp_syncookies == 2 ||
inet_csk_reqsk_queue_is_full(sk)) && !isn) {
want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name);
if (!want_cookie)
goto drop;
}


/* Accept backlog is full. If we have already queued enough
* of warm entries in syn queue, drop request. It is better than
* clogging syn queue with openreqs with exponentially increasing
* timeout.
*/
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
goto drop;
}

req = inet_reqsk_alloc(rsk_ops);
if (!req)
goto drop;

tcp_rsk(req)->af_specific = af_ops;

tcp_clear_options(&tmp_opt);
tmp_opt.mss_clamp = af_ops->mss_clamp;
tmp_opt.user_mss = tp->rx_opt.user_mss;
tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);

if (want_cookie && !tmp_opt.saw_tstamp)
tcp_clear_options(&tmp_opt);

tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
tcp_openreq_init(req, &tmp_opt, skb, sk);

af_ops->init_req(req, sk, skb);

if (security_inet_conn_request(sk, skb, req))
goto drop_and_free;

if (!want_cookie || tmp_opt.tstamp_ok)
TCP_ECN_create_request(req, skb, sock_net(sk));

if (want_cookie) {
isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
req->cookie_ts = tmp_opt.tstamp_ok;
} else if (!isn) {
/* VJ's idea. We save last timestamp seen
* from the destination in peer table, when entering
* state TIME-WAIT, and check against it before
* accepting new connection request.
*
* If "isn" is not zero, this request hit alive
* timewait bucket, so that all the necessary checks
* are made in the function processing timewait state.
*/
if (tmp_opt.saw_tstamp && tcp_death_row.sysctl_tw_recycle) {
bool strict;

dst = af_ops->route_req(sk, &fl, req, &strict);
if (dst && strict &&
!tcp_peer_is_proven(req, dst, true)) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
goto drop_and_release;
}
}
/* Kill the following clause, if you dislike this way. */
else if (!sysctl_tcp_syncookies &&
(sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) <
(sysctl_max_syn_backlog >> 2)) &&
!tcp_peer_is_proven(req, dst, false)) {
/* Without syncookies last quarter of
* backlog is filled with destinations,
* proven to be alive.
* It means that we continue to communicate
* to destinations, already remembered
* to the moment of synflood.
*/
pr_drop_req(req, ntohs(tcp_hdr(skb)->source),
rsk_ops->family);
goto drop_and_release;
}

isn = af_ops->init_seq(skb);
}
if (!dst) {
dst = af_ops->route_req(sk, &fl, req, NULL);
if (!dst)
goto drop_and_free;
}

tcp_rsk(req)->snt_isn = isn;
tcp_openreq_init_rwin(req, sk, dst);
fastopen = !want_cookie &&
tcp_try_fastopen(sk, skb, req, &foc, dst);
err = af_ops->send_synack(sk, dst, &fl, req,
skb_get_queue_mapping(skb), &foc);
if (!fastopen) {
if (err || want_cookie)
goto drop_and_free;

tcp_rsk(req)->listener = NULL;
af_ops->queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
}

return 0;

drop_and_release:
dst_release(dst);
drop_and_free:
reqsk_free(req);
drop:
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
return 0;
}
EXPORT_SYMBOL(tcp_conn_request);
Loading

0 comments on commit 9e1a21b

Please sign in to comment.