varnish-cache/bin/varnishd/cache/cache_backend.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 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
 * The director implementation for VCL backends.
30
 *
31
 */
32
33
#include "config.h"
34
35
#include <stdlib.h>
36
37
#include "cache_varnishd.h"
38
39
#include "vtcp.h"
40
#include "vtim.h"
41
#include "waiter/waiter.h"
42
43
#include "cache_director.h"
44
#include "cache_backend.h"
45
#include "cache_tcp_pool.h"
46
#include "cache_transport.h"
47
#include "http1/cache_http1.h"
48
49
#include "VSC_vbe.h"
50
51
/*--------------------------------------------------------------------*/
52
53
static const char * const vbe_proto_ident = "HTTP Backend";
54
55
static VTAILQ_HEAD(, backend) backends = VTAILQ_HEAD_INITIALIZER(backends);
56
static VTAILQ_HEAD(, backend) cool_backends =
57
    VTAILQ_HEAD_INITIALIZER(cool_backends);
58
static struct lock backends_mtx;
59
60
/*--------------------------------------------------------------------*/
61
62
#define FIND_TMO(tmx, dst, bo, be)                                      \
63
        do {                                                            \
64
                CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);                   \
65
                dst = bo->tmx;                                          \
66
                if (dst == 0.0)                                         \
67
                        dst = be->tmx;                                  \
68
                if (dst == 0.0)                                         \
69
                        dst = cache_param->tmx;                         \
70
        } while (0)
71
72
/*--------------------------------------------------------------------
73
 * Get a connection to the backend
74
 */
75
76
static struct vtp *
77 1359
vbe_dir_getfd(struct worker *wrk, struct backend *bp, struct busyobj *bo,
78
    unsigned force_fresh)
