source: bin/varnishd/cache/cache_backend.c @ 96e2a9

Revision 96e2a9, 12.8 KB checked in by Poul-Henning Kamp <phk@…>, 2 years ago (diff)

Avoid taking the saintmode lock if the list empty.

Submitted by: DocWilco

  • Property mode set to 100644
Line 
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 * Handle backend connections and backend request structures.
30 *
31 */
32
33#include "config.h"
34
35#include <poll.h>
36#include <stdlib.h>
37#include <stddef.h>
38#include <stdio.h>
39
40#include "cache.h"
41
42#include "cache_backend.h"
43#include "vrt.h"
44#include "vtcp.h"
45
46static struct mempool   *vbcpool;
47
48static unsigned         vbcps = sizeof(struct vbc);
49
50/*--------------------------------------------------------------------
51 * The "simple" director really isn't, since thats where all the actual
52 * connections happen.  Nontheless, pretend it is simple by sequestering
53 * the directoricity of it under this line.
54 */
55
56struct vdi_simple {
57        unsigned                magic;
58#define VDI_SIMPLE_MAGIC        0x476d25b7
59        struct director         dir;
60        struct backend          *backend;
61        const struct vrt_backend *vrt;
62};
63
64/*--------------------------------------------------------------------
65 * Create default Host: header for backend request
66 */
67void
68VDI_AddHostHeader(struct http *hp, const struct vbc *vbc)
69{
70
71        CHECK_OBJ_NOTNULL(vbc, VBC_MAGIC);
72        CHECK_OBJ_NOTNULL(vbc->vdis, VDI_SIMPLE_MAGIC);
73        http_PrintfHeader(hp,
74            "Host: %s", vbc->vdis->vrt->hosthdr);
75}
76
77/*--------------------------------------------------------------------*/
78
79/* Private interface from backend_cfg.c */
80void
81VBE_ReleaseConn(struct vbc *vc)
82{
83
84        CHECK_OBJ_NOTNULL(vc, VBC_MAGIC);
85        assert(vc->backend == NULL);
86        assert(vc->fd < 0);
87        MPL_Free(vbcpool, vc);
88}
89
90#define FIND_TMO(tmx, dst, sp, be)                                      \
91        do {                                                            \
92                CHECK_OBJ_NOTNULL(sp->req->busyobj, BUSYOBJ_MAGIC);     \
93                dst = sp->req->busyobj->tmx;                            \
94                if (dst == 0.0)                                         \
95                        dst = be->tmx;                                  \
96                if (dst == 0.0)                                         \
97                        dst = cache_param->tmx;                         \
98        } while (0)
99
100/*--------------------------------------------------------------------
101 * Attempt to connect to a given addrinfo entry.
102 *
103 * Must be called with locked backend, but will release the backend
104 * lock during the slow/sleeping stuff, so that other worker threads
105 * can have a go, while we ponder.
106 *
107 */
108
109static int
110vbe_TryConnect(const struct sess *sp, int pf, const struct sockaddr_storage *sa,
111    socklen_t salen, const struct vdi_simple *vs)
112{
113        int s, i, tmo;
114        double tmod;
115
116        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
117        CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
118
119        s = socket(pf, SOCK_STREAM, 0);
120        if (s < 0)
121                return (s);
122
123        FIND_TMO(connect_timeout, tmod, sp, vs->vrt);
124
125        tmo = (int)(tmod * 1000.0);
126
127        i = VTCP_connect(s, sa, salen, tmo);
128
129        if (i != 0) {
130                AZ(close(s));
131                return (-1);
132        }
133
134        return (s);
135}
136
137/*--------------------------------------------------------------------*/
138
139static void
140bes_conn_try(const struct sess *sp, struct vbc *vc, const struct vdi_simple *vs)
141{
142        int s;
143        struct backend *bp = vs->backend;
144        char abuf1[VTCP_ADDRBUFSIZE];
145        char pbuf1[VTCP_PORTBUFSIZE];
146
147        CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
148
149        Lck_Lock(&bp->mtx);
150        bp->refcount++;
151        bp->n_conn++;           /* It mostly works */
152        Lck_Unlock(&bp->mtx);
153
154        s = -1;
155        assert(bp->ipv6 != NULL || bp->ipv4 != NULL);
156
157        /* release lock during stuff that can take a long time */
158
159        if (cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
160                s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
161                vc->addr = bp->ipv6;
162                vc->addrlen = bp->ipv6len;
163        }
164        if (s == -1 && bp->ipv4 != NULL) {
165                s = vbe_TryConnect(sp, PF_INET, bp->ipv4, bp->ipv4len, vs);
166                vc->addr = bp->ipv4;
167                vc->addrlen = bp->ipv4len;
168        }
169        if (s == -1 && !cache_param->prefer_ipv6 && bp->ipv6 != NULL) {
170                s = vbe_TryConnect(sp, PF_INET6, bp->ipv6, bp->ipv6len, vs);
171                vc->addr = bp->ipv6;
172                vc->addrlen = bp->ipv6len;
173        }
174
175        vc->fd = s;
176        if (s < 0) {
177                Lck_Lock(&bp->mtx);
178                bp->n_conn--;
179                bp->refcount--;         /* Only keep ref on success */
180                Lck_Unlock(&bp->mtx);
181                vc->addr = NULL;
182                vc->addrlen = 0;
183        } else {
184                vc->vsl_id = s | VSL_BACKENDMARKER;
185                VTCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
186                VSLb(sp->req->vsl, SLT_BackendOpen, "%d %s %s %s ",
187                    vc->fd, vs->backend->display_name, abuf1, pbuf1);
188        }
189
190}
191
192/*--------------------------------------------------------------------
193 * Check that there is still something at the far end of a given socket.
194 * We poll the fd with instant timeout, if there are any events we can't
195 * use it (backends are not allowed to pipeline).
196 */
197
198static int
199vbe_CheckFd(int fd)
200{
201        struct pollfd pfd;
202
203        pfd.fd = fd;
204        pfd.events = POLLIN;
205        pfd.revents = 0;
206        return(poll(&pfd, 1, 0) == 0);
207}
208
209/*--------------------------------------------------------------------
210 * Manage a pool of vbc structures.
211 * XXX: as an experiment, make this caching controled by a parameter
212 * XXX: so we can see if it has any effect.
213 */
214
215static struct vbc *
216vbe_NewConn(void)
217{
218        struct vbc *vc;
219
220        vc = MPL_Get(vbcpool, NULL);
221        XXXAN(vc);
222        vc->magic = VBC_MAGIC;
223        vc->fd = -1;
224        return (vc);
225}
226
227/*--------------------------------------------------------------------
228 * It evaluates if a backend is healthy _for_a_specific_object_.
229 * That means that it relies on sp->req->objcore->objhead. This is mainly for
230 * saint-mode, but also takes backend->healthy into account. If
231 * cache_param->saintmode_threshold is 0, this is basically just a test of
232 * backend->healthy.
233 *
234 * The threshold has to be evaluated _after_ the timeout check, otherwise
235 * items would never time out once the threshold is reached.
236 */
237
238static unsigned int
239vbe_Healthy(const struct vdi_simple *vs, const struct sess *sp)
240{
241        struct trouble *tr;
242        struct trouble *tr2;
243        struct trouble *old;
244        unsigned i = 0, retval;
245        unsigned int threshold;
246        struct backend *backend;
247        uintptr_t target;
248        double now;
249
250        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
251        CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
252        backend = vs->backend;
253        CHECK_OBJ_NOTNULL(backend, BACKEND_MAGIC);
254
255        if (backend->admin_health == ah_probe && !backend->healthy)
256                return (0);
257
258        if (backend->admin_health == ah_sick)
259                return (0);
260
261        /* VRT/VCC sets threshold to UINT_MAX to mark that it's not
262         * specified by VCL (thus use param).
263         */
264        threshold = vs->vrt->saintmode_threshold;
265        if (threshold == UINT_MAX)
266                threshold = cache_param->saintmode_threshold;
267
268        if (backend->admin_health == ah_healthy)
269                threshold = UINT_MAX;
270
271        /* Saintmode is disabled, or list is empty */
272        if (threshold == 0 || VTAILQ_EMPTY(&backend->troublelist))
273                return (1);
274
275        if (sp->req->objcore == NULL)
276                return (1);
277
278        now = sp->t_req;
279        target = (uintptr_t)(sp->req->objcore->objhead);
280
281        old = NULL;
282        retval = 1;
283        Lck_Lock(&backend->mtx);
284        VTAILQ_FOREACH_SAFE(tr, &backend->troublelist, list, tr2) {
285                CHECK_OBJ_NOTNULL(tr, TROUBLE_MAGIC);
286
287                if (tr->timeout < now) {
288                        VTAILQ_REMOVE(&backend->troublelist, tr, list);
289                        old = tr;
290                        retval = 1;
291                        break;
292                }
293
294                if (tr->target == target) {
295                        retval = 0;
296                        break;
297                }
298
299                /* If the threshold is at 1, a single entry on the list
300                 * will disable the backend. Since 0 is disable, ++i
301                 * instead of i++ to allow this behavior.
302                 */
303                if (++i >= threshold) {
304                        retval = 0;
305                        break;
306                }
307        }
308        Lck_Unlock(&backend->mtx);
309
310        if (old != NULL)
311                FREE_OBJ(old);
312
313        return (retval);
314}
315
316/*--------------------------------------------------------------------
317 * Get a connection to a particular backend.
318 */
319
320static struct vbc *
321vbe_GetVbe(const struct sess *sp, struct vdi_simple *vs)
322{
323        struct vbc *vc;
324        struct backend *bp;
325
326        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
327        CHECK_OBJ_NOTNULL(vs, VDI_SIMPLE_MAGIC);
328        bp = vs->backend;
329        CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
330
331        /* first look for vbc's we can recycle */
332        while (1) {
333                Lck_Lock(&bp->mtx);
334                vc = VTAILQ_FIRST(&bp->connlist);
335                if (vc != NULL) {
336                        bp->refcount++;
337                        assert(vc->backend == bp);
338                        assert(vc->fd >= 0);
339                        AN(vc->addr);
340                        VTAILQ_REMOVE(&bp->connlist, vc, list);
341                }
342                Lck_Unlock(&bp->mtx);
343                if (vc == NULL)
344                        break;
345                if (vbe_CheckFd(vc->fd)) {
346                        /* XXX locking of stats */
347                        VSC_C_main->backend_reuse += 1;
348                        VSLb(sp->req->vsl, SLT_Backend, "%d %s %s",
349                            vc->fd, sp->req->director->vcl_name,
350                            bp->display_name);
351                        vc->vdis = vs;
352                        vc->recycled = 1;
353                        return (vc);
354                }
355                VSC_C_main->backend_toolate++;
356                VSLb(sp->req->vsl, SLT_BackendClose, "%d %s toolate",
357                    vc->fd, bp->display_name);
358
359                /* Checkpoint log to flush all info related to this connection
360                   before the OS reuses the FD */
361                VSL_Flush(sp->req->vsl, 0);
362
363                VTCP_close(&vc->fd);
364                VBE_DropRefConn(bp);
365                vc->backend = NULL;
366                VBE_ReleaseConn(vc);
367        }
368
369        if (!vbe_Healthy(vs, sp)) {
370                VSC_C_main->backend_unhealthy++;
371                return (NULL);
372        }
373
374        if (vs->vrt->max_connections > 0 &&
375            bp->n_conn >= vs->vrt->max_connections) {
376                VSC_C_main->backend_busy++;
377                return (NULL);
378        }
379
380        vc = vbe_NewConn();
381        assert(vc->fd == -1);
382        AZ(vc->backend);
383        bes_conn_try(sp, vc, vs);
384        if (vc->fd < 0) {
385                VBE_ReleaseConn(vc);
386                VSC_C_main->backend_fail++;
387                return (NULL);
388        }
389        vc->backend = bp;
390        VSC_C_main->backend_conn++;
391        VSLb(sp->req->vsl, SLT_Backend, "%d %s %s",
392            vc->fd, sp->req->director->vcl_name, bp->display_name);
393        vc->vdis = vs;
394        return (vc);
395}
396
397/*--------------------------------------------------------------------
398 * Returns the backend if and only if the this is a simple director.
399 * XXX: Needs a better name and possibly needs a better general approach.
400 * XXX: This is mainly used by the DNS director to fetch the actual backend
401 * XXX: so it can compare DNS lookups with the actual IP.
402 */
403
404struct backend *
405vdi_get_backend_if_simple(const struct director *d)
406{
407        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
408        struct vdi_simple *vs, *vs2;
409
410        vs2 = d->priv;
411        if (vs2->magic != VDI_SIMPLE_MAGIC)
412                return (NULL);
413        CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
414        return (vs->backend);
415}
416
417/*--------------------------------------------------------------------
418 *
419 */
420
421void
422VBE_UseHealth(const struct director *vdi)
423{
424        struct vdi_simple *vs;
425
426        ASSERT_CLI();
427
428        if (strcmp(vdi->name, "simple"))
429                return;
430        CAST_OBJ_NOTNULL(vs, vdi->priv, VDI_SIMPLE_MAGIC);
431        if (vs->vrt->probe == NULL)
432                return;
433        VBP_Use(vs->backend, vs->vrt->probe);
434}
435
436/*--------------------------------------------------------------------
437 *
438 */
439
440static struct vbc * __match_proto__(vdi_getfd_f)
441vdi_simple_getfd(const struct director *d, struct sess *sp)
442{
443        struct vdi_simple *vs;
444        struct vbc *vc;
445
446        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
447        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
448        CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
449        vc = vbe_GetVbe(sp, vs);
450        if (vc != NULL) {
451                FIND_TMO(first_byte_timeout,
452                    vc->first_byte_timeout, sp, vs->vrt);
453                FIND_TMO(between_bytes_timeout,
454                    vc->between_bytes_timeout, sp, vs->vrt);
455        }
456        return (vc);
457}
458
459static unsigned
460vdi_simple_healthy(const struct director *d, const struct sess *sp)
461{
462        struct vdi_simple *vs;
463
464        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
465        CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
466        return (vbe_Healthy(vs, sp));
467}
468
469static void
470vdi_simple_fini(const struct director *d)
471{
472        struct vdi_simple *vs;
473
474        ASSERT_CLI();
475        CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
476        CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
477
478        if (vs->vrt->probe != NULL)
479                VBP_Remove(vs->backend, vs->vrt->probe);
480        VBE_DropRefVcl(vs->backend);
481        free(vs->dir.vcl_name);
482        vs->dir.magic = 0;
483        FREE_OBJ(vs);
484}
485
486void
487VRT_init_dir_simple(struct cli *cli, struct director **bp, int idx,
488    const void *priv)
489{
490        const struct vrt_backend *t;
491        struct vdi_simple *vs;
492
493        ASSERT_CLI();
494        (void)cli;
495        t = priv;
496
497        ALLOC_OBJ(vs, VDI_SIMPLE_MAGIC);
498        XXXAN(vs);
499        vs->dir.magic = DIRECTOR_MAGIC;
500        vs->dir.priv = vs;
501        vs->dir.name = "simple";
502        REPLACE(vs->dir.vcl_name, t->vcl_name);
503        vs->dir.getfd = vdi_simple_getfd;
504        vs->dir.fini = vdi_simple_fini;
505        vs->dir.healthy = vdi_simple_healthy;
506
507        vs->vrt = t;
508
509        vs->backend = VBE_AddBackend(cli, t);
510        if (vs->vrt->probe != NULL)
511                VBP_Insert(vs->backend, vs->vrt->probe, vs->vrt->hosthdr);
512
513        bp[idx] = &vs->dir;
514}
515
516void
517VDI_Init(void)
518{
519
520        vbcpool = MPL_New("vbc", &cache_param->vbc_pool, &vbcps);
521        AN(vbcpool);
522}
Note: See TracBrowser for help on using the repository browser.