Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: vstream_tweak.c,v 1.3 2020/03/18 19:05:22 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	vstream_tweak 3
      6 /* SUMMARY
      7 /*	performance tweaks
      8 /* SYNOPSIS
      9 /*	#include <vstream.h>
     10 /*
     11 /*	VSTREAM	*vstream_tweak_sock(stream)
     12 /*	VSTREAM	*stream;
     13 /*
     14 /*	VSTREAM	*vstream_tweak_tcp(stream)
     15 /*	VSTREAM	*stream;
     16 /* DESCRIPTION
     17 /*	vstream_tweak_sock() does a best effort to boost your
     18 /*	network performance on the specified generic stream.
     19 /*
     20 /*	vstream_tweak_tcp() does a best effort to boost your
     21 /*	Internet performance on the specified TCP stream.
     22 /*
     23 /*	Arguments:
     24 /* .IP stream
     25 /*	The stream being boosted.
     26 /* DIAGNOSTICS
     27 /*	Panics: interface violations.
     28 /* LICENSE
     29 /* .ad
     30 /* .fi
     31 /*	The Secure Mailer license must be distributed with this software.
     32 /* AUTHOR(S)
     33 /*	Wietse Venema
     34 /*	IBM T.J. Watson Research
     35 /*	P.O. Box 704
     36 /*	Yorktown Heights, NY 10598, USA
     37 /*
     38 /*	Wietse Venema
     39 /*	Google, Inc.
     40 /*	111 8th Avenue
     41 /*	New York, NY 10011, USA
     42 /*--*/
     43 
     44 /* System library. */
     45 
     46 #include <sys_defs.h>
     47 #include <sys/socket.h>
     48 #include <netinet/in.h>
     49 #include <netinet/tcp.h>
     50 #include <errno.h>
     51 
     52 /* Utility library. */
     53 
     54 #include <msg.h>
     55 #include <vstream.h>
     56 
     57 /* Application-specific. */
     58 
     59 #ifdef HAS_IPV6
     60 #define SOCKADDR_STORAGE struct sockaddr_storage
     61 #else
     62 #define SOCKADDR_STORAGE struct sockaddr
     63 #endif
     64 
     65 /* vstream_tweak_sock - boost your generic network performance */
     66 
     67 int     vstream_tweak_sock(VSTREAM *fp)
     68 {
     69     SOCKADDR_STORAGE ss;
     70     struct sockaddr *sa = (struct sockaddr *) &ss;
     71     SOCKADDR_SIZE sa_length = sizeof(ss);
     72     int     ret;
     73 
     74     /*
     75      * If the caller doesn't know if this socket is AF_LOCAL, AF_INET, etc.,
     76      * figure it out for them.
     77      */
     78     if ((ret = getsockname(vstream_fileno(fp), sa, &sa_length)) >= 0) {
     79 	switch (sa->sa_family) {
     80 #ifdef AF_INET6
     81 	case AF_INET6:
     82 #endif
     83 	case AF_INET:
     84 	    ret = vstream_tweak_tcp(fp);
     85 	    break;
     86 	}
     87     }
     88     return (ret);
     89 }
     90 
     91 /* vstream_tweak_tcp - boost your TCP performance */
     92 
     93 int     vstream_tweak_tcp(VSTREAM *fp)
     94 {
     95     const char *myname = "vstream_tweak_tcp";
     96     int     mss = 0;
     97     SOCKOPT_SIZE mss_len = sizeof(mss);
     98     int     err;
     99 
    100     /*
    101      * Avoid Nagle delays when VSTREAM buffers are smaller than the MSS.
    102      *
    103      * Forcing TCP_NODELAY to be "always on" would hurt performance in the
    104      * common case where VSTREAM buffers are larger than the MSS.
    105      *
    106      * Instead we ask the kernel what the current MSS is, and take appropriate
    107      * action. Linux <= 2.2 getsockopt(TCP_MAXSEG) always returns zero (or
    108      * whatever value was stored last with setsockopt()).
    109      *
    110      * Some ancient FreeBSD kernels don't report 'host unreachable' errors with
    111      * getsockopt(SO_ERROR), and then treat getsockopt(TCP_MAXSEG) as a NOOP,
    112      * leaving the mss parameter value unchanged. To work around these two
    113      * getsockopt() bugs we set mss = 0, which is a harmless value.
    114      */
    115     if ((err = getsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_MAXSEG,
    116 			  (void *) &mss, &mss_len)) < 0
    117 	&& errno != ECONNRESET) {
    118 	msg_warn("%s: getsockopt TCP_MAXSEG: %m", myname);
    119 	return (err);
    120     }
    121     if (msg_verbose)
    122 	msg_info("%s: TCP_MAXSEG %d", myname, mss);
    123 
    124     /*
    125      * Fix for recent Postfix versions: increase the VSTREAM buffer size if
    126      * it is smaller than the MSS. Note: the MSS may change when the route
    127      * changes and IP path MTU discovery is turned on, so we choose a
    128      * somewhat larger buffer.
    129      *
    130      * Note: as of 20120527, the CA_VSTREAM_CTL_BUFSIZE request can reduce the
    131      * stream buffer size to less than VSTREAM_BUFSIZE, when the request is
    132      * made before the first stream read or write operation. We don't want to
    133      * reduce the buffer size.
    134      *
    135      * As of 20190820 we increase the mss size multiplier from 2x to 4x, because
    136      * some LINUX loopback TCP stacks report an MSS of 21845 which is 3x
    137      * smaller than the MTU of 65536. Even with a VSTREAM buffer 2x the
    138      * reported MSS size, performance would suck due to Nagle or delayed ACK
    139      * delays.
    140      */
    141 #define EFF_BUFFER_SIZE(fp) (vstream_req_bufsize(fp) ? \
    142 		vstream_req_bufsize(fp) : VSTREAM_BUFSIZE)
    143 
    144 #ifdef CA_VSTREAM_CTL_BUFSIZE
    145     if (mss > EFF_BUFFER_SIZE(fp) / 4) {
    146 	if (mss < INT_MAX / 2)
    147 	    mss *= 2;
    148 	if (mss < INT_MAX / 2)
    149 	    mss *= 2;
    150 	vstream_control(fp,
    151 			CA_VSTREAM_CTL_BUFSIZE(mss),
    152 			CA_VSTREAM_CTL_END);
    153     }
    154 
    155     /*
    156      * Workaround for older Postfix versions: turn on TCP_NODELAY if the
    157      * VSTREAM buffer size is smaller than the MSS.
    158      */
    159 #else
    160     if (mss > VSTREAM_BUFSIZE) {
    161 	int     nodelay = 1;
    162 
    163 	if ((err = setsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_NODELAY,
    164 			      (void *) &nodelay, sizeof(nodelay))) < 0
    165 	    && errno != ECONNRESET)
    166 	    msg_warn("%s: setsockopt TCP_NODELAY: %m", myname);
    167     }
    168 #endif
    169     return (err);
    170 }
    171