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