varnish-cache/bin/varnishd/cache/cache_backend_probe.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2011 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * Poll backends for collection of health statistics
30
 *
31
 * We co-opt threads from the worker pool for probing the backends,
32
 * but we want to avoid a potentially messy cleanup operation when we
33
 * retire the backend, so the thread owns the health information, which
34
 * the backend references, rather than the other way around.
35
 *
36
 */
37
38
#include "config.h"
39
40
#include <poll.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
44
#include "cache_varnishd.h"
45
46
#include "binary_heap.h"
47
#include "vcli_serve.h"
48
#include "vsa.h"
49
#include "vtcp.h"
50
#include "vtim.h"
51
#include "waiter/waiter.h"
52
53
#include "cache_director.h"
54
#include "cache_backend.h"
55
#include "cache_tcp_pool.h"
56
57
/* Default averaging rate, we want something pretty responsive */
58
#define AVG_RATE                        4
59
60
struct vbp_target {
61
        unsigned                        magic;
62
#define VBP_TARGET_MAGIC                0x6b7cb656
63
64
        VRT_BACKEND_PROBE_FIELDS()
65
66
        struct backend                  *backend;
67
        struct tcp_pool                 *tcp_pool;
68
69
        char                            *req;
70
        int                             req_len;
71
72
        char                            resp_buf[128];
73
        unsigned                        good;
74
75
        /* Collected statistics */
76
#define BITMAP(n, c, t, b)      uint64_t        n;
77
#include "tbl/backend_poll.h"
78
79
        double                          last;
80
        double                          avg;
81
        double                          rate;
82
83
        double                          due;
84
        int                             running;
85
        int                             heap_idx;
86
        struct pool_task                task;
87
};
88
89
static struct lock                      vbp_mtx;
90
static pthread_cond_t                   vbp_cond;
91
static struct binheap                   *vbp_heap;
92
93
static const unsigned char vbp_proxy_local[] = {
94
        0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51,
95
        0x55, 0x49, 0x54, 0x0a, 0x20, 0x00, 0x00, 0x00,
96
};
97
98
/*--------------------------------------------------------------------*/
99
100
static void
101 3
vbp_delete(struct vbp_target *vt)
102
{
103
#define DN(x)   /**/
104
        VRT_BACKEND_PROBE_HANDLE();
105
#undef DN
106 3
        VTP_Rel(&vt->tcp_pool);
107 3
        free(vt->req);
108 3
        FREE_OBJ(vt);
109 3
}
110
111
112
/*--------------------------------------------------------------------
113
 * Record pokings...
114
 */
