/*++ /* NAME /* vstream_tweak 3 /* SUMMARY /* performance tweaks /* SYNOPSIS /* #include /* /* 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 #include #include #include #include /* Utility library. */ #include #include /* 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); }