| | varnish-cache/bin/varnishd/acceptor/cache_acceptor_tcp.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2015 Varnish Software AS |
3 |
|
* All rights reserved. |
4 |
|
* |
5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
6 |
|
* |
7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
8 |
|
* |
9 |
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
* modification, are permitted provided that the following conditions |
11 |
|
* are met: |
12 |
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
* documentation and/or other materials provided with the distribution. |
17 |
|
* |
18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
|
* SUCH DAMAGE. |
29 |
|
* |
30 |
|
*/ |
31 |
|
|
32 |
|
#include "config.h" |
33 |
|
|
34 |
|
#include <stdlib.h> |
35 |
|
#include <netinet/in.h> |
36 |
|
#include <netinet/tcp.h> |
37 |
|
|
38 |
|
#include "cache/cache_varnishd.h" |
39 |
|
|
40 |
|
#include "acceptor/cache_acceptor.h" |
41 |
|
#include "acceptor/acceptor_priv.h" |
42 |
|
#include "acceptor/acceptor_tcp.h" |
43 |
|
|
44 |
|
#include "cache/cache_transport.h" |
45 |
|
#include "cache/cache_pool.h" |
46 |
|
#include "common/heritage.h" |
47 |
|
|
48 |
|
#include "vcli_serve.h" |
49 |
|
#include "vsa.h" |
50 |
|
#include "vtcp.h" |
51 |
|
#include "vtim.h" |
52 |
|
|
53 |
|
/*-------------------------------------------------------------------- |
54 |
|
* TCP options we want to control |
55 |
|
*/ |
56 |
|
|
57 |
|
static struct sock_opt sock_opts[] = { |
58 |
|
/* Note: Setting the mod counter to something not-zero is needed |
59 |
|
* to force the setsockopt() calls on startup */ |
60 |
|
|
61 |
|
SOCK_OPT(SOL_SOCKET, SO_LINGER, struct linger) |
62 |
|
SOCK_OPT(SOL_SOCKET, SO_KEEPALIVE, int) |
63 |
|
SOCK_OPT(SOL_SOCKET, SO_SNDTIMEO, struct timeval) |
64 |
|
SOCK_OPT(SOL_SOCKET, SO_RCVTIMEO, struct timeval) |
65 |
|
|
66 |
|
SOCK_OPT(IPPROTO_TCP, TCP_NODELAY, int) |
67 |
|
|
68 |
|
#if defined(HAVE_TCP_KEEP) |
69 |
|
SOCK_OPT(IPPROTO_TCP, TCP_KEEPIDLE, int) |
70 |
|
SOCK_OPT(IPPROTO_TCP, TCP_KEEPCNT, int) |
71 |
|
SOCK_OPT(IPPROTO_TCP, TCP_KEEPINTVL, int) |
72 |
|
#elif defined(HAVE_TCP_KEEPALIVE) |
73 |
|
SOCK_OPT(IPPROTO_TCP, TCP_KEEPALIVE, int) |
74 |
|
#endif |
75 |
|
|
76 |
|
#undef SOCK_OPT |
77 |
|
}; |
78 |
|
|
79 |
|
static const int n_sock_opts = sizeof sock_opts / sizeof sock_opts[0]; |
80 |
|
|
81 |
|
/*-------------------------------------------------------------------- |
82 |
|
* We want to get out of any kind of trouble-hit TCP connections as fast |
83 |
|
* as absolutely possible, so we set them LINGER disabled, so that even if |
84 |
|
* there are outstanding write data on the socket, a close(2) will return |
85 |
|
* immediately. |
86 |
|
*/ |
87 |
|
static const struct linger disable_so_linger = { |
88 |
|
.l_onoff = 0, |
89 |
|
}; |
90 |
|
|
91 |
|
/* |
92 |
|
* We turn on keepalives by default to assist in detecting clients that have |
93 |
|
* hung up on connections returning from waitinglists |
94 |
|
*/ |
95 |
|
static const unsigned enable_so_keepalive = 1; |
96 |
|
|
97 |
|
/* We disable Nagle's algorithm in favor of low latency setups. |
98 |
|
*/ |
99 |
|
static const unsigned enable_tcp_nodelay = 1; |
100 |
|
|
101 |
|
/*-------------------------------------------------------------------- |
102 |
|
* Some kernels have bugs/limitations with respect to which options are |
103 |
|
* inherited from the accept/listen socket, so we have to keep track of |
104 |
|
* which, if any, sockopts we have to set on the accepted socket. |
105 |
|
*/ |
106 |
|
|
107 |
|
static int |
108 |
69976 |
vca_tcp_sockopt_init(void) |
109 |
|
{ |
110 |
|
struct sock_opt *so; |
111 |
|
union sock_arg tmp; |
112 |
69976 |
int n, chg = 0; |
113 |
|
size_t sz; |
114 |
|
|
115 |
69976 |
memset(&tmp, 0, sizeof tmp); |
116 |
|
|
117 |
629784 |
for (n = 0; n < n_sock_opts; n++) { |
118 |
559808 |
so = &sock_opts[n]; |
119 |
|
|
120 |
|
#define SET_VAL(nm, so, fld, val) \ |
121 |
|
do { \ |
122 |
|
if (!strcmp(#nm, so->strname)) { \ |
123 |
|
assert(so->sz == sizeof so->arg->fld); \ |
124 |
|
so->arg->fld = (val); \ |
125 |
|
} \ |
126 |
|
} while (0) |
127 |
|
|
128 |
|
#define NEW_VAL(nm, so, fld, val) \ |
129 |
|
do { \ |
130 |
|
if (!strcmp(#nm, so->strname)) { \ |
131 |
|
sz = sizeof tmp.fld; \ |
132 |
|
assert(so->sz == sz); \ |
133 |
|
tmp.fld = (val); \ |
134 |
|
if (memcmp(&so->arg->fld, &(tmp.fld), sz)) { \ |
135 |
|
memcpy(&so->arg->fld, &(tmp.fld), sz); \ |
136 |
|
so->mod++; \ |
137 |
|
chg = 1; \ |
138 |
|
} \ |
139 |
|
} \ |
140 |
|
} while (0) |
141 |
|
|
142 |
559808 |
SET_VAL(SO_LINGER, so, lg, disable_so_linger); |
143 |
559808 |
SET_VAL(SO_KEEPALIVE, so, i, enable_so_keepalive); |
144 |
559808 |
NEW_VAL(SO_SNDTIMEO, so, tv, |
145 |
|
VTIM_timeval_sock(cache_param->idle_send_timeout)); |
146 |
559808 |
NEW_VAL(SO_RCVTIMEO, so, tv, |
147 |
|
VTIM_timeval_sock(cache_param->timeout_idle)); |
148 |
559808 |
SET_VAL(TCP_NODELAY, so, i, enable_tcp_nodelay); |
149 |
|
#if defined(HAVE_TCP_KEEP) |
150 |
559808 |
NEW_VAL(TCP_KEEPIDLE, so, i, |
151 |
|
(int)cache_param->tcp_keepalive_time); |
152 |
559808 |
NEW_VAL(TCP_KEEPCNT, so, i, |
153 |
|
(int)cache_param->tcp_keepalive_probes); |
154 |
559808 |
NEW_VAL(TCP_KEEPINTVL, so, i, |
155 |
|
(int)cache_param->tcp_keepalive_intvl); |
156 |
|
#elif defined(HAVE_TCP_KEEPALIVE) |
157 |
|
NEW_VAL(TCP_KEEPALIVE, so, i, |
158 |
|
(int)cache_param->tcp_keepalive_time); |
159 |
|
#endif |
160 |
559808 |
} |
161 |
|
|
162 |
69976 |
return (chg); |
163 |
|
} |
164 |
|
|
165 |
|
static void |
166 |
33334 |
vca_tcp_sockopt_test(const struct listen_sock *ls, const struct sess *sp) |
167 |
|
{ |
168 |
|
struct conn_heritage *ch; |
169 |
|
struct sock_opt *so; |
170 |
|
union sock_arg tmp; |
171 |
|
socklen_t l; |
172 |
|
int i, n; |
173 |
|
|
174 |
33334 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
175 |
33334 |
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); |
176 |
|
|
177 |
299942 |
for (n = 0; n < n_sock_opts; n++) { |
178 |
266696 |
so = &sock_opts[n]; |
179 |
266696 |
ch = &ls->conn_heritage[n]; |
180 |
|
|
181 |
266696 |
if (ch->sess_set) { |
182 |
1854 |
VSL(SLT_Debug, sp->vxid, |
183 |
|
"sockopt: Not testing nonhereditary %s for %s=%s", |
184 |
927 |
so->strname, ls->name, ls->endpoint); |
185 |
927 |
continue; |
186 |
|
} |
187 |
|
|
188 |
265769 |
memset(&tmp, 0, sizeof tmp); |
189 |
265769 |
l = so->sz; |
190 |
265769 |
i = getsockopt(sp->fd, so->level, so->optname, &tmp, &l); |
191 |
|
|
192 |
265769 |
if (i == 0 && memcmp(&tmp, so->arg, so->sz)) { |
193 |
131482 |
VSL(SLT_Debug, sp->vxid, |
194 |
|
"sockopt: Test confirmed %s non heredity for %s=%s", |
195 |
65741 |
so->strname, ls->name, ls->endpoint); |
196 |
65741 |
ch->sess_set = 1; |
197 |
65741 |
} |
198 |
|
|
199 |
265769 |
if (i && errno != ENOPROTOOPT) |
200 |
0 |
VTCP_Assert(i); |
201 |
265681 |
} |
202 |
33246 |
} |
203 |
|
|
204 |
|
static void |
205 |
110724 |
vca_tcp_sockopt_set(const struct listen_sock *ls, const struct sess *sp) |
206 |
|
{ |
207 |
|
struct conn_heritage *ch; |
208 |
|
struct sock_opt *so; |
209 |
|
vxid_t vxid; |
210 |
|
int n, sock; |
211 |
|
|
212 |
110724 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
213 |
|
|
214 |
110724 |
if (sp != NULL) { |
215 |
74926 |
CHECK_OBJ(sp, SESS_MAGIC); |
216 |
74926 |
sock = sp->fd; |
217 |
74926 |
vxid = sp->vxid; |
218 |
74926 |
} else { |
219 |
35798 |
sock = ls->sock; |
220 |
35798 |
vxid = NO_VXID; |
221 |
|
} |
222 |
|
|
223 |
996452 |
for (n = 0; n < n_sock_opts; n++) { |
224 |
885728 |
so = &sock_opts[n]; |
225 |
885728 |
ch = &ls->conn_heritage[n]; |
226 |
|
|
227 |
885728 |
if (sp == NULL && ch->listen_mod == so->mod) { |
228 |
826 |
VSL(SLT_Debug, vxid, |
229 |
|
"sockopt: Not setting unmodified %s for %s=%s", |
230 |
826 |
so->strname, ls->name, ls->endpoint); |
231 |
826 |
continue; |
232 |
|
} |
233 |
|
|
234 |
884902 |
if (sp != NULL && !ch->sess_set) { |
235 |
899010 |
VSL(SLT_Debug, sp->vxid, |
236 |
|
"sockopt: %s may be inherited for %s=%s", |
237 |
449505 |
so->strname, ls->name, ls->endpoint); |
238 |
449505 |
continue; |
239 |
|
} |
240 |
|
|
241 |
435397 |
VSL(SLT_Debug, vxid, |
242 |
|
"sockopt: Setting %s for %s=%s", |
243 |
435397 |
so->strname, ls->name, ls->endpoint); |
244 |
435397 |
VTCP_Assert(setsockopt(sock, |
245 |
|
so->level, so->optname, so->arg, so->sz)); |
246 |
|
|
247 |
435397 |
if (sp == NULL) |
248 |
285558 |
ch->listen_mod = so->mod; |
249 |
435397 |
} |
250 |
110724 |
} |
251 |
|
|
252 |
|
static void |
253 |
36621 |
vca_tcp_init(void) |
254 |
|
{ |
255 |
|
|
256 |
36621 |
} |
257 |
|
|
258 |
|
static int |
259 |
35680 |
vca_tcp_listen(struct cli *cli, struct listen_sock *ls) |
260 |
|
{ |
261 |
|
|
262 |
35680 |
CHECK_OBJ_NOTNULL(ls->transport, TRANSPORT_MAGIC); |
263 |
35680 |
assert (ls->sock > 0); // We know where stdin is |
264 |
|
|
265 |
35680 |
if (cache_param->tcp_fastopen && |
266 |
0 |
VTCP_fastopen(ls->sock, cache_param->listen_depth)) |
267 |
0 |
VSL(SLT_Error, NO_VXID, |
268 |
|
"Kernel TCP Fast Open: sock=%d, errno=%d %s", |
269 |
0 |
ls->sock, errno, VAS_errtxt(errno)); |
270 |
|
|
271 |
35680 |
if (listen(ls->sock, cache_param->listen_depth)) { |
272 |
0 |
VCLI_SetResult(cli, CLIS_CANT); |
273 |
0 |
VCLI_Out(cli, "Listen failed on socket '%s': %s", |
274 |
0 |
ls->endpoint, VAS_errtxt(errno)); |
275 |
0 |
return (-1); |
276 |
|
} |
277 |
|
|
278 |
35680 |
AZ(ls->conn_heritage); |
279 |
35680 |
ls->conn_heritage = calloc(n_sock_opts, |
280 |
|
sizeof *ls->conn_heritage); |
281 |
35680 |
AN(ls->conn_heritage); |
282 |
|
|
283 |
35680 |
ls->test_heritage = 1; |
284 |
35680 |
vca_tcp_sockopt_set(ls, NULL); |
285 |
|
|
286 |
35680 |
if (cache_param->accept_filter && VTCP_filter_http(ls->sock)) |
287 |
70960 |
VSL(SLT_Error, NO_VXID, |
288 |
|
"Kernel filtering: sock=%d, errno=%d %s", |
289 |
35480 |
ls->sock, errno, VAS_errtxt(errno)); |
290 |
|
|
291 |
35680 |
return (0); |
292 |
35680 |
} |
293 |
|
|
294 |
|
static void |
295 |
36440 |
vca_tcp_start(struct cli *cli) |
296 |
|
{ |
297 |
|
struct listen_sock *ls; |
298 |
|
|
299 |
36440 |
ASSERT_CLI(); |
300 |
|
|
301 |
36440 |
(void)vca_tcp_sockopt_init(); |
302 |
|
|
303 |
72120 |
VTAILQ_FOREACH(ls, &TCP_acceptor.socks, vcalist) { |
304 |
35680 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
305 |
|
|
306 |
35680 |
if (vca_tcp_listen(cli, ls)) |
307 |
0 |
return; |
308 |
35680 |
} |
309 |
36440 |
} |
310 |
|
|
311 |
|
static void |
312 |
70840 |
vca_tcp_event(struct cli *cli, struct listen_sock *ls, enum vca_event event) |
313 |
|
{ |
314 |
|
char h[VTCP_ADDRBUFSIZE], p[VTCP_PORTBUFSIZE]; |
315 |
|
|
316 |
70840 |
(void) ls; // XXX const? |
317 |
70840 |
switch (event) { |
318 |
|
case VCA_EVENT_LADDR: |
319 |
70840 |
VTCP_myname(ls->sock, h, sizeof h, p, sizeof p); |
320 |
70840 |
VCLI_Out(cli, "%s %s %s\n", ls->name, h, p); |
321 |
70840 |
break; |
322 |
|
default: |
323 |
0 |
WRONG("INVALID VCA_EVENT"); |
324 |
0 |
} |
325 |
70840 |
} |
326 |
|
|
327 |
|
/*-------------------------------------------------------------------- |
328 |
|
* The pool-task for a newly accepted session |
329 |
|
* |
330 |
|
* Called from assigned worker thread |
331 |
|
*/ |
332 |
|
|
333 |
|
static void |
334 |
74921 |
vca_mk_tcp(const struct wrk_accept *wa, |
335 |
|
struct sess *sp, char *laddr, char *lport, char *raddr, char *rport) |
336 |
|
{ |
337 |
74921 |
struct suckaddr *sa = NULL; |
338 |
|
ssize_t sz; |
339 |
|
|
340 |
74921 |
AN(SES_Reserve_remote_addr(sp, &sa, &sz)); |
341 |
74921 |
AN(sa); |
342 |
74921 |
assert(sz == vsa_suckaddr_len); |
343 |
74921 |
AN(VSA_Build(sa, &wa->acceptaddr, wa->acceptaddrlen)); |
344 |
74921 |
sp->sattr[SA_CLIENT_ADDR] = sp->sattr[SA_REMOTE_ADDR]; |
345 |
|
|
346 |
74921 |
VTCP_name(sa, raddr, VTCP_ADDRBUFSIZE, rport, VTCP_PORTBUFSIZE); |
347 |
74921 |
AN(SES_Set_String_Attr(sp, SA_CLIENT_IP, raddr)); |
348 |
74921 |
AN(SES_Set_String_Attr(sp, SA_CLIENT_PORT, rport)); |
349 |
|
|
350 |
|
|
351 |
74921 |
AN(SES_Reserve_local_addr(sp, &sa, &sz)); |
352 |
74921 |
AN(VSA_getsockname(sp->fd, sa, sz)); |
353 |
74921 |
sp->sattr[SA_SERVER_ADDR] = sp->sattr[SA_LOCAL_ADDR]; |
354 |
74921 |
VTCP_name(sa, laddr, VTCP_ADDRBUFSIZE, lport, VTCP_PORTBUFSIZE); |
355 |
74921 |
} |
356 |
|
|
357 |
|
static void v_matchproto_(task_func_t) |
358 |
74928 |
vca_tcp_make_session(struct worker *wrk, void *arg) |
359 |
|
{ |
360 |
|
char laddr[VTCP_ADDRBUFSIZE]; |
361 |
|
char lport[VTCP_PORTBUFSIZE]; |
362 |
|
char raddr[VTCP_ADDRBUFSIZE]; |
363 |
|
char rport[VTCP_PORTBUFSIZE]; |
364 |
|
struct wrk_accept *wa; |
365 |
|
struct sess *sp; |
366 |
|
struct req *req; |
367 |
|
|
368 |
74928 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
369 |
74928 |
CAST_OBJ_NOTNULL(wa, arg, WRK_ACCEPT_MAGIC); |
370 |
|
|
371 |
74928 |
VTCP_blocking(wa->acceptsock); |
372 |
|
|
373 |
|
/* Turn accepted socket into a session */ |
374 |
74928 |
AN(WS_Reservation(wrk->aws)); |
375 |
74928 |
sp = SES_New(wrk->pool); |
376 |
74928 |
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); |
377 |
74928 |
wrk->stats->s_sess++; |
378 |
|
|
379 |
74928 |
sp->t_open = VTIM_real(); |
380 |
74928 |
sp->t_idle = sp->t_open; |
381 |
74928 |
sp->vxid = VXID_Get(wrk, VSL_CLIENTMARKER); |
382 |
|
|
383 |
74928 |
sp->fd = wa->acceptsock; |
384 |
74928 |
wa->acceptsock = -1; |
385 |
74928 |
sp->listen_sock = wa->acceptlsock; |
386 |
|
|
387 |
74928 |
assert((size_t)wa->acceptaddrlen <= vsa_suckaddr_len); |
388 |
|
|
389 |
74928 |
vca_mk_tcp(wa, sp, laddr, lport, raddr, rport); |
390 |
|
|
391 |
74928 |
AN(wa->acceptlsock->name); |
392 |
149856 |
VSL(SLT_Begin, sp->vxid, "sess 0 %s", |
393 |
74928 |
wa->acceptlsock->transport->name); |
394 |
149856 |
VSL(SLT_SessOpen, sp->vxid, "%s %s %s %s %s %.6f %d", |
395 |
74928 |
raddr, rport, wa->acceptlsock->name, laddr, lport, |
396 |
74928 |
sp->t_open, sp->fd); |
397 |
|
|
398 |
74928 |
vca_pace_good(); |
399 |
74928 |
wrk->stats->sess_conn++; |
400 |
|
|
401 |
74928 |
if (wa->acceptlsock->test_heritage) { |
402 |
33334 |
vca_tcp_sockopt_test(wa->acceptlsock, sp); |
403 |
33334 |
wa->acceptlsock->test_heritage = 0; |
404 |
33334 |
} |
405 |
|
|
406 |
74928 |
vca_tcp_sockopt_set(wa->acceptlsock, sp); |
407 |
|
|
408 |
74928 |
req = Req_New(sp); |
409 |
74928 |
CHECK_OBJ_NOTNULL(req, REQ_MAGIC); |
410 |
74928 |
req->htc->rfd = &sp->fd; |
411 |
|
|
412 |
74928 |
SES_SetTransport(wrk, sp, req, wa->acceptlsock->transport); |
413 |
74928 |
WS_Release(wrk->aws, 0); |
414 |
74928 |
} |
415 |
|
|
416 |
|
/*-------------------------------------------------------------------- |
417 |
|
* This function accepts on a single socket for a single thread pool. |
418 |
|
* |
419 |
|
* As long as we can stick the accepted connection to another thread |
420 |
|
* we do so, otherwise we put the socket back on the "BACK" pool |
421 |
|
* and handle the new connection ourselves. |
422 |
|
*/ |
423 |
|
|
424 |
|
static void v_matchproto_(task_func_t) |
425 |
127 |
vca_tcp_accept_task(struct worker *wrk, void *arg) |
426 |
|
{ |
427 |
|
char laddr[VTCP_ADDRBUFSIZE]; |
428 |
|
char lport[VTCP_PORTBUFSIZE]; |
429 |
|
struct listen_sock *ls; |
430 |
|
struct wrk_accept wa; |
431 |
|
struct poolsock *ps; |
432 |
|
int i; |
433 |
|
|
434 |
127 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
435 |
127 |
CAST_OBJ_NOTNULL(ps, arg, POOLSOCK_MAGIC); |
436 |
127 |
ls = ps->lsock; |
437 |
127 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
438 |
127 |
CHECK_OBJ_NOTNULL(ps->pool, POOL_MAGIC); |
439 |
|
|
440 |
49271 |
while (!pool_accepting) |
441 |
49144 |
VTIM_sleep(.1); |
442 |
|
|
443 |
|
/* Dont hold on to (possibly) discarded VCLs */ |
444 |
127 |
if (wrk->wpriv->vcl != NULL) |
445 |
1 |
VCL_Rel(&wrk->wpriv->vcl); |
446 |
|
|
447 |
74962 |
while (!ps->pool->die) { |
448 |
74882 |
INIT_OBJ(&wa, WRK_ACCEPT_MAGIC); |
449 |
74882 |
wa.acceptlsock = ls; |
450 |
|
|
451 |
74882 |
vca_pace_check(); |
452 |
|
|
453 |
74882 |
wa.acceptaddrlen = sizeof wa.acceptaddr; |
454 |
74882 |
do { |
455 |
290712 |
i = accept(ls->sock, (void*)&wa.acceptaddr, |
456 |
145356 |
&wa.acceptaddrlen); |
457 |
145356 |
} while (i < 0 && errno == EAGAIN && !ps->pool->die); |
458 |
|
|
459 |
74882 |
if (i < 0 && ps->pool->die) |
460 |
0 |
break; |
461 |
|
|
462 |
74882 |
if (i < 0 && ls->sock == -2) { |
463 |
|
/* Shut down in progress */ |
464 |
0 |
sleep(2); |
465 |
0 |
continue; |
466 |
|
} |
467 |
|
|
468 |
74882 |
if (i < 0) { |
469 |
0 |
switch (errno) { |
470 |
|
case ECONNABORTED: |
471 |
0 |
wrk->stats->sess_fail_econnaborted++; |
472 |
0 |
break; |
473 |
|
case EINTR: |
474 |
0 |
wrk->stats->sess_fail_eintr++; |
475 |
0 |
break; |
476 |
|
case EMFILE: |
477 |
0 |
wrk->stats->sess_fail_emfile++; |
478 |
0 |
vca_pace_bad(); |
479 |
0 |
break; |
480 |
|
case EBADF: |
481 |
0 |
wrk->stats->sess_fail_ebadf++; |
482 |
0 |
vca_pace_bad(); |
483 |
0 |
break; |
484 |
|
case ENOBUFS: |
485 |
|
case ENOMEM: |
486 |
0 |
wrk->stats->sess_fail_enomem++; |
487 |
0 |
vca_pace_bad(); |
488 |
0 |
break; |
489 |
|
default: |
490 |
0 |
wrk->stats->sess_fail_other++; |
491 |
0 |
vca_pace_bad(); |
492 |
0 |
break; |
493 |
|
} |
494 |
|
|
495 |
0 |
i = errno; |
496 |
0 |
wrk->stats->sess_fail++; |
497 |
|
|
498 |
0 |
VTCP_myname(ls->sock, laddr, VTCP_ADDRBUFSIZE, |
499 |
0 |
lport, VTCP_PORTBUFSIZE); |
500 |
|
|
501 |
0 |
VSL(SLT_SessError, NO_VXID, "%s %s %s %d %d \"%s\"", |
502 |
0 |
wa.acceptlsock->name, laddr, lport, |
503 |
0 |
ls->sock, i, VAS_errtxt(i)); |
504 |
0 |
(void)Pool_TrySumstat(wrk); |
505 |
0 |
continue; |
506 |
|
} |
507 |
|
|
508 |
74882 |
wa.acceptsock = i; |
509 |
|
|
510 |
74882 |
if (!Pool_Task_Arg(wrk, TASK_QUEUE_REQ, |
511 |
|
vca_tcp_make_session, &wa, sizeof wa)) { |
512 |
|
/* |
513 |
|
* We couldn't get another thread, so we will handle |
514 |
|
* the request in this worker thread, but first we |
515 |
|
* must reschedule the listening task so it will be |
516 |
|
* taken up by another thread again. |
517 |
|
*/ |
518 |
127 |
if (!ps->pool->die) { |
519 |
47 |
AZ(Pool_Task(wrk->pool, ps->task, |
520 |
|
TASK_QUEUE_VCA)); |
521 |
47 |
return; |
522 |
|
} |
523 |
80 |
} |
524 |
74835 |
if (!ps->pool->die && DO_DEBUG(DBG_SLOW_ACCEPTOR)) |
525 |
240 |
VTIM_sleep(2.0); |
526 |
|
|
527 |
|
} |
528 |
|
|
529 |
80 |
VSL(SLT_Debug, NO_VXID, "XXX Accept thread dies %p", ps); |
530 |
80 |
FREE_OBJ(ps); |
531 |
127 |
} |
532 |
|
|
533 |
|
static void |
534 |
72457 |
vca_tcp_accept(struct pool *pp) |
535 |
|
{ |
536 |
|
struct listen_sock *ls; |
537 |
|
struct poolsock *ps; |
538 |
|
|
539 |
143537 |
VTAILQ_FOREACH(ls, &TCP_acceptor.socks, vcalist) { |
540 |
71080 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
541 |
|
|
542 |
71080 |
ALLOC_OBJ(ps, POOLSOCK_MAGIC); |
543 |
71080 |
AN(ps); |
544 |
71080 |
ps->lsock = ls; |
545 |
71080 |
ps->task->func = vca_tcp_accept_task; |
546 |
71080 |
ps->task->priv = ps; |
547 |
71080 |
ps->pool = pp; |
548 |
71080 |
VTAILQ_INSERT_TAIL(&pp->poolsocks, ps, list); |
549 |
71080 |
AZ(Pool_Task(pp, ps->task, TASK_QUEUE_VCA)); |
550 |
71080 |
} |
551 |
72457 |
} |
552 |
|
|
553 |
|
static void |
554 |
33536 |
vca_tcp_update(pthread_mutex_t *shut_mtx) |
555 |
|
{ |
556 |
|
struct listen_sock *ls; |
557 |
|
|
558 |
33536 |
if (!vca_tcp_sockopt_init()) |
559 |
33378 |
return; |
560 |
|
|
561 |
158 |
PTOK(pthread_mutex_lock(shut_mtx)); |
562 |
|
|
563 |
276 |
VTAILQ_FOREACH(ls, &TCP_acceptor.socks, vcalist) { |
564 |
118 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
565 |
|
|
566 |
118 |
if (ls->sock == -2) |
567 |
0 |
continue; // VCA_Shutdown |
568 |
118 |
assert (ls->sock > 0); |
569 |
118 |
vca_tcp_sockopt_set(ls, NULL); |
570 |
|
/* If one of the options on a socket has |
571 |
|
* changed, also force a retest of whether |
572 |
|
* the values are inherited to the |
573 |
|
* accepted sockets. This should then |
574 |
|
* catch any false positives from previous |
575 |
|
* tests that could happen if the set |
576 |
|
* value of an option happened to just be |
577 |
|
* the OS default for that value, and |
578 |
|
* wasn't actually inherited from the |
579 |
|
* listening socket. */ |
580 |
118 |
ls->test_heritage = 1; |
581 |
118 |
} |
582 |
|
|
583 |
158 |
PTOK(pthread_mutex_unlock(shut_mtx)); |
584 |
33536 |
} |
585 |
|
|
586 |
|
static void |
587 |
36120 |
vca_tcp_shutdown(void) |
588 |
|
{ |
589 |
|
struct listen_sock *ls; |
590 |
|
int i; |
591 |
|
|
592 |
71480 |
VTAILQ_FOREACH(ls, &TCP_acceptor.socks, vcalist) { |
593 |
35360 |
CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC); |
594 |
|
|
595 |
35360 |
i = ls->sock; |
596 |
35360 |
ls->sock = -2; |
597 |
35360 |
(void)close(i); |
598 |
35360 |
} |
599 |
36120 |
} |
600 |
|
|
601 |
|
struct acceptor TCP_acceptor = { |
602 |
|
.magic = ACCEPTOR_MAGIC, |
603 |
|
.name = "tcp", |
604 |
|
.config = vca_tcp_config, |
605 |
|
.init = vca_tcp_init, |
606 |
|
.open = vca_tcp_open, |
607 |
|
.reopen = vca_tcp_reopen, |
608 |
|
.start = vca_tcp_start, |
609 |
|
.event = vca_tcp_event, |
610 |
|
.accept = vca_tcp_accept, |
611 |
|
.update = vca_tcp_update, |
612 |
|
.shutdown = vca_tcp_shutdown, |
613 |
|
}; |