115
116
static void
117 212
vbp_start_poke(struct vbp_target *vt)
118
{
119 212
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
120
121
#define BITMAP(n, c, t, b) \
122
        vt->n <<= 1;
123
#include "tbl/backend_poll.h"
124
125 212
        vt->last = 0;
126 212
        vt->resp_buf[0] = '\0';
127 212
}
128
129
static void
130 212
vbp_has_poked(struct vbp_target *vt)
131
{
132
        unsigned i, j;
133
        uint64_t u;
134
135 212
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
136
137
        /* Calculate exponential average */
138 212
        if (vt->happy & 1) {
139 132
                if (vt->rate < AVG_RATE)
140 58
                        vt->rate += 1.0;
141 132
                vt->avg += (vt->last - vt->avg) / vt->rate;
142
        }
143
144 212
        u = vt->happy;
145 1744
        for (i = j = 0; i < vt->window; i++) {
146 1532
                if (u & 1)
147 706
                        j++;
148 1532
                u >>= 1;
149
        }
150 212
        vt->good = j;
151 212
}
152
153
static void
154 191
vbp_update_backend(struct vbp_target *vt)
155
{
156
        unsigned i;
157
        char bits[10];
158
        const char *logmsg;
159
160 191
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
161
162 191
        Lck_Lock(&vbp_mtx);
163 191
        if (vt->backend != NULL) {
164 190
                i = 0;
165
#define BITMAP(n, c, t, b) \
166
                bits[i++] = (vt->n & 1) ? c : '-';
167
#include "tbl/backend_poll.h"
168 190
                bits[i] = '\0';
169 190
                assert(i < sizeof bits);
170
171 190
                if (vt->good >= vt->threshold) {
172 78
                        if (vt->backend->director->health)
173 70
                                logmsg = "Still healthy";
174
                        else {
175 8
                                logmsg = "Back healthy";
176 16
                                vt->backend->director->health_changed =
177 8
                                     VTIM_real();
178
                        }
179 78
                        vt->backend->director->health = 1;
180
                } else {
181 112
                        if (vt->backend->director->health) {
182 18
                                logmsg = "Went sick";
183 36
                                vt->backend->director->health_changed =
184 18
                                     VTIM_real();
185
                        } else
186 94
                                logmsg = "Still sick";
187 112
                        vt->backend->director->health = 0;
188
                }
189 380
                VSL(SLT_Backend_health, 0, "%s %s %s %u %u %u %.6f %.6f %s",
190 190
                    vt->backend->director->display_name, logmsg, bits,
191
                    vt->good, vt->threshold, vt->window,
192 190
                    vt->last, vt->avg, vt->resp_buf);
193 190
                VBE_SetHappy(vt->backend, vt->happy);
194
        }
195 191
        Lck_Unlock(&vbp_mtx);
196 191
}
197
198
static void
199 34
vbp_reset(struct vbp_target *vt)
200
{
201
        unsigned u;
202
203 34
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
204 34
        vt->avg = 0.0;
205 34
        vt->rate = 0.0;
206
#define BITMAP(n, c, t, b) \
207
        vt->n = 0;
208
#include "tbl/backend_poll.h"
209
210 89
        for (u = 0; u < vt->initial; u++) {
211 55
                vbp_start_poke(vt);
212 55
                vt->happy |= 1;
213 55
                vbp_has_poked(vt);
214
        }
215 34
}
216
217
/*--------------------------------------------------------------------
218
 * Poke one backend, once, but possibly at both IPv4 and IPv6 addresses.
219
 *
220
 * We do deliberately not use the stuff in cache_backend.c, because we
221
 * want to measure the backends response without local distractions.
222
 */
