xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/vstream_tweak.c (revision 33881f779a77dce6440bdc44610d94de75bebefe)
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 
vstream_tweak_sock(VSTREAM * fp)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 
vstream_tweak_tcp(VSTREAM * fp)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