| | 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 |
15933 |
vbp_start_poke(struct vbp_target *vt) |
| 134 |
|
{ |
| 135 |
15933 |
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 |
15812 |
vbp_has_poked(struct vbp_target *vt) |
| 147 |
|
{ |
| 148 |
|
unsigned i, j; |
| 149 |
|
uint64_t u; |
| 150 |
|
|
| 151 |
15812 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
| 152 |
|
|
| 153 |
|
/* Calculate exponential average */ |
| 154 |
15812 |
if (vt->happy & 1) { |
| 155 |
13645 |
if (vt->rate < AVG_RATE) |
| 156 |
7204 |
vt->rate += 1.0; |
| 157 |
13645 |
vt->avg += (vt->last - vt->avg) / vt->rate; |
| 158 |
13645 |
} |
| 159 |
|
|
| 160 |
15812 |
u = vt->happy; |
| 161 |
129741 |
for (i = j = 0; i < vt->window; i++) { |
| 162 |
113929 |
if (u & 1) |
| 163 |
62850 |
j++; |
| 164 |
113929 |
u >>= 1; |
| 165 |
113929 |
} |
| 166 |
15812 |
vt->good = j; |
| 167 |
15812 |
} |
| 168 |
|
|
| 169 |
|
void |
| 170 |
11092 |
VBP_Update_Backend(struct vbp_target *vt) |
| 171 |
|
{ |
| 172 |
11092 |
unsigned i = 0, chg; |
| 173 |
|
char bits[10]; |
| 174 |
|
|
| 175 |
11092 |
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 |
11092 |
assert(i < sizeof bits); |
| 182 |
|
|
| 183 |
|
Lck_Lock(&vbp_mtx); |
| 184 |
11092 |
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 |
11092 |
if (i && chg && vt->backend->director != NULL) |
| 193 |
1962 |
VDI_Event(vt->backend->director, VDI_EVENT_SICK); |
| 194 |
|
|
| 195 |
11092 |
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 |
11092 |
if (chg) |
| 204 |
2842 |
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 |
7870 |
vbp_write(struct vbp_target *vt, int *sock, const void *buf, size_t len) |
| 236 |
|
{ |
| 237 |
|
int i; |
| 238 |
|
|
| 239 |
7870 |
i = write(*sock, buf, len); |
| 240 |
7870 |
VTCP_Assert(i); |
| 241 |
7870 |
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 |
7870 |
return (0); |
| 255 |
7870 |
} |
| 256 |
|
|
| 257 |
|
static int |
| 258 |
242 |
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 |
242 |
char vsabuf[vsa_suckaddr_len]; |
| 264 |
|
const struct suckaddr *sua; |
| 265 |
|
int proto; |
| 266 |
|
struct vsb vsb; |
| 267 |
|
|
| 268 |
242 |
sua = VSA_getsockname(*sock, vsabuf, sizeof vsabuf); |
| 269 |
242 |
AN(sua); |
| 270 |
242 |
AN(VSB_init(&vsb, buf, sizeof buf)); |
| 271 |
|
|
| 272 |
242 |
proto = VSA_Get_Proto(sua); |
| 273 |
242 |
if (proto == AF_INET || proto == AF_INET6) { |
| 274 |
122 |
VTCP_name(sua, addr, sizeof addr, port, sizeof port); |
| 275 |
122 |
VSB_printf(&vsb, "PROXY %s %s %s %s %s\r\n", |
| 276 |
122 |
proto == AF_INET ? "TCP4" : "TCP6", |
| 277 |
122 |
addr, addr, port, port); |
| 278 |
122 |
} else { |
| 279 |
120 |
VSB_cat(&vsb, "PROXY UNKNOWN\r\n"); |
| 280 |
|
} |
| 281 |
242 |
AZ(VSB_finish(&vsb)); |
| 282 |
|
|
| 283 |
242 |
VSB_fini(&vsb); |
| 284 |
242 |
return (vbp_write(vt, sock, buf, strlen(buf))); |
| 285 |
242 |
} |
| 286 |
|
|
| 287 |
|
static void |
| 288 |
7933 |
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 |
7933 |
struct pollfd pfda[1], *pfd = pfda; |
| 296 |
|
const struct suckaddr *sa; |
| 297 |
|
|
| 298 |
7933 |
t_start = t_now = VTIM_real(); |
| 299 |
7933 |
t_end = t_start + vt->timeout; |
| 300 |
|
|
| 301 |
7933 |
s = VCP_Open(vt->conn_pool, t_end - t_now, &sa, &err); |
| 302 |
7933 |
if (s < 0) { |
| 303 |
638 |
bprintf(vt->resp_buf, "Open error %d (%s)", err, VAS_errtxt(err)); |
| 304 |
638 |
Lck_Lock(&vbp_mtx); |
| 305 |
638 |
if (vt->backend) |
| 306 |
638 |
VBE_Connect_Error(vt->backend->vsc, err); |
| 307 |
638 |
Lck_Unlock(&vbp_mtx); |
| 308 |
638 |
return; |
| 309 |
|
} |
| 310 |
|
|
| 311 |
7295 |
i = VSA_Get_Proto(sa); |
| 312 |
7295 |
if (VSA_Compare(sa, bogo_ip) == 0) |
| 313 |
2577 |
vt->good_unix |= 1; |
| 314 |
4718 |
else if (i == AF_INET) |
| 315 |
4718 |
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 |
7295 |
t_now = VTIM_real(); |
| 322 |
7295 |
tmo = t_end - t_now; |
| 323 |
7295 |
if (tmo <= 0) { |
| 324 |
81 |
bprintf(vt->resp_buf, |
| 325 |
|
"Open timeout %.3fs exceeded by %.3fs", |
| 326 |
|
vt->timeout, -tmo); |
| 327 |
81 |
VTCP_close(&s); |
| 328 |
81 |
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 |
242 |
if (vbp_write_proxy_v1(vt, &s) != 0) |
| 348 |
0 |
return; |
| 349 |
7214 |
} else if (proxy_header == 2 && |
| 350 |
333 |
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 |
14547 |
while (1) { |
| 362 |
14547 |
pfd->events = POLLIN; |
| 363 |
14547 |
pfd->revents = 0; |
| 364 |
14547 |
t_now = VTIM_real(); |
| 365 |
14547 |
tmo = t_end - t_now; |
| 366 |
14547 |
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 |
14547 |
i = poll(pfd, 1, VTIM_poll_tmo(tmo)); |
| 374 |
14547 |
if (i <= 0) { |
| 375 |
809 |
if (!i) { |
| 376 |
809 |
if (!vt->exp_close) |
| 377 |
167 |
break; |
| 378 |
642 |
errno = ETIMEDOUT; |
| 379 |
642 |
} |
| 380 |
642 |
bprintf(vt->resp_buf, "Poll error %d (%s)", |
| 381 |
|
errno, VAS_errtxt(errno)); |
| 382 |
642 |
i = -1; |
| 383 |
642 |
break; |
| 384 |
|
} |
| 385 |
13738 |
if (rlen < sizeof vt->resp_buf) |
| 386 |
25016 |
i = read(s, vt->resp_buf + rlen, |
| 387 |
12508 |
sizeof vt->resp_buf - rlen); |
| 388 |
|
else |
| 389 |
1230 |
i = read(s, buf, sizeof buf); |
| 390 |
13738 |
VTCP_Assert(i); |
| 391 |
13738 |
if (i <= 0) { |
| 392 |
6405 |
if (i < 0) |
| 393 |
46 |
bprintf(vt->resp_buf, "Read error %d (%s)", |
| 394 |
|
errno, VAS_errtxt(errno)); |
| 395 |
6405 |
break; |
| 396 |
|
} |
| 397 |
7333 |
rlen += i; |
| 398 |
|
} |
| 399 |
|
|
| 400 |
7214 |
VTCP_close(&s); |
| 401 |
|
|
| 402 |
7214 |
if (i < 0) { |
| 403 |
|
/* errno reported above */ |
| 404 |
688 |
vt->err_recv |= 1; |
| 405 |
688 |
return; |
| 406 |
|
} |
| 407 |
|
|
| 408 |
6526 |
if (rlen == 0) { |
| 409 |
240 |
bprintf(vt->resp_buf, "%s", "Empty response"); |
| 410 |
240 |
return; |
| 411 |
|
} |
| 412 |
|
|
| 413 |
|
/* So we have a good receive ... */ |
| 414 |
6286 |
t_now = VTIM_real(); |
| 415 |
6286 |
vt->last = t_now - t_start; |
| 416 |
6286 |
vt->good_recv |= 1; |
| 417 |
|
|
| 418 |
|
/* Now find out if we like the response */ |
| 419 |
6286 |
vt->resp_buf[sizeof vt->resp_buf - 1] = '\0'; |
| 420 |
6286 |
p = strchr(vt->resp_buf, '\r'); |
| 421 |
6286 |
if (p != NULL) |
| 422 |
6285 |
*p = '\0'; |
| 423 |
6286 |
p = strchr(vt->resp_buf, '\n'); |
| 424 |
6286 |
if (p != NULL) |
| 425 |
0 |
*p = '\0'; |
| 426 |
|
|
| 427 |
6286 |
i = sscanf(vt->resp_buf, "HTTP/%*f %u ", &resp); |
| 428 |
|
|
| 429 |
6286 |
if (i == 1 && resp == vt->exp_status) |
| 430 |
5645 |
vt->happy |= 1; |
| 431 |
7933 |
} |
| 432 |
|
|
| 433 |
|
/*-------------------------------------------------------------------- |
| 434 |
|
*/ |
| 435 |
|
static void |
| 436 |
9292 |
vbp_heap_insert(struct vbp_target *vt) |
| 437 |
|
{ |
| 438 |
|
// Lck_AssertHeld(&vbp_mtx); |
| 439 |
9292 |
VBH_insert(vbp_heap, vt); |
| 440 |
9292 |
if (VBH_root(vbp_heap) == vt) |
| 441 |
8627 |
PTOK(pthread_cond_signal(&vbp_cond)); |
| 442 |
9292 |
} |
| 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 |
7852 |
vbp_task_complete(struct vbp_target *vt) |
| 453 |
|
{ |
| 454 |
7852 |
CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC); |
| 455 |
|
|
| 456 |
7852 |
Lck_AssertHeld(&vbp_mtx); |
| 457 |
|
|
| 458 |
7852 |
assert(vt->heap_idx == VBH_NOIDX); |
| 459 |
|
|
| 460 |
7852 |
if (vt->state == vbp_state_running) { |
| 461 |
7812 |
vt->state = vbp_state_scheduled; |
| 462 |
7812 |
vt->due = VTIM_real() + vt->interval; |
| 463 |
7812 |
vbp_heap_insert(vt); |
| 464 |
7812 |
vt = NULL; |
| 465 |
7852 |
} 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 |
7852 |
return (vt); |
| 472 |
|
} |
| 473 |
|
|
| 474 |
|
static void v_matchproto_(task_func_t) |
| 475 |
7852 |
vbp_task(struct worker *wrk, void *priv) |
| 476 |
|
{ |
| 477 |
|
struct vbp_target *vt; |
| 478 |
|
|
| 479 |
7852 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 480 |
7852 |
CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC); |
| 481 |
|
|
| 482 |
7852 |
AN(vt->req); |
| 483 |
7852 |
assert(vt->req_len > 0); |
| 484 |
|
|
| 485 |
7852 |
vbp_start_poke(vt); |
| 486 |
7852 |
vbp_poke(vt); |
| 487 |
7852 |
vbp_has_poked(vt); |
| 488 |
7852 |
VBP_Update_Backend(vt); |
| 489 |
|
|
| 490 |
7852 |
Lck_Lock(&vbp_mtx); |
| 491 |
7852 |
vt = vbp_task_complete(vt); |
| 492 |
7852 |
Lck_Unlock(&vbp_mtx); |
| 493 |
7852 |
if (vt == NULL) |
| 494 |
7852 |
return; |
| 495 |
0 |
vbp_delete(vt); |
| 496 |
7852 |
} |
| 497 |
|
|
| 498 |
|
/*-------------------------------------------------------------------- |
| 499 |
|
*/ |
| 500 |
|
|
| 501 |
|
static void * v_matchproto_(bgthread_t) |
| 502 |
38034 |
vbp_scheduler(struct worker *wrk, void *priv) |
| 503 |
|
{ |
| 504 |
|
vtim_real now, nxt; |
| 505 |
|
struct vbp_target *vt; |
| 506 |
|
int r; |
| 507 |
|
|
| 508 |
38034 |
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); |
| 509 |
38034 |
AZ(priv); |
| 510 |
38034 |
Lck_Lock(&vbp_mtx); |
| 511 |
92217 |
while (1) { |
| 512 |
100190 |
now = VTIM_real(); |
| 513 |
100190 |
vt = VBH_root(vbp_heap); |
| 514 |
100190 |
if (vt == NULL) { |
| 515 |
46333 |
nxt = 8.192 + now; |
| 516 |
46333 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
| 517 |
100190 |
} else if (vt->due > now) { |
| 518 |
7850 |
nxt = vt->due; |
| 519 |
7850 |
(void)Lck_CondWaitUntil(&vbp_cond, &vbp_mtx, nxt); |
| 520 |
7850 |
} else { |
| 521 |
46007 |
assert(vt->state == vbp_state_scheduled); |
| 522 |
7973 |
VBH_delete(vbp_heap, vt->heap_idx); |
| 523 |
7973 |
vt->state = vbp_state_running; |
| 524 |
7973 |
vt->task->func = vbp_task; |
| 525 |
7973 |
vt->task->priv = vt; |
| 526 |
7973 |
Lck_Unlock(&vbp_mtx); |
| 527 |
|
|
| 528 |
7973 |
r = Pool_Task_Any(vt->task, TASK_QUEUE_REQ); |
| 529 |
|
|
| 530 |
7973 |
Lck_Lock(&vbp_mtx); |
| 531 |
7973 |
if (r == 0) |
| 532 |
7973 |
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 |
9972 |
VSB_putc(vsb, c); |
| 562 |
|
else |
| 563 |
89868 |
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 |
1083 |
vbp_cmp(void *priv, const void *a, const void *b) |
| 779 |
|
{ |
| 780 |
|
const struct vbp_target *aa, *bb; |
| 781 |
|
|
| 782 |
1083 |
AZ(priv); |
| 783 |
1083 |
CAST_OBJ_NOTNULL(aa, a, VBP_TARGET_MAGIC); |
| 784 |
1083 |
CAST_OBJ_NOTNULL(bb, b, VBP_TARGET_MAGIC); |
| 785 |
|
|
| 786 |
1083 |
return (aa->due < bb->due); |
| 787 |
|
} |
| 788 |
|
|
| 789 |
|
static void v_matchproto_(vbh_update_t) |
| 790 |
19101 |
vbp_update(void *priv, void *p, unsigned u) |
| 791 |
|
{ |
| 792 |
|
struct vbp_target *vt; |
| 793 |
|
|
| 794 |
19101 |
AZ(priv); |
| 795 |
19101 |
CAST_OBJ_NOTNULL(vt, p, VBP_TARGET_MAGIC); |
| 796 |
19101 |
vt->heap_idx = u; |
| 797 |
19101 |
} |
| 798 |
|
|
| 799 |
|
/*-------------------------------------------------------------------*/ |
| 800 |
|
|
| 801 |
|
void |
| 802 |
38034 |
VBP_Init(void) |
| 803 |
|
{ |
| 804 |
|
pthread_t thr; |
| 805 |
|
|
| 806 |
38034 |
Lck_New(&vbp_mtx, lck_probe); |
| 807 |
38034 |
vbp_heap = VBH_new(NULL, vbp_cmp, vbp_update); |
| 808 |
38034 |
AN(vbp_heap); |
| 809 |
38034 |
PTOK(pthread_cond_init(&vbp_cond, NULL)); |
| 810 |
38034 |
WRK_BgThread(&thr, "backend-probe-scheduler", vbp_scheduler, NULL); |
| 811 |
38034 |
} |