223
224
static int
225 96
vbp_write(struct vbp_target *vt, int *sock, const void *buf, size_t len)
226
{
227
        int i;
228
229 96
        i = write(*sock, buf, len);
230 96
        if (i != len) {
231 0
                if (i < 0)
232 0
                        vt->err_xmit |= 1;
233 0
                VTCP_close(sock);
234 0
                return (-1);
235
        }
236 96
        return (0);
237
}
238
239
static int
240 4
vbp_write_proxy_v1(struct vbp_target *vt, int *sock)
241
{
242
        char buf[105]; /* maximum size for a TCP6 PROXY line with null char */
243
        char addr[VTCP_ADDRBUFSIZE];
244
        char port[VTCP_PORTBUFSIZE];
245
        struct sockaddr_storage ss;
246
        struct vsb vsb;
247
        socklen_t l;
248
249 4
        VTCP_myname(*sock, addr, sizeof addr, port, sizeof port);
250 4
        AN(VSB_new(&vsb, buf, sizeof buf, VSB_FIXEDLEN));
251 4
        AZ(VSB_cat(&vsb, "PROXY"));
252
253 4
        l = sizeof ss;
254 4
        AZ(getsockname(*sock, (void *)&ss, &l));
255 4
        if (ss.ss_family == AF_INET6)
256 0
                VSB_printf(&vsb, " TCP6 ");
257 4
        else if (ss.ss_family == AF_INET)
258 4
                VSB_printf(&vsb, " TCP4 ");
259
        else
260 0
                WRONG("Unknown family");
261 4
        VSB_printf(&vsb, "%s %s %s %s\r\n", addr, addr, port, port);
262 4
        AZ(VSB_finish(&vsb));
263
264 4
        return (vbp_write(vt, sock, VSB_data(&vsb), VSB_len(&vsb)));
265
}
266
267
static void
268 157
vbp_poke(struct vbp_target *vt)
269
{
270
        int s, tmo, i, proxy_header;
271
        double t_start, t_now, t_end;
272
        unsigned rlen, resp;
273
        char buf[8192], *p;
274 157
        struct pollfd pfda[1], *pfd = pfda;
275
        const struct suckaddr *sa;
276
277 157
        t_start = t_now = VTIM_real();
278 157
        t_end = t_start + vt->timeout;
279
280 157
        s = VTP_Open(vt->tcp_pool, t_end - t_now, &sa);
281 157
        if (s < 0) {
282
                /* Got no connection: failed */
283 143
                return;
284
        }
285
286 88
        i = VSA_Get_Proto(sa);
287 88
        if (i == AF_INET)
288 88
                vt->good_ipv4 |= 1;
289 0
        else if (i == AF_INET6)
290 0
                vt->good_ipv6 |= 1;
291
        else
292 0
                WRONG("Wrong probe protocol family");
293
294 88
        t_now = VTIM_real();
295 88
        tmo = (int)round((t_end - t_now) * 1e3);
296 88
        if (tmo <= 0) {
297
                /* Spent too long time getting it */
298 0
                VTCP_close(&s);
299 0
                return;
300
        }
301
302 88
        Lck_Lock(&vbp_mtx);
303 88
        if (vt->backend != NULL)
304 88
                proxy_header = vt->backend->proxy_header;
305
        else
306 0
                proxy_header = -1;
307 88
        Lck_Unlock(&vbp_mtx);
308
309 88
        if (proxy_header < 0)
310 0
                return;
311
312
        /* Send the PROXY header */
313 88
        assert(proxy_header <= 2);
314 88
        if (proxy_header == 1) {
315 4
                if (vbp_write_proxy_v1(vt, &s) != 0)
316 0
                        return;
317 88
        } else if (proxy_header == 2 &&
318 4
            vbp_write(vt, &s, vbp_proxy_local, sizeof vbp_proxy_local) != 0)
319 0
                return;
320
321
        /* Send the request */
322 88
        if (vbp_write(vt, &s, vt->req, vt->req_len) != 0)
323 0
                return;
324 88
        vt->good_xmit |= 1;
325
326 88
        pfd->fd = s;
327 88
        rlen = 0;
328
        while (1) {
329 179
                pfd->events = POLLIN;
330 179
                pfd->revents = 0;
331 179
                tmo = (int)round((t_end - t_now) * 1e3);
332 179
                if (tmo > 0)
333 179
                        i = poll(pfd, 1, tmo);
334 178
                if (i == 0 || tmo <= 0) {
335 0
                        if (i == 0)
336 0
                                vt->err_recv |= 1;
337 0
                        VTCP_close(&s);
338 0
                        return;
339
                }
340 178
                if (rlen < sizeof vt->resp_buf)
341 162
                        i = read(s, vt->resp_buf + rlen,
342
                            sizeof vt->resp_buf - rlen);
343
                else
344 16
                        i = read(s, buf, sizeof buf);
345 179
                if (i <= 0)
346 88
                        break;
347 91
                rlen += i;
348 91
        }
349
350 88
        VTCP_close(&s);
351
352 88
        if (i < 0) {
353 1
                vt->err_recv |= 1;
354 1
                return;
355
        }
356
357 87
        if (rlen == 0)
358 4
                return;
359
360
        /* So we have a good receive ... */
361 83
        t_now = VTIM_real();
362 83
        vt->last = t_now - t_start;
363 83
        vt->good_recv |= 1;
364
365
        /* Now find out if we like the response */
366 83
        vt->resp_buf[sizeof vt->resp_buf - 1] = '\0';
367 83
        p = strchr(vt->resp_buf, '\r');
368 83
        if (p != NULL)
369 83
                *p = '\0';
370 83
        p = strchr(vt->resp_buf, '\n');
371 83
        if (p != NULL)
372 0
                *p = '\0';
373
374 83
        i = sscanf(vt->resp_buf, "HTTP/%*f %u ", &resp);
375
376 83
        if (i == 1 && resp == vt->exp_status)
377 76
                vt->happy |= 1;
378
}
379
380
/*--------------------------------------------------------------------
381
 */
