source: bin/varnishd/stevedore.c @ 856edab

Revision 856edab, 13.6 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Give the stevedore the chance to tell which LRU list a given object
should be on.

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2007-2010 Redpill Linpro AS
3 * All rights reserved.
4 *
5 * Author: Dag-Erling Smørgav <des@des.no>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * STEVEDORE: one who works at or is responsible for loading and
29 * unloading ships in port.  Example: "on the wharves, stevedores were
30 * unloading cargo from the far corners of the world." Origin: Spanish
31 * estibador, from estibar to pack.  First Known Use: 1788
32 */
33
34#include "config.h"
35
36#include "svnid.h"
37SVNID("$Id$")
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <math.h>
43
44#include "cache.h"
45#include "stevedore.h"
46#include "hash_slinger.h"
47#include "cli_priv.h"
48#include "vrt_obj.h"
49
50static VTAILQ_HEAD(, stevedore) stevedores =
51    VTAILQ_HEAD_INITIALIZER(stevedores);
52
53static const struct stevedore * volatile stv_next;
54
55static struct stevedore *stv_transient;
56
57/*--------------------------------------------------------------------
58 * NB! Dirty trick alert:
59 *
60 * We use a captive objcore as tail senteniel for LRU lists, but to
61 * make sure it does not get into play by accident, we do _not_
62 * initialize its magic with OBJCORE_MAGIC.
63 *
64 */
65
66struct lru *
67LRU_Alloc(void)
68{
69        struct lru *l;
70
71        ALLOC_OBJ(l, LRU_MAGIC);
72        AN(l);
73        VLIST_INIT(&l->lru_head);
74        VLIST_INSERT_HEAD(&l->lru_head, &l->senteniel, lru_list);
75        return (l);
76}
77
78/*--------------------------------------------------------------------
79 * XXX: trust pointer writes to be atomic
80 */
81
82static struct stevedore *
83stv_pick_stevedore(const char *hint)
84{
85        struct stevedore *stv;
86
87        if (hint != NULL && *hint != '\0') {
88                VTAILQ_FOREACH(stv, &stevedores, list) {
89                        if (!strcmp(stv->ident, hint))
90                                return (stv);
91                }
92                if (!strcmp(TRANSIENT_STORAGE, hint))
93                        return (stv_transient);
94        }
95        /* pick a stevedore and bump the head along */
96        stv = VTAILQ_NEXT(stv_next, list);
97        if (stv == NULL)
98                stv = VTAILQ_FIRST(&stevedores);
99        AN(stv);
100        AN(stv->name);
101        stv_next = stv;
102        return (stv);
103}
104
105/*-------------------------------------------------------------------*/
106
107static struct storage *
108stv_alloc(const struct sess *sp, size_t size)
109{
110        struct storage *st;
111        struct stevedore *stv = NULL;
112        unsigned fail = 0;
113
114        /*
115         * Always try the stevedore which allocated the object in order to
116         * not needlessly split an object across multiple stevedores.
117         */
118        if (sp->obj != NULL) {
119                CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC);
120                stv = sp->obj->objstore->stevedore;
121        } else {
122                INCOMPL();
123                stv = stv_transient;
124        }
125        CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
126
127        for (;;) {
128                /* try to allocate from it */
129                AN(stv->alloc);
130                st = stv->alloc(stv, size);
131                if (st != NULL)
132                        break;
133
134                /* no luck; try to free some space and keep trying */
135                if (EXP_NukeOne(sp, stv->lru) == -1)
136                        break;
137
138                /* Enough is enough: try another if we have one */
139                if (++fail == 50)       /* XXX Param */
140                        break;
141        }
142        CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
143        return (st);
144}
145
146
147/*-------------------------------------------------------------------*
148 * Structure used to transport internal knowledge from STV_NewObject()
149 * to STV_MkObject().  Nobody else should mess with this struct.
150 */
151
152struct stv_objsecrets {
153        unsigned        magic;
154#define STV_OBJ_SECRETES_MAGIC  0x78c87247
155        unsigned        nhttp;
156        unsigned        lhttp;
157        unsigned        wsl;
158        double          ttl;
159};
160
161/*--------------------------------------------------------------------
162 * This function is called by stevedores ->allocobj() method, which
163 * very often will be stv_default_allocobj() below, to convert a slab
164 * of storage into object which the stevedore can then register in its
165 * internal state, before returning it to STV_NewObject().
166 * As you probably guessed: All this for persistence.
167 */
168
169struct object *
170STV_MkObject(struct sess *sp, void *ptr, unsigned ltot,
171    const struct stv_objsecrets *soc)
172{
173        struct object *o;
174        unsigned l;
175
176        CHECK_OBJ_NOTNULL(soc, STV_OBJ_SECRETES_MAGIC);
177
178        assert(PAOK(ptr));
179        assert(PAOK(soc->wsl));
180        assert(PAOK(soc->lhttp));
181
182        assert(ltot >= sizeof *o + soc->lhttp + soc->wsl);
183
184        o = ptr;
185        memset(o, 0, sizeof *o);
186        o->magic = OBJECT_MAGIC;
187
188        l = PRNDDN(ltot - (sizeof *o + soc->lhttp));
189        assert(l >= soc->wsl);
190
191        o->http = HTTP_create(o + 1, soc->nhttp);
192        WS_Init(o->ws_o, "obj", (char *)(o + 1) + soc->lhttp, soc->wsl);
193        WS_Assert(o->ws_o);
194        assert(o->ws_o->e <= (char*)ptr + ltot);
195
196        http_Setup(o->http, o->ws_o);
197        o->http->magic = HTTP_MAGIC;
198        o->grace = NAN;
199        o->entered = NAN;
200        o->ttl = soc->ttl;
201        VTAILQ_INIT(&o->store);
202        sp->wrk->stats.n_object++;
203
204        if (sp->objcore != NULL) {
205                CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC);
206
207                o->objcore = sp->objcore;
208                sp->objcore = NULL;     /* refcnt follows pointer. */
209                BAN_NewObj(o);
210
211                o->objcore->methods = &default_oc_methods;
212                o->objcore->priv = o;
213        }
214        return (o);
215}
216
217/*--------------------------------------------------------------------
218 * This is the default ->allocobj() which all stevedores who do not
219 * implement persistent storage can rely on.
220 */
221
222static struct object *
223stv_default_allocobj(struct stevedore *stv, struct sess *sp, unsigned ltot,
224    const struct stv_objsecrets *soc)
225{
226        struct object *o;
227        struct storage *st;
228
229        CHECK_OBJ_NOTNULL(soc, STV_OBJ_SECRETES_MAGIC);
230        st = stv->alloc(stv, ltot);
231        if (st == NULL)
232                return (NULL);
233        if (st->space < ltot) {
234                stv->free(st);
235                return (NULL);
236        }
237        ltot = st->len = st->space;
238        o = STV_MkObject(sp, st->ptr, ltot, soc);
239        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
240        o->objstore = st;
241        return (o);
242}
243
244/*-------------------------------------------------------------------
245 * Allocate storage for an object, based on the header information.
246 * XXX: If we know (a hint of) the length, we could allocate space
247 * XXX: for the body in the same allocation while we are at it.
248 */
249
250struct object *
251STV_NewObject(struct sess *sp, const char *hint, unsigned wsl, double ttl,
252    unsigned nhttp)
253{
254        struct object *o;
255        struct stevedore *stv;
256        unsigned lhttp, ltot;
257        struct stv_objsecrets soc;
258
259        assert(wsl > 0);
260        wsl = PRNDUP(wsl);
261
262        lhttp = HTTP_estimate(nhttp);
263        lhttp = PRNDUP(lhttp);
264
265        memset(&soc, 0, sizeof soc);
266        soc.magic = STV_OBJ_SECRETES_MAGIC;
267        soc.nhttp = nhttp;
268        soc.lhttp = lhttp;
269        soc.wsl = wsl;
270        soc.ttl = ttl;
271
272        ltot = sizeof *o + wsl + lhttp;
273
274        stv = stv_pick_stevedore(hint);
275        AN(stv->allocobj);
276        o = stv->allocobj(stv, sp, ltot, &soc);
277        if (o == NULL)
278                return (NULL);
279        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
280        CHECK_OBJ_NOTNULL(o->objstore, STORAGE_MAGIC);
281        return (o);
282}
283
284/*-------------------------------------------------------------------*/
285
286static struct lru *
287stv_default_getlru(const struct object *o)
288{
289
290        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
291        CHECK_OBJ_NOTNULL(o->objstore, STORAGE_MAGIC);
292        CHECK_OBJ_NOTNULL(o->objstore->stevedore, STEVEDORE_MAGIC);
293        CHECK_OBJ_NOTNULL(o->objstore->stevedore->lru, LRU_MAGIC);
294        return (o->objstore->stevedore->lru);
295}
296
297/*-------------------------------------------------------------------*/
298
299void
300STV_Freestore(struct object *o)
301{
302        struct storage *st, *stn;
303
304        VTAILQ_FOREACH_SAFE(st, &o->store, list, stn) {
305                CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
306                VTAILQ_REMOVE(&o->store, st, list);
307                STV_free(st);
308        }
309}
310
311/*---------------------------------------------------------------------
312 * Default objcore methods
313 */
314
315static struct object * __match_proto__(getobj_f)
316default_oc_getobj(struct worker *wrk, struct objcore *oc)
317{
318        struct object *o;
319
320        (void)wrk;
321        if (oc->priv == NULL)
322                return (NULL);
323        CAST_OBJ_NOTNULL(o, oc->priv, OBJECT_MAGIC);
324        return (o);
325}
326
327static void
328default_oc_freeobj(struct objcore *oc)
329{
330        struct object *o;
331
332        CAST_OBJ_NOTNULL(o, oc->priv, OBJECT_MAGIC);
333        oc->priv = NULL;
334        oc->methods = NULL;
335
336        STV_Freestore(o);
337        STV_free(o->objstore);
338}
339
340struct objcore_methods default_oc_methods = {
341        .getobj = default_oc_getobj,
342        .freeobj = default_oc_freeobj,
343};
344
345/*-------------------------------------------------------------------*/
346
347struct storage *
348STV_alloc(const struct sess *sp, size_t size)
349{
350
351        return (stv_alloc(sp, size));
352}
353
354void
355STV_trim(struct storage *st, size_t size)
356{
357
358        CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
359        AN(st->stevedore);
360        if (st->stevedore->trim)
361                st->stevedore->trim(st, size);
362}
363
364void
365STV_free(struct storage *st)
366{
367
368        CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
369        AN(st->stevedore);
370        AN(st->stevedore->free);
371        st->stevedore->free(st);
372}
373
374void
375STV_open(void)
376{
377        struct stevedore *stv;
378
379        VTAILQ_FOREACH(stv, &stevedores, list) {
380                if (stv->open != NULL)
381                        stv->open(stv);
382        }
383        stv = stv_transient;
384        if (stv->open != NULL)
385                stv->open(stv);
386}
387
388void
389STV_close(void)
390{
391        struct stevedore *stv;
392
393        VTAILQ_FOREACH(stv, &stevedores, list)
394                if (stv->close != NULL)
395                        stv->close(stv);
396        stv = stv_transient;
397        if (stv->close != NULL)
398                stv->close(stv);
399}
400
401struct lru *
402STV_lru(const struct object *o)
403{
404        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
405        CHECK_OBJ_NOTNULL(o->objstore, STORAGE_MAGIC);
406        CHECK_OBJ_NOTNULL(o->objstore->stevedore, STEVEDORE_MAGIC);
407        AN(o->objstore->stevedore->getlru);
408
409        return (o->objstore->stevedore->getlru(o));
410}
411
412/*--------------------------------------------------------------------
413 * Parse a stevedore argument on the form:
414 *      [ name '=' ] strategy [ ',' arg ] *
415 */
416
417static const struct choice STV_choice[] = {
418        { "file",       &smf_stevedore },
419        { "malloc",     &sma_stevedore },
420        { "persistent", &smp_stevedore },
421#ifdef HAVE_LIBUMEM
422        { "umem",       &smu_stevedore },
423#endif
424        { NULL,         NULL }
425};
426
427void
428STV_Config(const char *spec)
429{
430        char **av;
431        const char *p, *q;
432        struct stevedore *stv;
433        const struct stevedore *stv2;
434        int ac, l;
435        static unsigned seq = 0;
436
437        ASSERT_MGT();
438        p = strchr(spec, '=');
439        q = strchr(spec, ',');
440        if (p != NULL && (q == NULL || q > p)) {
441                av = ParseArgv(p + 1, ARGV_COMMA);
442        } else {
443                av = ParseArgv(spec, ARGV_COMMA);
444                p = NULL;
445        }
446        AN(av);
447
448        if (av[0] != NULL)
449                ARGV_ERR("%s\n", av[0]);
450
451        if (av[1] == NULL)
452                ARGV_ERR("-s argument lacks strategy {malloc, file, ...}\n");
453
454        for (ac = 0; av[ac + 2] != NULL; ac++)
455                continue;
456
457        stv2 = pick(STV_choice, av[1], "storage");
458        AN(stv2);
459
460        /* Append strategy to ident string */
461        vsb_printf(vident, ",-s%s", av[1]);
462
463        av += 2;
464
465        CHECK_OBJ_NOTNULL(stv2, STEVEDORE_MAGIC);
466        ALLOC_OBJ(stv, STEVEDORE_MAGIC);
467        AN(stv);
468
469        *stv = *stv2;
470        AN(stv->name);
471        AN(stv->alloc);
472        if (stv->allocobj == NULL)
473                stv->allocobj = stv_default_allocobj;
474        if (stv->getlru == NULL)
475                stv->getlru = stv_default_getlru;
476
477        if (p == NULL)
478                bprintf(stv->ident, "s%u", seq++);
479        else {
480                l = p - spec;
481                if (l > sizeof stv->ident - 1)
482                        l = sizeof stv->ident - 1;
483                bprintf(stv->ident, "%.*s", l, spec);
484        }
485
486        VTAILQ_FOREACH(stv2, &stevedores, list) {
487                if (strcmp(stv2->ident, stv->ident))
488                        continue;
489                ARGV_ERR("(-s%s=%s) already defined once\n",
490                    stv->ident, stv->name);
491        }
492
493        stv->lru = LRU_Alloc();
494
495        if (stv->init != NULL)
496                stv->init(stv, ac, av);
497        else if (ac != 0)
498                ARGV_ERR("(-s%s) too many arguments\n", stv->name);
499
500        if (!strcmp(stv->ident, TRANSIENT_STORAGE)) {
501                stv->transient = 1;
502                AZ(stv_transient);
503                stv_transient = stv;
504        } else {
505                VTAILQ_INSERT_TAIL(&stevedores, stv, list);
506                if (!stv_next)
507                        stv_next = VTAILQ_FIRST(&stevedores);
508        }
509}
510
511/*--------------------------------------------------------------------*/
512
513void
514STV_Config_Transient(void)
515{
516        const struct stevedore *stv;
517
518        ASSERT_MGT();
519        VTAILQ_FOREACH(stv, &stevedores, list)
520                if (!strcmp(stv->ident, TRANSIENT_STORAGE))
521                        return;
522        STV_Config(TRANSIENT_STORAGE "=malloc");
523}
524
525/*--------------------------------------------------------------------*/
526
527static void
528stv_cli_list(struct cli *cli, const char * const *av, void *priv)
529{
530        struct stevedore *stv;
531
532        ASSERT_MGT();
533        (void)av;
534        (void)priv;
535        cli_out(cli, "Storage devices:\n");
536        VTAILQ_FOREACH(stv, &stevedores, list)
537                cli_out(cli, "\tstorage.%s = %s\n", stv->ident, stv->name);
538}
539
540/*--------------------------------------------------------------------*/
541
542struct cli_proto cli_stv[] = {
543        { "storage.list", "storage.list", "List storage devices\n",
544            0, 0, "", stv_cli_list },
545        { NULL}
546};
547
548/*--------------------------------------------------------------------
549 * VRT functions for stevedores
550 */
551
552#include "vrt.h"
553
554static const struct stevedore *
555stv_find(const char *nm)
556{
557        const struct stevedore *stv;
558
559        VTAILQ_FOREACH(stv, &stevedores, list)
560                if (!strcmp(stv->ident, nm))
561                        return (stv);
562        if (!strcmp(TRANSIENT_STORAGE, nm))
563                return (stv_transient);
564        return (NULL);
565}
566
567int
568VRT_Stv(const char *nm)
569{
570
571        if (stv_find(nm) != NULL)
572                return (1);
573        return (0);
574}
575
576#define VRTSTVVAR(nm, vtype, ctype, dval)       \
577ctype                                           \
578VRT_Stv_##nm(const char *nm)                    \
579{                                               \
580        const struct stevedore *stv;            \
581                                                \
582        stv = stv_find(nm);                     \
583        if (stv == NULL)                        \
584                return (dval);                  \
585        if (stv->var_##nm == NULL)              \
586                return (dval);                  \
587        return (stv->var_##nm(stv));            \
588}
589
590#include "vrt_stv_var.h"
591#undef VRTSTVVAR
Note: See TracBrowser for help on using the repository browser.