diff options
Diffstat (limited to '')
-rw-r--r-- | src/util/vstream_tweak.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/util/vstream_tweak.c b/src/util/vstream_tweak.c new file mode 100644 index 0000000..7100bc6 --- /dev/null +++ b/src/util/vstream_tweak.c @@ -0,0 +1,168 @@ +/*++ +/* NAME +/* vstream_tweak 3 +/* SUMMARY +/* performance tweaks +/* SYNOPSIS +/* #include <vstream.h> +/* +/* VSTREAM *vstream_tweak_sock(stream) +/* VSTREAM *stream; +/* +/* VSTREAM *vstream_tweak_tcp(stream) +/* VSTREAM *stream; +/* DESCRIPTION +/* vstream_tweak_sock() does a best effort to boost your +/* network performance on the specified generic stream. +/* +/* vstream_tweak_tcp() does a best effort to boost your +/* Internet performance on the specified TCP stream. +/* +/* Arguments: +/* .IP stream +/* The stream being boosted. +/* DIAGNOSTICS +/* Panics: interface violations. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <errno.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> + +/* Application-specific. */ + +#ifdef HAS_IPV6 +#define SOCKADDR_STORAGE struct sockaddr_storage +#else +#define SOCKADDR_STORAGE struct sockaddr +#endif + +/* vstream_tweak_sock - boost your generic network performance */ + +int vstream_tweak_sock(VSTREAM *fp) +{ + SOCKADDR_STORAGE ss; + struct sockaddr *sa = (struct sockaddr *) &ss; + SOCKADDR_SIZE sa_length = sizeof(ss); + int ret; + + /* + * If the caller doesn't know if this socket is AF_LOCAL, AF_INET, etc., + * figure it out for them. + */ + if ((ret = getsockname(vstream_fileno(fp), sa, &sa_length)) >= 0) { + switch (sa->sa_family) { +#ifdef AF_INET6 + case AF_INET6: +#endif + case AF_INET: + ret = vstream_tweak_tcp(fp); + break; + } + } + return (ret); +} + +/* vstream_tweak_tcp - boost your TCP performance */ + +int vstream_tweak_tcp(VSTREAM *fp) +{ + const char *myname = "vstream_tweak_tcp"; + int mss = 0; + SOCKOPT_SIZE mss_len = sizeof(mss); + int err; + + /* + * Avoid Nagle delays when VSTREAM buffers are smaller than the MSS. + * + * Forcing TCP_NODELAY to be "always on" would hurt performance in the + * common case where VSTREAM buffers are larger than the MSS. + * + * Instead we ask the kernel what the current MSS is, and take appropriate + * action. Linux <= 2.2 getsockopt(TCP_MAXSEG) always returns zero (or + * whatever value was stored last with setsockopt()). + * + * Some ancient FreeBSD kernels don't report 'host unreachable' errors with + * getsockopt(SO_ERROR), and then treat getsockopt(TCP_MAXSEG) as a NOOP, + * leaving the mss parameter value unchanged. To work around these two + * getsockopt() bugs we set mss = 0, which is a harmless value. + */ + if ((err = getsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_MAXSEG, + (void *) &mss, &mss_len)) < 0 + && errno != ECONNRESET) { + msg_warn("%s: getsockopt TCP_MAXSEG: %m", myname); + return (err); + } + if (msg_verbose) + msg_info("%s: TCP_MAXSEG %d", myname, mss); + + /* + * Fix for recent Postfix versions: increase the VSTREAM buffer size if + * it is smaller than the MSS. Note: the MSS may change when the route + * changes and IP path MTU discovery is turned on, so we choose a + * somewhat larger buffer. + * + * Note: as of 20120527, the CA_VSTREAM_CTL_BUFSIZE request can reduce the + * stream buffer size to less than VSTREAM_BUFSIZE, when the request is + * made before the first stream read or write operation. We don't want to + * reduce the buffer size. + * + * As of 20190820 we increase the mss size multiplier from 2x to 4x, because + * some LINUX loopback TCP stacks report an MSS of 21845 which is 3x + * smaller than the MTU of 65536. Even with a VSTREAM buffer 2x the + * reported MSS size, performance would suck due to Nagle or delayed ACK + * delays. + */ +#define EFF_BUFFER_SIZE(fp) (vstream_req_bufsize(fp) ? \ + vstream_req_bufsize(fp) : VSTREAM_BUFSIZE) + +#ifdef CA_VSTREAM_CTL_BUFSIZE + if (mss > EFF_BUFFER_SIZE(fp) / 4) { + if (mss < INT_MAX / 2) + mss *= 2; + if (mss < INT_MAX / 2) + mss *= 2; + vstream_control(fp, + CA_VSTREAM_CTL_BUFSIZE(mss), + CA_VSTREAM_CTL_END); + } + + /* + * Workaround for older Postfix versions: turn on TCP_NODELAY if the + * VSTREAM buffer size is smaller than the MSS. + */ +#else + if (mss > VSTREAM_BUFSIZE) { + int nodelay = 1; + + if ((err = setsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_NODELAY, + (void *) &nodelay, sizeof(nodelay))) < 0 + && errno != ECONNRESET) + msg_warn("%s: setsockopt TCP_NODELAY: %m", myname); + } +#endif + return (err); +} |