382
383
static void v_matchproto_(task_func_t)
384 157
vbp_task(struct worker *wrk, void *priv)
385
{
386
        struct vbp_target *vt;
387
388 157
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
389 157
        CAST_OBJ_NOTNULL(vt, priv, VBP_TARGET_MAGIC);
390
391 157
        AN(vt->running);
392 157
        AN(vt->req);
393 157
        assert(vt->req_len > 0);
394
395 157
        vbp_start_poke(vt);
396 157
        vbp_poke(vt);
397 157
        vbp_has_poked(vt);
398 157
        vbp_update_backend(vt);
399
400 157
        Lck_Lock(&vbp_mtx);
401 157
        if (vt->running < 0) {
402 1
                assert(vt->heap_idx == BINHEAP_NOIDX);
403 1
                vbp_delete(vt);
404
        } else {
405 156
                vt->running = 0;
406 156
                if (vt->heap_idx != BINHEAP_NOIDX) {
407 156
                        vt->due = VTIM_real() + vt->interval;
408 156
                        binheap_delete(vbp_heap, vt->heap_idx);
409 156
                        binheap_insert(vbp_heap, vt);
410
                }
411
        }
412 157
        Lck_Unlock(&vbp_mtx);
413 157
}
414
415
/*--------------------------------------------------------------------
416
 */
417
418
static void * v_matchproto_()
419 614
vbp_thread(struct worker *wrk, void *priv)
420
{
421
        double now, nxt;
422
        struct vbp_target *vt;
423
424 614
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
425 614
        AZ(priv);
426 614
        Lck_Lock(&vbp_mtx);
427
        while (1) {
428 968
                now = VTIM_real();
429 968
                vt = binheap_root(vbp_heap);
430 968
                if (vt == NULL) {
431 651
                        nxt = 8.192 + now;
432 651
                        (void)Lck_CondWait(&vbp_cond, &vbp_mtx, nxt);
433 317
                } else if (vt->due > now) {
434 158
                        nxt = vt->due;
435 158
                        vt = NULL;
436 158
                        (void)Lck_CondWait(&vbp_cond, &vbp_mtx, nxt);
437
                } else {
438 159
                        binheap_delete(vbp_heap, vt->heap_idx);
439 159
                        vt->due = now + vt->interval;
440 159
                        if (!vt->running) {
441 157
                                vt->running = 1;
442 157
                                vt->task.func = vbp_task;
443 157
                                vt->task.priv = vt;
444 157
                                if (Pool_Task_Any(&vt->task, TASK_QUEUE_REQ))
445 0
                                        vt->running = 0;
446
                        }
447 159
                        binheap_insert(vbp_heap, vt);
448
                }
449 354
        }
450
        NEEDLESS(Lck_Unlock(&vbp_mtx));
451
        NEEDLESS(return NULL);
452
}
453
454
455
/*--------------------------------------------------------------------
456
 * Cli functions
457
 */