79
{
80
        struct vtp *vtp;
81
        double tmod;
82
        char abuf1[VTCP_ADDRBUFSIZE], abuf2[VTCP_ADDRBUFSIZE];
83
        char pbuf1[VTCP_PORTBUFSIZE], pbuf2[VTCP_PORTBUFSIZE];
84
85 1359
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
86 1359
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
87 1359
        CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
88 1359
        AN(bp->vsc);
89
90 1359
        if (!VDI_Healthy(bp->director, NULL)) {
91 4
                VSLb(bo->vsl, SLT_FetchError,
92
                     "backend %s: unhealthy", bp->director->display_name);
93
                // XXX: per backend stats ?
94 4
                VSC_C_main->backend_unhealthy++;
95 4
                return (NULL);
96
        }
97
98 1355
        if (bp->max_connections > 0 && bp->n_conn >= bp->max_connections) {
99 1
                VSLb(bo->vsl, SLT_FetchError,
100
                     "backend %s: busy", bp->director->display_name);
101
                // XXX: per backend stats ?
102 1
                VSC_C_main->backend_busy++;
103 1
                return (NULL);
104
        }
105
106 1354
        AZ(bo->htc);
107 1354
        bo->htc = WS_Alloc(bo->ws, sizeof *bo->htc);
108 1354
        if (bo->htc == NULL) {
109 15
                VSLb(bo->vsl, SLT_FetchError, "out of workspace");
110
                /* XXX: counter ? */
111 15
                return (NULL);
112
        }
113 1339
        bo->htc->doclose = SC_NULL;
114
115 1339
        FIND_TMO(connect_timeout, tmod, bo, bp);
116 1339
        vtp = VTP_Get(bp->tcp_pool, tmod, wrk, force_fresh);
117 1339
        if (vtp == NULL) {
118 14
                VSLb(bo->vsl, SLT_FetchError,
119
                     "backend %s: fail", bp->director->display_name);
120
                // XXX: Per backend stats ?
121 14
                VSC_C_main->backend_fail++;
122 14
                bo->htc = NULL;
123 14
                return (NULL);
124
        }
125
126 1325
        assert(vtp->fd >= 0);
127 1325
        AN(vtp->addr);
128
129 1325
        Lck_Lock(&bp->mtx);
130 1325
        bp->n_conn++;
131 1325
        bp->vsc->conn++;
132 1325
        bp->vsc->req++;
133 1325
        Lck_Unlock(&bp->mtx);
134
135 1325
        if (bp->proxy_header != 0)
136 4
                VPX_Send_Proxy(vtp->fd, bp->proxy_header, bo->sp);
137
138 1325
        VTCP_myname(vtp->fd, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
139 1325
        VTCP_hisname(vtp->fd, abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
140 1325
        VSLb(bo->vsl, SLT_BackendOpen, "%d %s %s %s %s %s",
141
            vtp->fd, bp->director->display_name, abuf2, pbuf2, abuf1, pbuf1);
142
143 1325
        INIT_OBJ(bo->htc, HTTP_CONN_MAGIC);
144 1325
        bo->htc->priv = vtp;
145 1325
        bo->htc->rfd = &vtp->fd;
146 1325
        FIND_TMO(first_byte_timeout,
147
            bo->htc->first_byte_timeout, bo, bp);
148 1325
        FIND_TMO(between_bytes_timeout,
149
            bo->htc->between_bytes_timeout, bo, bp);
150 1325
        return (vtp);
151
}
152
153
static unsigned v_matchproto_(vdi_healthy_f)
154 247
vbe_dir_healthy(const struct director *d, const struct busyobj *bo,
155
    double *changed)
156
{
157
        struct backend *be;
158
159 247
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
160 247
        CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC);
161 247
        CAST_OBJ_NOTNULL(be, d->priv, BACKEND_MAGIC);
162 247
        return (VDI_Healthy(be->director, changed));
163
}
164
165
static void v_matchproto_(vdi_finish_f)
166 1324
vbe_dir_finish(const struct director *d, struct worker *wrk,
167
    struct busyobj *bo)
168
{
169
        struct backend *bp;
170
        struct vtp *vtp;
171
172 1324
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
173 1324
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
174 1324
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
175 1324
        CAST_OBJ_NOTNULL(bp, d->priv, BACKEND_MAGIC);
176
177 1324
        CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
178 1324
        CAST_OBJ_NOTNULL(vtp, bo->htc->priv, VTP_MAGIC);
179 1324
        bo->htc->priv = NULL;
180 1324
        if (vtp->state != VTP_STATE_USED)
181 1
                assert(bo->htc->doclose == SC_TX_PIPE ||
182
                        bo->htc->doclose == SC_RX_TIMEOUT);
183 1324
        if (bo->htc->doclose != SC_NULL || bp->proxy_header != 0) {
184 171
                VSLb(bo->vsl, SLT_BackendClose, "%d %s", vtp->fd,
185
                    bp->director->display_name);
186 169
                VTP_Close(&vtp);
187 169
                AZ(vtp);
188 169
                Lck_Lock(&bp->mtx);
189
        } else {
190 1153
                assert (vtp->state == VTP_STATE_USED);
191 1153
                VSLb(bo->vsl, SLT_BackendReuse, "%d %s", vtp->fd,
192
                    bp->director->display_name);
193 1153
                Lck_Lock(&bp->mtx);
194 1155
                VSC_C_main->backend_recycle++;
195 1155
                VTP_Recycle(wrk, &vtp);
196
        }
197 1324
        assert(bp->n_conn > 0);
198 1324
        bp->n_conn--;
199 1324
        bp->vsc->conn--;
200
#define ACCT(foo)       bp->vsc->foo += bo->acct.foo;
201
#include "tbl/acct_fields_bereq.h"
202 1324
        Lck_Unlock(&bp->mtx);
203 1324
        bo->htc = NULL;
204 1324
}
205
206
static int v_matchproto_(vdi_gethdrs_f)
207 1345
vbe_dir_gethdrs(const struct director *d, struct worker *wrk,
208
    struct busyobj *bo)
209
{
210 1345
        int i, extrachance = 1;
211
        struct backend *bp;
212
        struct vtp *vtp;
213
214 1345
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
215 1345
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
216 1345
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
217 1345
        CAST_OBJ_NOTNULL(bp, d->priv, BACKEND_MAGIC);
218
219
        /*
220
         * Now that we know our backend, we can set a default Host:
221
         * header if one is necessary.  This cannot be done in the VCL
222
         * because the backend may be chosen by a director.
223
         */
224 1345
        if (!http_GetHdr(bo->bereq, H_Host, NULL) && bp->hosthdr != NULL)
225 1297
                http_PrintfHeader(bo->bereq, "Host: %s", bp->hosthdr);
226
227
        do {
228 1350
                vtp = vbe_dir_getfd(wrk, bp, bo, extrachance == 0);
229 1349
                if (vtp == NULL)
230 33
                        return (-1);
231 1316
                AN(bo->htc);
232 1316
                if (vtp->state != VTP_STATE_STOLEN)
233 850
                        extrachance = 0;
234
235 1316
                i = V1F_SendReq(wrk, bo, &bo->acct.bereq_hdrbytes, 0);
236
237 1316
                if (vtp->state != VTP_STATE_USED) {
238 932
                        if (VTP_Wait(wrk, vtp, VTIM_real() +
239 466
                            bo->htc->first_byte_timeout) != 0) {
240 1
                                bo->htc->doclose = SC_RX_TIMEOUT;
241 1
                                VSLb(bo->vsl, SLT_FetchError,
242
                                     "Timed out reusing backend connection");
243 1
                                extrachance = 0;
244
                        }
245
                }
246
247 1316
                if (bo->htc->doclose == SC_NULL) {
248 1306
                        assert(vtp->state == VTP_STATE_USED);
249 1306
                        if (i == 0)
250 1306
                                i = V1F_FetchRespHdr(bo);
251 1306
                        if (i == 0) {
252 1271
                                AN(bo->htc->priv);
253 1271
                                return (0);
254
                        }
255
                }
256
257
                /*
258
                 * If we recycled a backend connection, there is a finite chance
259
                 * that the backend closed it before we got the bereq to it.
260
                 * In that case do a single automatic retry if req.body allows.
261
                 */
262 45
                vbe_dir_finish(d, wrk, bo);
263 45
                AZ(bo->htc);
264 45
                if (i < 0 || extrachance == 0)
265
                        break;
266 4
                if (bo->req != NULL &&
267 0
                    bo->req->req_body_status != REQ_BODY_NONE &&
268 0
                    bo->req->req_body_status != REQ_BODY_CACHED)
269 0
                        break;
270 4
                VSC_C_main->backend_retry++;
271 4
        } while (extrachance--);
272 41
        return (-1);
273
}
274
275
static const struct suckaddr * v_matchproto_(vdi_getip_f)
276 1
vbe_dir_getip(const struct director *d, struct worker *wrk,
277
    struct busyobj *bo)
278
{
279
        struct vtp *vtp;
280
281 1
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
282 1
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
283 1
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
284 1
        CHECK_OBJ_NOTNULL(bo->htc, HTTP_CONN_MAGIC);
285 1
        CAST_OBJ_NOTNULL(vtp, bo->htc->priv, VTP_MAGIC);
286
287 1
        return (vtp->addr);
288
}
289
290
/*--------------------------------------------------------------------*/
291
292
static enum sess_close
293 10
vbe_dir_http1pipe(const struct director *d, struct req *req, struct busyobj *bo)
294
{
295
        int i;
296
        enum sess_close retval;
297
        struct backend *bp;
298
        struct v1p_acct v1a;
299
        struct vtp *vtp;
300
301 10
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
302 10
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
303 10
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
304 10
        CAST_OBJ_NOTNULL(bp, d->priv, BACKEND_MAGIC);
305
306 10
        memset(&v1a, 0, sizeof v1a);
307
308
        /* This is hackish... */
309 10
        v1a.req = req->acct.req_hdrbytes;
310 10
        req->acct.req_hdrbytes = 0;
311
312 10
        req->res_mode = RES_PIPE;
313
314 10
        vtp = vbe_dir_getfd(req->wrk, bp, bo, 0);
315
316 10
        if (vtp == NULL) {
317 1
                retval = SC_TX_ERROR;
318
        } else {
319 9
                i = V1F_SendReq(req->wrk, bo, &v1a.bereq, 1);
320 9
                VSLb_ts_req(req, "Pipe", W_TIM_real(req->wrk));
321 9
                if (i == 0)
322 9
                        V1P_Process(req, vtp->fd, &v1a);
323 9
                VSLb_ts_req(req, "PipeSess", W_TIM_real(req->wrk));
324 9
                bo->htc->doclose = SC_TX_PIPE;
325 9
                vbe_dir_finish(d, req->wrk, bo);
326 9
                retval = SC_TX_PIPE;
327
        }
328 10
        V1P_Charge(req, &v1a, bp->vsc);
329 10
        return (retval);
330
}
331
332
/*--------------------------------------------------------------------*/
333
334
static void
335 844
vbe_dir_event(const struct director *d, enum vcl_event_e ev)
336
{
337
        struct backend *bp;
338
        struct VSC_vbe *vsc;
339
340 844
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
341 844
        CAST_OBJ_NOTNULL(bp, d->priv, BACKEND_MAGIC);
342
343 844
        if (ev == VCL_EVENT_WARM) {
344 815
                AZ(bp->vsc);
345 815
                bp->vsc = VSC_vbe_New(bp->director->display_name);
346 815
                AN(bp->vsc);
347
        }
348
349 844
        if (bp->probe != NULL && ev == VCL_EVENT_WARM)
350 15
                VBP_Control(bp, 1);
351
352 844
        if (bp->probe != NULL && ev == VCL_EVENT_COLD)
353 5
                VBP_Control(bp, 0);
354
355 844
        if (ev == VCL_EVENT_COLD) {
356 29
                AN(bp->vsc);
357 29
                Lck_Lock(&backends_mtx);
358 29
                vsc = bp->vsc;
359 29
                bp->vsc = NULL;
360 29
                Lck_Unlock(&backends_mtx);
361 29
                VSC_vbe_Destroy(&vsc);
362 29
                AZ(bp->vsc);
363
        }
364 844
}
365
366
/*---------------------------------------------------------------------*/
367
368
static void v_matchproto_(vdi_destroy_f)
369 38
vbe_destroy(const struct director *d)
370
{
371
        struct backend *be;
372
373 38
        ASSERT_CLI();
374 38
        CAST_OBJ_NOTNULL(be, d->priv, BACKEND_MAGIC);
375
376 38
        if (be->probe != NULL)
377 3
                VBP_Remove(be);
378
379 38
        Lck_Lock(&backends_mtx);
380 38
        if (be->cooled > 0)
381 0
                VTAILQ_REMOVE(&cool_backends, be, list);
382
        else
383 38
                VTAILQ_REMOVE(&backends, be, list);
384 38
        VSC_C_main->n_backend--;
385 38
        VTP_Rel(&be->tcp_pool);
386 38
        Lck_Unlock(&backends_mtx);
387
388
#define DA(x)   do { if (be->x != NULL) free(be->x); } while (0)
389
#define DN(x)   /**/
390 38
        VRT_BACKEND_HANDLE();
391
#undef DA
392
#undef DN
393
394 38
        AZ(be->vsc);
395 38
        Lck_Delete(&be->mtx);
396 38
        FREE_OBJ(be);
397 38
}
398
399
/*--------------------------------------------------------------------*/
400
401
static void
402 1
vbe_panic(const struct director *d, struct vsb *vsb)
403
{
404
        struct backend *bp;
405
406 1
        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
407 1
        CAST_OBJ_NOTNULL(bp, d->priv, BACKEND_MAGIC);
408
409 1
        VSB_printf(vsb, "display_name = %s,\n", bp->director->display_name);
410 1
        if (bp->ipv4_addr != NULL)
411 1
                VSB_printf(vsb, "ipv4 = %s,\n", bp->ipv4_addr);
412 1
        if (bp->ipv6_addr != NULL)
413 0
                VSB_printf(vsb, "ipv6 = %s,\n", bp->ipv6_addr);
414 1
        VSB_printf(vsb, "port = %s,\n", bp->port);
415 1
        VSB_printf(vsb, "hosthdr = %s,\n", bp->hosthdr);
416 1
        VSB_printf(vsb, "health = %s,\n",
417 1
            bp->director->health ? "healthy" : "sick");
418 2
        VSB_printf(vsb, "admin_health = %s, changed = %f,\n",
419 1
            VDI_Ahealth(bp->director),
420
            bp->director->health_changed);
421 1
        VSB_printf(vsb, "n_conn = %u,\n", bp->n_conn);
422 1
}
423
424
/*--------------------------------------------------------------------
425
 * Create a new static or dynamic director::backend instance.
426
 */
427
428
struct director *
429 828
VRT_new_backend(VRT_CTX, const struct vrt_backend *vrt)
430
{
431
        struct backend *be;
432
        struct director *d;
433
        struct vcl *vcl;
434
        const struct vrt_backend_probe *vbp;
435
        int retval;
436
437 828
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
438 828
        CHECK_OBJ_NOTNULL(vrt, VRT_BACKEND_MAGIC);
439 828
        assert(vrt->ipv4_suckaddr != NULL || vrt->ipv6_suckaddr != NULL);
440
441 828
        vcl = ctx->vcl;
442 828
        AN(vcl);
443 828
        AN(vrt->vcl_name);
444
445
        /* Create new backend */
446 828
        ALLOC_OBJ(be, BACKEND_MAGIC);
447 828
        XXXAN(be);
448 828
        Lck_New(&be->mtx, lck_backend);
449
450
#define DA(x)   do { if (vrt->x != NULL) REPLACE((be->x), (vrt->x)); } while (0)
451
#define DN(x)   do { be->x = vrt->x; } while (0)
452 828
        VRT_BACKEND_HANDLE();
453
#undef DA
454
#undef DN
455
456 828
        d = be->director;
457 828
        INIT_OBJ(d, DIRECTOR_MAGIC);
458 828
        d->priv = be;
459 828
        d->name = "backend";
460 828
        d->vcl_name = be->vcl_name;
461 828
        d->http1pipe = vbe_dir_http1pipe;
462 828
        d->healthy = vbe_dir_healthy;
463 828
        d->gethdrs = vbe_dir_gethdrs;
464 828
        d->getip = vbe_dir_getip;
465 828
        d->finish = vbe_dir_finish;
466 828
        d->event = vbe_dir_event;
467 828
        d->panic = vbe_panic;
468 828
        d->destroy = vbe_destroy;
469
470 828
        d->health = 1;
471 828
        d->health_changed = VTIM_real();
472 828
        d->admin_health = VDI_AH_PROBE;
473
474 828
        vbp = vrt->probe;
475 828
        if (vbp == NULL)
476 817
                vbp = VCL_DefaultProbe(vcl);
477
478 828
        Lck_Lock(&backends_mtx);
479 828
        VTAILQ_INSERT_TAIL(&backends, be, list);
480 828
        VSC_C_main->n_backend++;
481 828
        be->tcp_pool = VTP_Ref(vrt->ipv4_suckaddr, vrt->ipv6_suckaddr,
482
            vbe_proto_ident);
483 828
        Lck_Unlock(&backends_mtx);
484
485 828
        if (vbp != NULL) {
486 14
                VTP_AddRef(be->tcp_pool);
487 14
                VBP_Insert(be, vbp, be->tcp_pool);
488
        }
489
490 828
        retval = VCL_AddDirector(ctx->vcl, d, vrt->vcl_name);
491
492 828
        if (retval == 0)
493 828
                return (d);
494
495 0
        VRT_delete_backend(ctx, &d);
496 0
        AZ(d);
497 0
        return (NULL);
498
}
499
500
/*--------------------------------------------------------------------
501
 * Delete a dynamic director::backend instance.  Undeleted dynamic and
502
 * static instances are GC'ed when the VCL is discarded (in cache_vcl.c)
503
 */
504
505
void
506 5
VRT_delete_backend(VRT_CTX, struct director **dp)
507
{
508
        struct director *d;
509
        struct backend *be;
510
511 5
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
512 5
        TAKE_OBJ_NOTNULL(d, dp, DIRECTOR_MAGIC);
513 5
        CAST_OBJ_NOTNULL(be, d->priv, BACKEND_MAGIC);
514 5
        Lck_Lock(&be->mtx);
515 5
        be->director->admin_health = VDI_AH_DELETED;
516 5
        be->director->health_changed = VTIM_real();
517 5
        be->cooled = VTIM_real() + 60.;
518 5
        Lck_Unlock(&be->mtx);
519 5
        Lck_Lock(&backends_mtx);
520 5
        VTAILQ_REMOVE(&backends, be, list);
521 5
        VTAILQ_INSERT_TAIL(&cool_backends, be, list);
522 5
        Lck_Unlock(&backends_mtx);
523
524
        // NB. The backend is still usable for the ongoing transactions,
525
        // this is why we don't bust the director's magic number.
526 5
}
527
528
void
529 191
VBE_SetHappy(const struct backend *be, uint64_t happy)
530
{
531
532 191
        CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
533 191
        Lck_Lock(&backends_mtx);
534 191
        if (be->vsc != NULL)
535 177
                be->vsc->happy = happy;
536 191
        Lck_Unlock(&backends_mtx);
537 191
}
538
539
/*---------------------------------------------------------------------*/
540
541
void
542 4986
VBE_Poll(void)
543
{
544
        struct backend *be, *be2;
545 4986
        double now = VTIM_real();
546
547 4986
        ASSERT_CLI();
548 4986
        Lck_Lock(&backends_mtx);
549 4986
        VTAILQ_FOREACH_SAFE(be, &cool_backends, list, be2) {
550 10
                CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
551 10
                if (be->cooled > now)
552 10
                        break;
553 0
                if (be->n_conn > 0)
554 0
                        continue;
555 0
                Lck_Unlock(&backends_mtx);
556 0
                VCL_DelDirector(be->director);
557 0
                Lck_Lock(&backends_mtx);
558
        }
559 4986
        Lck_Unlock(&backends_mtx);
560 4986
}
561
562
/*---------------------------------------------------------------------*/
563
564
void
565 614
VBE_InitCfg(void)
566
{
567
568 614
        Lck_New(&backends_mtx, lck_vbe);
569 614
}