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