458
459
static void
460 16
vbp_bitmap(struct cli *cli, char c, uint64_t map, const char *lbl)
461
{
462
        int i;
463 16
        uint64_t u = (1ULL << 63);
464
465 16
        VCLI_Out(cli, "  ");
466 1040
        for (i = 0; i < 64; i++) {
467 1024
                if (map & u)
468 84
                        VCLI_Out(cli, "%c", c);
469
                else
470 940
                        VCLI_Out(cli, "-");
471 1024
                map <<= 1;
472
        }
473 16
        VCLI_Out(cli, " %s\n", lbl);
474 16
}
475
476
/*lint -e{506} constant value boolean */
477
/*lint -e{774} constant value boolean */
478
static void
479 5
vbp_health_one(struct cli *cli, const struct vbp_target *vt)
480
{
481
482 5
        VCLI_Out(cli,
483
            "  Current states  good: %2u threshold: %2u window: %2u\n",
484
            vt->good, vt->threshold, vt->window);
485 5
        VCLI_Out(cli,
486
            "  Average response time of good probes: %.6f\n", vt->avg);
487 5
        VCLI_Out(cli,
488
            "  Oldest ======================"
489
            "============================ Newest\n");
490
491
#define BITMAP(n, c, t, b)                                      \
492
                if ((vt->n != 0) || (b))                        \
493
                        vbp_bitmap(cli, (c), vt->n, (t));
494
#include "tbl/backend_poll.h"
495 5
}
496
497
void
498 23
VBP_Status(struct cli *cli, const struct backend *be, int details)
499
{
500
        struct vbp_target *vt;
501
502 23
        CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
503 23
        vt = be->probe;
504 23
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
505 23
        VCLI_Out(cli, "%d/%d", vt->good, vt->window);
506 23
        if (details) {
507 5
                VCLI_Out(cli, "\n");
508 5
                vbp_health_one(cli, vt);
509
        }
510 23
}
511
512
/*--------------------------------------------------------------------
513
 * Build request from probe spec
514
 */
515
516
static void
517 14
vbp_build_req(struct vbp_target *vt, const struct vrt_backend_probe *vbp,
518
    const struct backend *be)
519
{
520
        struct vsb *vsb;
521
522 14
        vsb = VSB_new_auto();
523 14
        AN(vsb);
524 14
        VSB_clear(vsb);
525 14
        if (vbp->request != NULL) {
526 0
                VSB_cat(vsb, vbp->request);
527
        } else {
528 14
                VSB_printf(vsb, "GET %s HTTP/1.1\r\n",
529 14
                    vbp->url != NULL ?  vbp->url : "/");
530 14
                if (be->hosthdr != NULL)
531 14
                        VSB_printf(vsb, "Host: %s\r\n", be->hosthdr);
532 14
                VSB_printf(vsb, "Connection: close\r\n");
533 14
                VSB_printf(vsb, "\r\n");
534
        }
535 14
        AZ(VSB_finish(vsb));
536 14
        vt->req = strdup(VSB_data(vsb));
537 14
        AN(vt->req);
538 14
        vt->req_len = VSB_len(vsb);
539 14
        VSB_destroy(&vsb);
540 14
}
541
542
/*--------------------------------------------------------------------
543
 * Sanitize and set defaults
544
 * XXX: we could make these defaults parameters
545
 */
546
547
static void
548 14
vbp_set_defaults(struct vbp_target *vt, const struct vrt_backend_probe *vp)
549
{
550
551
#define DN(x)   do { vt->x = vp->x; } while (0)
552 14
        VRT_BACKEND_PROBE_HANDLE();
553
#undef DN
554
555 14
        if (vt->timeout == 0.0)
556 11
                vt->timeout = 2.0;
557 14
        if (vt->interval == 0.0)
558 3
                vt->interval = 5.0;
559 14
        if (vt->window == 0)
560 7
                vt->window = 8;
561 14
        if (vt->threshold == 0)
562 7
                vt->threshold = 3;
563 14
        if (vt->exp_status == 0)
564 14
                vt->exp_status = 200;
565
566 14
        if (vt->initial == ~0U)
567 4
                vt->initial = vt->threshold - 1;
568
569 14
        if (vt->initial > vt->threshold)
570 0
                vt->initial = vt->threshold;
571 14
}
572
573
/*--------------------------------------------------------------------
574
 */
