| | varnish-cache/bin/varnishd/cache/cache_backend_probe.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2011 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 |
|
* Poll backends for collection of health statistics |
31 |
|
* |
32 |
|
* We co-opt threads from the worker pool for probing the backends, |
33 |
440 |
* but we want to avoid a potentially messy cleanup operation when we |
34 |
440 |
* retire the backend, so the thread owns the health information, which |
35 |
440 |
* the backend references, rather than the other way around. |
36 |
440 |
* |
37 |
440 |
*/ |
38 |
440 |
|
39 |
440 |
#include "config.h" |
40 |
520 |
|
41 |
|
#include <poll.h> |
42 |
|
#include <stdio.h> |
43 |
|
#include <stdlib.h> |
44 |
|
|
45 |
|
#include "cache_varnishd.h" |
46 |
|
|
47 |
|
#include "vbh.h" |
48 |
|
#include "vsa.h" |
49 |
|
#include "vtcp.h" |
50 |
|
#include "vtim.h" |
51 |
|
|
52 |
|
#include "cache_backend.h" |
53 |
|
#include "cache_conn_pool.h" |
54 |
|
|
55 |
|
#include "VSC_vbe.h" |
56 |
|
|
57 |
|
struct vbp_state { |
58 |
|
const char *name; |
59 |
|
}; |
60 |
|
|
61 |
|
#define VBP_STATE(n) static const struct vbp_state vbp_state_ ## n [1] = {{ .name = #n }} |
62 |
|
VBP_STATE(scheduled); |
63 |
|
VBP_STATE(running); |
64 |
|
VBP_STATE(cold); |
65 |
|
VBP_STATE(cooling); |
66 |
|
VBP_STATE(deleted); |
67 |
|
#undef VBP_STATE |
68 |
|
|
69 |
|
/* Default averaging rate, we want something pretty responsive */ |
70 |
|
#define AVG_RATE 4 |
71 |
|
|
72 |
|
struct vbp_target { |
73 |
|
unsigned magic; |
74 |
|
#define VBP_TARGET_MAGIC 0x6b7cb656 |
75 |
|
|
76 |
|
VRT_BACKEND_PROBE_FIELDS() |
77 |
|
|
78 |
|
struct backend *backend; |
79 |
|
struct conn_pool *conn_pool; |
80 |
|
|
81 |
|
char *req; |
82 |
|
int req_len; |
83 |
|
|
84 |
|
char resp_buf[128]; |
85 |
|
unsigned good; |
86 |
|
|
87 |
|
/* Collected statistics */ |
88 |
|
#define BITMAP(n, c, t, b) uintmax_t n; |
89 |
|
#include "tbl/backend_poll.h" |
90 |
|
|
91 |
|
vtim_dur last; |
92 |
|
vtim_dur avg; |
93 |
|
double rate; |
94 |
|
|
95 |
|
vtim_real due; |
96 |
|
const struct vbp_state *state; |
97 |
|
int heap_idx; |
98 |
|
struct pool_task task[1]; |
99 |
|
}; |
100 |
|
|
101 |
|
static struct lock vbp_mtx; |
102 |
|
static pthread_cond_t vbp_cond; |
103 |
|
static struct vbh *vbp_heap; |
104 |
|
|
105 |
|
static const unsigned char vbp_proxy_local[] = { |
106 |
|
0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, |
107 |
|
0x55, 0x49, 0x54, 0x0a, 0x20, 0x00, 0x00, 0x00, |
108 |
|
}; |
109 |
|
|
110 |
|
/*--------------------------------------------------------------------*/ |
111 |
|
|
112 |
|
static void |
113 |
240 |
vbp_delete(struct vbp_target *vt) |
114 |
|
{ |
115 |
240 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
116 |
|
|
117 |
240 |
assert(vt->heap_idx == VBH_NOIDX); |
118 |
|
|
119 |
|
#define DN(x) /**/ |
120 |
240 |
VRT_BACKEND_PROBE_HANDLE(); |
121 |
|
#undef DN |
122 |
240 |
VCP_Rel(&vt->conn_pool); |
123 |
240 |
free(vt->req); |
124 |
240 |
FREE_OBJ(vt); |
125 |
240 |
} |
126 |
|
|
127 |
|
|
128 |
|
/*-------------------------------------------------------------------- |
129 |
|
* Record pokings... |
130 |
|
*/ |
131 |
|
|
132 |
|
static void |
133 |
15793 |
vbp_start_poke(struct vbp_target *vt) |
134 |
|
{ |
135 |
15793 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
136 |
|
|
137 |
|
#define BITMAP(n, c, t, b) \ |
138 |
|
vt->n <<= 1; |
139 |
|
#include "tbl/backend_poll.h" |
140 |
|
|
141 |
|
vt->last = 0; |
142 |
|
vt->resp_buf[0] = '\0'; |
143 |
|
} |
144 |
|
|
145 |
|
static void |
146 |
15672 |
vbp_has_poked(struct vbp_target *vt) |
147 |
|
{ |
148 |
|
unsigned i, j; |
149 |
|
uint64_t u; |
150 |
|
|
151 |
15672 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
152 |
|
|
153 |
|
/* Calculate exponential average */ |
154 |
15672 |
if (vt->happy & 1) { |
155 |
13637 |
if (vt->rate < AVG_RATE) |
156 |
7210 |
vt->rate += 1.0; |
157 |
13637 |
vt->avg += (vt->last - vt->avg) / vt->rate; |
158 |
13637 |
} |
159 |
|
|
160 |
15672 |
u = vt->happy; |
161 |
129177 |
for (i = j = 0; i < vt->window; i++) { |
162 |
113505 |
if (u & 1) |
163 |
62753 |
j++; |
164 |
113505 |
u >>= 1; |
165 |
113505 |
} |
166 |
15672 |
vt->good = j; |
167 |
15672 |
} |
168 |
|
|
169 |
|
void |
170 |
10953 |
VBP_Update_Backend(struct vbp_target *vt) |
171 |
|
{ |
172 |
10953 |
unsigned i = 0, chg; |
173 |
|
char bits[10]; |
174 |
|
|
175 |
10953 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
176 |
|
|
177 |
|
#define BITMAP(n, c, t, b) \ |
178 |
|
bits[i++] = (vt->n & 1) ? c : '-'; |
179 |
|
#include "tbl/backend_poll.h" |
180 |
|
bits[i] = '\0'; |
181 |
10953 |
assert(i < sizeof bits); |
182 |
|
|
183 |
|
Lck_Lock(&vbp_mtx); |
184 |
10953 |
if (vt->backend == NULL) { |
185 |
0 |
Lck_Unlock(&vbp_mtx); |
186 |
0 |
return; |
187 |
|
} |
188 |
|
|
189 |
|
i = (vt->good < vt->threshold); |
190 |
|
chg = (i != vt->backend->sick); |
191 |
|
vt->backend->sick = i; |
192 |
10953 |
if (i && chg && vt->backend->director != NULL) |
193 |
1939 |
VDI_Event(vt->backend->director, VDI_EVENT_SICK); |
194 |
|
|
195 |
10953 |
AN(vt->backend->vcl_name); |
196 |
|
VSL(SLT_Backend_health, NO_VXID, |
197 |
|
"%s %s %s %s %u %u %u %.6f %.6f \"%s\"", |
198 |
|
vt->backend->vcl_name, chg ? "Went" : "Still", |
199 |
|
i ? "sick" : "healthy", bits, |
200 |
|
vt->good, vt->threshold, vt->window, |
201 |
|
vt->last, vt->avg, vt->resp_buf); |
202 |
|
vt->backend->vsc->happy = vt->happy; |
203 |
10953 |
if (chg) |
204 |
2819 |
vt->backend->changed = VTIM_real(); |
205 |
|
Lck_Unlock(&vbp_mtx); |
206 |
|
} |
207 |
|
|
208 |
|
static void |
209 |
3240 |
vbp_reset(struct vbp_target *vt) |
210 |
|
{ |
211 |
|
unsigned u; |
212 |
|
|
213 |
3240 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
214 |
3240 |
vt->avg = 0.0; |
215 |
3240 |
vt->rate = 0.0; |
216 |
|
#define BITMAP(n, c, t, b) \ |
217 |
|
vt->n = 0; |
218 |
|
#include "tbl/backend_poll.h" |
219 |
|
|
220 |
11200 |
for (u = 0; u < vt->initial; u++) { |
221 |
7960 |
vbp_start_poke(vt); |
222 |
7960 |
vt->happy |= 1; |
223 |
7960 |
vbp_has_poked(vt); |
224 |
7960 |
} |
225 |
|
} |
226 |
|
|
227 |
|
/*-------------------------------------------------------------------- |
228 |
|
* Poke one backend, once, but possibly at both IPv4 and IPv6 addresses. |
229 |
|
* |
230 |
|
* We do deliberately not use the stuff in cache_backend.c, because we |
231 |
|
* want to measure the backends response without local distractions. |
232 |
|
*/ |
233 |
|
|
234 |
|
static int |
235 |
7850 |
vbp_write(struct vbp_target *vt, int *sock, const void *buf, size_t len) |
236 |
|
{ |
237 |
|
int i; |
238 |
|
|
239 |
7850 |
i = write(*sock, buf, len); |
240 |
7850 |
VTCP_Assert(i); |
241 |
7850 |
if (i != len) { |
242 |
0 |
if (i < 0) { |
243 |
0 |
vt->err_xmit |= 1; |
244 |
0 |
bprintf(vt->resp_buf, "Write error %d (%s)", |
245 |
|
errno, VAS_errtxt(errno)); |
246 |
0 |
} else { |
247 |
0 |
bprintf(vt->resp_buf, |
248 |
|
"Short write (%d/%zu) error %d (%s)", |
249 |
|
i, len, errno, VAS_errtxt(errno)); |
250 |
|
} |
251 |
0 |
VTCP_close(sock); |
252 |
0 |
return (-1); |
253 |
|
} |
254 |
7850 |
return (0); |
255 |
7850 |
} |
256 |
|
|
257 |
|
static int |
258 |
245 |
vbp_write_proxy_v1(struct vbp_target *vt, int *sock) |
259 |
|
{ |
260 |
|
char buf[105]; /* maximum size for a TCP6 PROXY line with null char */ |
261 |
|
char addr[VTCP_ADDRBUFSIZE]; |
262 |
|
char port[VTCP_PORTBUFSIZE]; |
263 |
245 |
char vsabuf[vsa_suckaddr_len]; |
264 |
|
const struct suckaddr *sua; |
265 |
|
int proto; |
266 |
|
struct vsb vsb; |
267 |
|
|
268 |
245 |
sua = VSA_getsockname(*sock, vsabuf, sizeof vsabuf); |
269 |
245 |
AN(sua); |
270 |
245 |
AN(VSB_init(&vsb, buf, sizeof buf)); |
271 |
|
|
272 |
245 |
proto = VSA_Get_Proto(sua); |
273 |
245 |
if (proto == AF_INET || proto == AF_INET6) { |
274 |
125 |
VTCP_name(sua, addr, sizeof addr, port, sizeof port); |
275 |
125 |
VSB_printf(&vsb, "PROXY %s %s %s %s %s\r\n", |
276 |
125 |
proto == AF_INET ? "TCP4" : "TCP6", |
277 |
125 |
addr, addr, port, port); |
278 |
125 |
} else { |
279 |
120 |
VSB_cat(&vsb, "PROXY UNKNOWN\r\n"); |
280 |
|
} |
281 |
245 |
AZ(VSB_finish(&vsb)); |
282 |
|
|
283 |
245 |
VSB_fini(&vsb); |
284 |
245 |
return (vbp_write(vt, sock, buf, strlen(buf))); |
285 |
245 |
} |
286 |
|
|
287 |
|
static void |
288 |
7793 |
vbp_poke(struct vbp_target *vt) |
289 |
|
{ |
290 |
|
int s, i, proxy_header, err; |
291 |
|
vtim_real t_start, t_now, t_end; |
292 |
|
vtim_dur tmo; |
293 |
|
unsigned rlen, resp; |
294 |
|
char buf[8192], *p; |
295 |
7793 |
struct pollfd pfda[1], *pfd = pfda; |
296 |
|
const struct suckaddr *sa; |
297 |
|
|
298 |
7793 |
t_start = t_now = VTIM_real(); |
299 |
7793 |
t_end = t_start + vt->timeout; |
300 |
|
|
301 |
7793 |
s = VCP_Open(vt->conn_pool, t_end - t_now, &sa, &err); |
302 |
7793 |
if (s < 0) { |
303 |
499 |
bprintf(vt->resp_buf, "Open error %d (%s)", err, VAS_errtxt(err)); |
304 |
499 |
Lck_Lock(&vbp_mtx); |
305 |
499 |
if (vt->backend) |
306 |
499 |
VBE_Connect_Error(vt->backend->vsc, err); |
307 |
499 |
Lck_Unlock(&vbp_mtx); |
308 |
499 |
return; |
309 |
|
} |
310 |
|
|
311 |
7294 |
i = VSA_Get_Proto(sa); |
312 |
7294 |
if (VSA_Compare(sa, bogo_ip) == 0) |
313 |
2575 |
vt->good_unix |= 1; |
314 |
4719 |
else if (i == AF_INET) |
315 |
4719 |
vt->good_ipv4 |= 1; |
316 |
0 |
else if (i == AF_INET6) |
317 |
0 |
vt->good_ipv6 |= 1; |
318 |
|
else |
319 |
0 |
WRONG("Wrong probe protocol family"); |
320 |
|
|
321 |
7294 |
t_now = VTIM_real(); |
322 |
7294 |
tmo = t_end - t_now; |
323 |
7294 |
if (tmo <= 0) { |
324 |
80 |
bprintf(vt->resp_buf, |
325 |
|
"Open timeout %.3fs exceeded by %.3fs", |
326 |
|
vt->timeout, -tmo); |
327 |
80 |
VTCP_close(&s); |
328 |
80 |
return; |
329 |
|
} |
330 |
|
|
331 |
7214 |
Lck_Lock(&vbp_mtx); |
332 |
7214 |
if (vt->backend != NULL) |
333 |
7214 |
proxy_header = vt->backend->proxy_header; |
334 |
|
else |
335 |
0 |
proxy_header = -1; |
336 |
7214 |
Lck_Unlock(&vbp_mtx); |
337 |
|
|
338 |
7214 |
if (proxy_header < 0) { |
339 |
0 |
bprintf(vt->resp_buf, "%s", "No backend"); |
340 |
0 |
VTCP_close(&s); |
341 |
0 |
return; |
342 |
|
} |
343 |
|
|
344 |
|
/* Send the PROXY header */ |
345 |
7214 |
assert(proxy_header <= 2); |
346 |
7214 |
if (proxy_header == 1) { |
347 |
245 |
if (vbp_write_proxy_v1(vt, &s) != 0) |
348 |
0 |
return; |
349 |
7214 |
} else if (proxy_header == 2 && |
350 |
311 |
vbp_write(vt, &s, vbp_proxy_local, sizeof vbp_proxy_local) != 0) |
351 |
0 |
return; |
352 |
|
|
353 |
|
/* Send the request */ |
354 |
7214 |
if (vbp_write(vt, &s, vt->req, vt->req_len) != 0) |
355 |
0 |
return; |
356 |
|
|
357 |
7214 |
vt->good_xmit |= 1; |
358 |
|
|
359 |
7214 |
pfd->fd = s; |
360 |
7214 |
rlen = 0; |
361 |
14512 |
while (1) { |
362 |
14512 |
pfd->events = POLLIN; |
363 |
14512 |
pfd->revents = 0; |
364 |
14512 |
t_now = VTIM_real(); |
365 |
14512 |
tmo = t_end - t_now; |
366 |
14512 |
if (tmo <= 0) { |
367 |
0 |
bprintf(vt->resp_buf, |
368 |
|
"Poll timeout %.3fs exceeded by %.3fs", |
369 |
|
vt->timeout, -tmo); |
370 |
0 |
i = -1; |
371 |
0 |
break; |
372 |
|
} |
373 |
14512 |
i = poll(pfd, 1, VTIM_poll_tmo(tmo)); |
374 |
14512 |
if (i <= 0) { |
375 |
808 |
if (!i) { |
376 |
808 |
if (!vt->exp_close) |
377 |
167 |
break; |
378 |
641 |
errno = ETIMEDOUT; |
379 |
641 |
} |
380 |
641 |
bprintf(vt->resp_buf, "Poll error %d (%s)", |
381 |
|
errno, VAS_errtxt(errno)); |
382 |
641 |
i = -1; |
383 |
641 |
break; |
384 |
|
} |
385 |
13704 |
if (rlen < sizeof vt->resp_buf) |
386 |
25072 |
i = read(s, vt->resp_buf + rlen, |
387 |
12536 |
sizeof vt->resp_buf - rlen); |
388 |
|
else |
389 |
1168 |
i = read(s, buf, sizeof buf); |
390 |
13704 |
VTCP_Assert(i); |
391 |
13704 |
if (i <= 0) { |
392 |
6406 |
if (i < 0) |
393 |
54 |
bprintf(vt->resp_buf, "Read error %d (%s)", |
394 |
|
errno, VAS_errtxt(errno)); |
395 |
6406 |
break; |
396 |
|
} |
397 |
7298 |
rlen += i; |
398 |
|
} |
399 |
|
|
400 |
7214 |
VTCP_close(&s); |
401 |
|
|
402 |
7214 |
if (i < 0) { |
403 |
|
/* errno reported above */ |
404 |
695 |
vt->err_recv |= 1; |
405 |
695 |
return; |
406 |
|
} |
407 |
|
|
408 |
6519 |
if (rlen == 0) { |
409 |
242 |
bprintf(vt->resp_buf, "%s", "Empty response"); |
410 |
242 |
return; |
411 |
|
} |
412 |
|
|
413 |
|
/* So we have a good receive ... */ |
414 |
6277 |
t_now = VTIM_real(); |
415 |
6277 |
vt->last = t_now - t_start; |
416 |
6277 |
vt->good_recv |= 1; |
417 |
|
|
418 |
|
/* Now find out if we like the response */ |
419 |
6277 |
vt->resp_buf[sizeof vt->resp_buf - 1] = '\0'; |
420 |
6277 |
p = strchr(vt->resp_buf, '\r'); |
421 |
6277 |
if (p != NULL) |
422 |
6276 |
*p = '\0'; |
423 |
6277 |
p = strchr(vt->resp_buf, '\n'); |
424 |
6277 |
if (p != NULL) |
425 |
0 |
*p = '\0'; |
426 |
|
|
427 |
6277 |
i = sscanf(vt->resp_buf, "HTTP/%*f %u ", &resp); |
428 |
|
|
429 |
6277 |
if (i == 1 && resp == vt->exp_status) |
430 |
5637 |
vt->happy |= 1; |
431 |
7793 |
} |
432 |
|
|
433 |
|
/*-------------------------------------------------------------------- |
434 |
|
*/ |
435 |
|
static void |
436 |
9153 |
vbp_heap_insert(struct vbp_target *vt) |
437 |
|
{ |
438 |
|
// Lck_AssertHeld(&vbp_mtx); |
439 |
9153 |
VBH_insert(vbp_heap, vt); |
440 |
9153 |
if (VBH_root(vbp_heap) == vt) |
441 |
8483 |
PTOK(pthread_cond_signal(&vbp_cond)); |
442 |
9153 |
} |
443 |
|
|
444 |
|
/*-------------------------------------------------------------------- |
445 |
|
*/ |
446 |
|
|
447 |
|
/* |
448 |
|
* called when a task was successful or could not get scheduled |
449 |
|
* returns non-NULL if target is to be deleted (outside mtx) |
450 |
|
*/ |
451 |
|
static struct vbp_target * |
452 |
7713 |
vbp_task_complete(struct vbp_target *vt) |
453 |
|
{ |
454 |
7713 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
455 |
|
|
456 |
7713 |
Lck_AssertHeld(&vbp_mtx); |
457 |
|
|
458 |
7713 |
assert(vt->heap_idx == VBH_NOIDX); |
459 |
|
|
460 |
7713 |
if (vt->state == vbp_state_running) { |
461 |
7673 |
vt->state = vbp_state_scheduled; |
462 |
7673 |
vt->due = VTIM_real() + vt->interval; |
463 |
7673 |
vbp_heap_insert(vt); |
464 |
7673 |
vt = NULL; |
465 |
7713 |
} else if (vt->state == vbp_state_cooling) { |
466 |
40 |
vt->state = vbp_state_cold; |
467 |
40 |
vt = NULL; |
468 |
40 |
} else if (vt->state != vbp_state_deleted) { |
469 |
0 |
WRONG(vt->state->name); |
470 |
0 |
} |
471 |
7713 |
return (vt); |
472 |
|
} |
473 |
|
|
474 |
|
static void v_matchproto_(task_func_t) |
475 |
7713 |
vbp_task(struct worker *wrk, void *priv) |
476 |
|
{ |
477 |
|
struct vbp_target *vt; |
478 |
|
|
479 |
7713 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
480 |
7713 |
CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC); |
481 |
|
|
482 |
7713 |
AN(vt->req); |
483 |
7713 |
assert(vt->req_len > 0); |
484 |
|
|
485 |
7713 |
vbp_start_poke(vt); |
486 |
7713 |
vbp_poke(vt); |
487 |
7713 |
vbp_has_poked(vt); |
488 |
7713 |
VBP_Update_Backend(vt); |
489 |
|
|
490 |
7713 |
Lck_Lock(&vbp_mtx); |
491 |
7713 |
vt = vbp_task_complete(vt); |
492 |
7713 |
Lck_Unlock(&vbp_mtx); |
493 |
7713 |
if (vt == NULL) |
494 |
7713 |
return; |
495 |
0 |
vbp_delete(vt); |
496 |
7713 |
} |
497 |
|
|
498 |
|
/*-------------------------------------------------------------------- |
499 |
|
*/ |
500 |
|
|
501 |
|
static void * v_matchproto_(bgthread_t) |
502 |
36676 |
vbp_scheduler(struct worker *wrk, void *priv) |
503 |
|
{ |
504 |
|
vtim_real now, nxt; |
505 |
|
struct vbp_target *vt; |
506 |
|
int r; |
507 |
|
|
508 |
36676 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
509 |
36676 |
AZ(priv); |
510 |
36676 |
Lck_Lock(&vbp_mtx); |
511 |
89104 |
while (1) { |
512 |
96937 |
now = VTIM_real(); |
513 |
96937 |
vt = VBH_root(vbp_heap); |
514 |
96937 |
if (vt == NULL) { |
515 |
44724 |
nxt = 8.192 + now; |
516 |
44724 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
517 |
96937 |
} else if (vt->due > now) { |
518 |
7704 |
nxt = vt->due; |
519 |
7704 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
520 |
7704 |
} else { |
521 |
44509 |
assert(vt->state == vbp_state_scheduled); |
522 |
7833 |
VBH_delete(vbp_heap, vt->heap_idx); |
523 |
7833 |
vt->state = vbp_state_running; |
524 |
7833 |
vt->task->func = vbp_task; |
525 |
7833 |
vt->task->priv = vt; |
526 |
7833 |
Lck_Unlock(&vbp_mtx); |
527 |
|
|
528 |
7833 |
r = Pool_Task_Any(vt->task, TASK_QUEUE_REQ); |
529 |
|
|
530 |
7833 |
Lck_Lock(&vbp_mtx); |
531 |
7833 |
if (r == 0) |
532 |
7833 |
continue; |
533 |
0 |
vt = vbp_task_complete(vt); |
534 |
0 |
if (vt == NULL) |
535 |
0 |
continue; |
536 |
0 |
Lck_Unlock(&vbp_mtx); |
537 |
|
|
538 |
0 |
vbp_delete(vt); |
539 |
|
|
540 |
0 |
Lck_Lock(&vbp_mtx); |
541 |
|
} |
542 |
|
} |
543 |
|
NEEDLESS(Lck_Unlock(&vbp_mtx)); |
544 |
|
NEEDLESS(return (NULL)); |
545 |
|
} |
546 |
|
|
547 |
|
|
548 |
|
/*-------------------------------------------------------------------- |
549 |
|
* Cli functions |
550 |
|
*/ |
551 |
|
|
552 |
|
static void |
553 |
1560 |
vbp_bitmap(struct vsb *vsb, char c, uint64_t map, const char *lbl) |
554 |
|
{ |
555 |
|
int i; |
556 |
1560 |
uint64_t u = (1ULL << 63); |
557 |
|
|
558 |
1560 |
VSB_cat(vsb, " "); |
559 |
101400 |
for (i = 0; i < 64; i++) { |
560 |
99840 |
if (map & u) |
561 |
9966 |
VSB_putc(vsb, c); |
562 |
|
else |
563 |
89874 |
VSB_putc(vsb, '-'); |
564 |
99840 |
map <<= 1; |
565 |
99840 |
} |
566 |
1560 |
VSB_printf(vsb, " %s\n", lbl); |
567 |
1560 |
} |
568 |
|
|
569 |
|
/*lint -e{506} constant value boolean */ |
570 |
|
/*lint -e{774} constant value boolean */ |
571 |
|
void |
572 |
3120 |
VBP_Status(struct vsb *vsb, const struct backend *be, int details, int json) |
573 |
|
{ |
574 |
|
struct vbp_target *vt; |
575 |
|
|
576 |
3120 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
577 |
3120 |
vt = be->probe; |
578 |
3120 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
579 |
|
|
580 |
3120 |
if (!details) { |
581 |
2600 |
if (json) |
582 |
240 |
VSB_printf(vsb, "[%u, %u, \"%s\"]", |
583 |
120 |
vt->good, vt->window, |
584 |
120 |
vt->backend->sick ? "sick" : "healthy"); |
585 |
|
else |
586 |
4960 |
VSB_printf(vsb, "%u/%u\t%s", vt->good, vt->window, |
587 |
2480 |
vt->backend->sick ? "sick" : "healthy"); |
588 |
2600 |
return; |
589 |
|
} |
590 |
|
|
591 |
520 |
if (json) { |
592 |
80 |
VSB_cat(vsb, "{\n"); |
593 |
80 |
VSB_indent(vsb, 2); |
594 |
|
#define BITMAP(nn, cc, tt, bb) \ |
595 |
|
VSB_printf(vsb, "\"bits_%c\": %ju,\n", cc, vt->nn); |
596 |
|
#include "tbl/backend_poll.h" |
597 |
|
VSB_printf(vsb, "\"good\": %u,\n", vt->good); |
598 |
|
VSB_printf(vsb, "\"threshold\": %u,\n", vt->threshold); |
599 |
|
VSB_printf(vsb, "\"window\": %u", vt->window); |
600 |
|
VSB_indent(vsb, -2); |
601 |
|
VSB_cat(vsb, "\n"); |
602 |
|
VSB_cat(vsb, "},\n"); |
603 |
|
return; |
604 |
|
} |
605 |
|
|
606 |
880 |
VSB_printf(vsb, |
607 |
|
"\n Current states good: %2u threshold: %2u window: %2u\n", |
608 |
440 |
vt->good, vt->threshold, vt->window); |
609 |
880 |
VSB_printf(vsb, |
610 |
440 |
" Average response time of good probes: %.6f\n", vt->avg); |
611 |
440 |
VSB_cat(vsb, |
612 |
|
" Oldest ======================" |
613 |
|
"============================ Newest\n"); |
614 |
|
|
615 |
|
#define BITMAP(n, c, t, b) \ |
616 |
|
if ((vt->n != 0) || (b)) \ |
617 |
|
vbp_bitmap(vsb, (c), vt->n, (t)); |
618 |
|
#include "tbl/backend_poll.h" |
619 |
|
} |
620 |
|
|
621 |
|
/*-------------------------------------------------------------------- |
622 |
|
* Build request from probe spec |
623 |
|
*/ |
624 |
|
|
625 |
|
static void |
626 |
1480 |
vbp_build_req(struct vbp_target *vt, const struct vrt_backend_probe *vbp, |
627 |
|
const struct backend *be) |
628 |
|
{ |
629 |
|
struct vsb *vsb; |
630 |
|
|
631 |
1480 |
vsb = VSB_new_auto(); |
632 |
1480 |
AN(vsb); |
633 |
1480 |
VSB_clear(vsb); |
634 |
1480 |
if (vbp->request != NULL) { |
635 |
80 |
VSB_cat(vsb, vbp->request); |
636 |
80 |
} else { |
637 |
1400 |
AN(be->hosthdr); |
638 |
2800 |
VSB_printf(vsb, |
639 |
|
"GET %s HTTP/1.1\r\n" |
640 |
|
"Host: %s\r\n" |
641 |
|
"Connection: close\r\n" |
642 |
|
"\r\n", |
643 |
1400 |
vbp->url != NULL ? vbp->url : "/", |
644 |
1400 |
be->hosthdr); |
645 |
|
} |
646 |
1480 |
AZ(VSB_finish(vsb)); |
647 |
1480 |
vt->req = strdup(VSB_data(vsb)); |
648 |
1480 |
AN(vt->req); |
649 |
1480 |
vt->req_len = VSB_len(vsb); |
650 |
1480 |
VSB_destroy(&vsb); |
651 |
1480 |
} |
652 |
|
|
653 |
|
/*-------------------------------------------------------------------- |
654 |
|
* Sanitize and set defaults |
655 |
|
* XXX: we could make these defaults parameters |
656 |
|
*/ |
657 |
|
|
658 |
|
static void |
659 |
1480 |
vbp_set_defaults(struct vbp_target *vt, const struct vrt_backend_probe *vp) |
660 |
|
{ |
661 |
|
|
662 |
|
#define DN(x) do { vt->x = vp->x; } while (0) |
663 |
1480 |
VRT_BACKEND_PROBE_HANDLE(); |
664 |
|
#undef DN |
665 |
|
|
666 |
1480 |
if (vt->timeout == 0.0) |
667 |
1080 |
vt->timeout = 2.0; |
668 |
1480 |
if (vt->interval == 0.0) |
669 |
320 |
vt->interval = 5.0; |
670 |
1480 |
if (vt->window == 0) |
671 |
640 |
vt->window = 8; |
672 |
1480 |
if (vt->threshold == 0) |
673 |
640 |
vt->threshold = 3; |
674 |
1480 |
if (vt->exp_status == 0) |
675 |
1480 |
vt->exp_status = 200; |
676 |
|
|
677 |
1480 |
if (vt->initial == ~0U) |
678 |
880 |
vt->initial = vt->threshold - 1; |
679 |
|
|
680 |
1480 |
vt->initial = vmin(vt->initial, vt->threshold); |
681 |
1480 |
} |
682 |
|
|
683 |
|
/*-------------------------------------------------------------------- |
684 |
|
*/ |
685 |
|
|
686 |
|
void |
687 |
1760 |
VBP_Control(const struct backend *be, int enable) |
688 |
|
{ |
689 |
|
struct vbp_target *vt; |
690 |
|
|
691 |
1760 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
692 |
1760 |
vt = be->probe; |
693 |
1760 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
694 |
|
|
695 |
1760 |
vbp_reset(vt); |
696 |
1760 |
VBP_Update_Backend(vt); |
697 |
|
|
698 |
1760 |
Lck_Lock(&vbp_mtx); |
699 |
1760 |
if (enable) { |
700 |
1480 |
if (vt->state == vbp_state_cooling) { |
701 |
0 |
vt->state = vbp_state_running; |
702 |
1480 |
} else if (vt->state == vbp_state_cold) { |
703 |
1480 |
assert(vt->heap_idx == VBH_NOIDX); |
704 |
1480 |
vt->due = VTIM_real(); |
705 |
1480 |
vbp_heap_insert(vt); |
706 |
1480 |
vt->state = vbp_state_scheduled; |
707 |
1480 |
} else |
708 |
0 |
WRONG(vt->state->name); |
709 |
1480 |
} else { |
710 |
280 |
if (vt->state == vbp_state_running) { |
711 |
40 |
vt->state = vbp_state_cooling; |
712 |
280 |
} else if (vt->state == vbp_state_scheduled) { |
713 |
240 |
assert(vt->heap_idx != VBH_NOIDX); |
714 |
240 |
VBH_delete(vbp_heap, vt->heap_idx); |
715 |
240 |
vt->state = vbp_state_cold; |
716 |
240 |
} else |
717 |
0 |
WRONG(vt->state->name); |
718 |
|
} |
719 |
1760 |
Lck_Unlock(&vbp_mtx); |
720 |
1760 |
} |
721 |
|
|
722 |
|
/*-------------------------------------------------------------------- |
723 |
|
* Insert/Remove/Use called from cache_backend.c |
724 |
|
*/ |
725 |
|
|
726 |
|
void |
727 |
1480 |
VBP_Insert(struct backend *b, const struct vrt_backend_probe *vp, |
728 |
|
struct conn_pool *tp) |
729 |
|
{ |
730 |
|
struct vbp_target *vt; |
731 |
|
|
732 |
1480 |
CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC); |
733 |
1480 |
CHECK_OBJ_NOTNULL(vp, VRT_BACKEND_PROBE_MAGIC); |
734 |
|
|
735 |
1480 |
AZ(b->probe); |
736 |
|
|
737 |
1480 |
ALLOC_OBJ(vt, VBP_TARGET_MAGIC); |
738 |
1480 |
XXXAN(vt); |
739 |
|
|
740 |
1480 |
vt->state = vbp_state_cold; |
741 |
1480 |
vt->conn_pool = tp; |
742 |
1480 |
VCP_AddRef(vt->conn_pool); |
743 |
1480 |
vt->backend = b; |
744 |
1480 |
b->probe = vt; |
745 |
|
|
746 |
1480 |
vbp_set_defaults(vt, vp); |
747 |
1480 |
vbp_build_req(vt, vp, b); |
748 |
|
|
749 |
1480 |
vbp_reset(vt); |
750 |
1480 |
} |
751 |
|
|
752 |
|
void |
753 |
240 |
VBP_Remove(struct backend *be) |
754 |
|
{ |
755 |
|
struct vbp_target *vt; |
756 |
|
|
757 |
240 |
CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC); |
758 |
240 |
vt = be->probe; |
759 |
240 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
760 |
|
|
761 |
240 |
Lck_Lock(&vbp_mtx); |
762 |
240 |
be->sick = 1; |
763 |
240 |
be->probe = NULL; |
764 |
240 |
vt->backend = NULL; |
765 |
240 |
if (vt->state == vbp_state_cooling) { |
766 |
0 |
vt->state = vbp_state_deleted; |
767 |
0 |
vt = NULL; |
768 |
0 |
} else |
769 |
240 |
assert(vt->state == vbp_state_cold); |
770 |
240 |
Lck_Unlock(&vbp_mtx); |
771 |
240 |
if (vt != NULL) |
772 |
240 |
vbp_delete(vt); |
773 |
240 |
} |
774 |
|
|
775 |
|
/*-------------------------------------------------------------------*/ |
776 |
|
|
777 |
|
static int v_matchproto_(vbh_cmp_t) |
778 |
1090 |
vbp_cmp(void *priv, const void *a, const void *b) |
779 |
|
{ |
780 |
|
const struct vbp_target *aa, *bb; |
781 |
|
|
782 |
1090 |
AZ(priv); |
783 |
1090 |
CAST_OBJ_NOTNULL(aa, a, VBP_TARGET_MAGIC); |
784 |
1090 |
CAST_OBJ_NOTNULL(bb, b, VBP_TARGET_MAGIC); |
785 |
|
|
786 |
1090 |
return (aa->due < bb->due); |
787 |
|
} |
788 |
|
|
789 |
|
static void v_matchproto_(vbh_update_t) |
790 |
18827 |
vbp_update(void *priv, void *p, unsigned u) |
791 |
|
{ |
792 |
|
struct vbp_target *vt; |
793 |
|
|
794 |
18827 |
AZ(priv); |
795 |
18827 |
CAST_OBJ_NOTNULL(vt, p, VBP_TARGET_MAGIC); |
796 |
18827 |
vt->heap_idx = u; |
797 |
18827 |
} |
798 |
|
|
799 |
|
/*-------------------------------------------------------------------*/ |
800 |
|
|
801 |
|
void |
802 |
36676 |
VBP_Init(void) |
803 |
|
{ |
804 |
|
pthread_t thr; |
805 |
|
|
806 |
36676 |
Lck_New(&vbp_mtx, lck_probe); |
807 |
36676 |
vbp_heap = VBH_new(NULL, vbp_cmp, vbp_update); |
808 |
36676 |
AN(vbp_heap); |
809 |
36676 |
PTOK(pthread_cond_init(&vbp_cond, NULL)); |
810 |
36676 |
WRK_BgThread(&thr, "backend-probe-scheduler", vbp_scheduler, NULL); |
811 |
36676 |
} |