575
576
void
577 20
VBP_Control(const struct backend *be, int enable)
578
{
579
        struct vbp_target *vt;
580
581 20
        CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
582 20
        vt = be->probe;
583 20
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
584
585 20
        vbp_reset(vt);
586 20
        vbp_update_backend(vt);
587
588 20
        Lck_Lock(&vbp_mtx);
589 20
        if (enable) {
590 15
                assert(vt->heap_idx == BINHEAP_NOIDX);
591 15
                vt->due = VTIM_real();
592 15
                binheap_insert(vbp_heap, vt);
593 15
                AZ(pthread_cond_signal(&vbp_cond));
594
        } else {
595 5
                assert(vt->heap_idx != BINHEAP_NOIDX);
596 5
                binheap_delete(vbp_heap, vt->heap_idx);
597
        }
598 20
        Lck_Unlock(&vbp_mtx);
599 20
}
600
601
/*--------------------------------------------------------------------
602
 * Insert/Remove/Use called from cache_backend.c
603
 */
604
605
void
606 14
VBP_Insert(struct backend *b, const struct vrt_backend_probe *vp,
607
    struct tcp_pool *tp)
608
{
609
        struct vbp_target *vt;
610
611 14
        CHECK_OBJ_NOTNULL(b, BACKEND_MAGIC);
612 14
        CHECK_OBJ_NOTNULL(vp, VRT_BACKEND_PROBE_MAGIC);
613
614 14
        AZ(b->probe);
615
616 14
        ALLOC_OBJ(vt, VBP_TARGET_MAGIC);
617 14
        XXXAN(vt);
618
619 14
        vt->tcp_pool = tp;
620 14
        vt->backend = b;
621 14
        b->probe = vt;
622
623 14
        vbp_set_defaults(vt, vp);
624 14
        vbp_build_req(vt, vp, b);
625
626 14
        vbp_reset(vt);
627 14
        vbp_update_backend(vt);
628 14
}
629
630
void
631 3
VBP_Remove(struct backend *be)
632
{
633
        struct vbp_target *vt;
634
635 3
        CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
636 3
        vt = be->probe;
637 3
        CHECK_OBJ_NOTNULL(vt, VBP_TARGET_MAGIC);
638
639 3
        Lck_Lock(&vbp_mtx);
640 3
        be->director->health = 1;
641 3
        be->probe = NULL;
642 3
        vt->backend = NULL;
643 3
        if (vt->running) {
644 1
                vt->running = -1;
645 1
                vt = NULL;
646
        }
647 3
        Lck_Unlock(&vbp_mtx);
648 3
        if (vt != NULL) {
649 2
                assert(vt->heap_idx == BINHEAP_NOIDX);
650 2
                vbp_delete(vt);
651
        }
652 3
}
653
654
/*-------------------------------------------------------------------*/
655
656
static int v_matchproto_(binheap_cmp_t)
657 54
vbp_cmp(void *priv, const void *a, const void *b)
658
{
659
        const struct vbp_target *aa, *bb;
660
661 54
        AZ(priv);
662 54
        CAST_OBJ_NOTNULL(aa, a, VBP_TARGET_MAGIC);
663 54
        CAST_OBJ_NOTNULL(bb, b, VBP_TARGET_MAGIC);
664
665 54
        if (aa->running && !bb->running)
666 20
                return (0);
667
668 34
        return (aa->due < bb->due);
669
}
670
671
static void v_matchproto_(binheap_update_t)
672 692
vbp_update(void *priv, void *p, unsigned u)
673
{
674
        struct vbp_target *vt;
675
676 692
        AZ(priv);
677 692
        CAST_OBJ_NOTNULL(vt, p, VBP_TARGET_MAGIC);
678 692
        vt->heap_idx = u;
679 692
}
680
681
/*-------------------------------------------------------------------*/
682
683
void
684 614
VBP_Init(void)
685
{
686
        pthread_t thr;
687
688 614
        Lck_New(&vbp_mtx, lck_backend);
689 614
        vbp_heap = binheap_new(NULL, vbp_cmp, vbp_update);
690 614
        AN(vbp_heap);
691 614
        AZ(pthread_cond_init(&vbp_cond, NULL));
692 614
        WRK_BgThread(&thr, "backend-poller", vbp_thread, NULL);
693 614
}