source: bin/varnishd/storage_persistent.c @ 8aa1d8

Revision 8aa1d8, 15.4 KB checked in by Poul-Henning Kamp <phk@…>, 3 years ago (diff)

Eliminate nested <*.h> includes from include/*

Sort #includes according to rules which are for me to know and you
to guess.

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2008-2011 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
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 * Persistent storage method
29 *
30 * XXX: Before we start the client or maybe after it stops, we should give the
31 * XXX: stevedores a chance to examine their storage for consistency.
32 *
33 * XXX: Do we ever free the LRU-lists ?
34 */
35
36#include "config.h"
37
38#include <sys/param.h>
39#include <sys/mman.h>
40
41#include <stdint.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include "cache.h"
47
48#include "cli_priv.h"
49#include "hash_slinger.h"
50#include "persistent.h"
51#include "stevedore.h"
52#include "vcli.h"
53#include "vend.h"
54#include "vsha256.h"
55
56#include "storage_persistent.h"
57
58/*--------------------------------------------------------------------*/
59
60/*
61 * silos is unlocked, it only changes during startup when we are
62 * single-threaded
63 */
64static VTAILQ_HEAD(,smp_sc)     silos = VTAILQ_HEAD_INITIALIZER(silos);
65
66/*--------------------------------------------------------------------
67 * Add bans to silos
68 */
69
70static void
71smp_appendban(struct smp_sc *sc, struct smp_signctx *ctx,
72    uint32_t len, const uint8_t *ban)
73{
74        uint8_t *ptr, *ptr2;
75
76        (void)sc;
77        ptr = ptr2 = SIGN_END(ctx);
78
79        memcpy(ptr, "BAN", 4);
80        ptr += 4;
81
82        vbe32enc(ptr, len);
83        ptr += 4;
84
85        memcpy(ptr, ban, len);
86        ptr += len;
87
88        smp_append_sign(ctx, ptr2, ptr - ptr2);
89}
90
91/* Trust that cache_ban.c takes care of locking */
92
93void
94SMP_NewBan(const uint8_t *ban, unsigned ln)
95{
96        struct smp_sc *sc;
97
98        VTAILQ_FOREACH(sc, &silos, list) {
99                smp_appendban(sc, &sc->ban1, ln, ban);
100                smp_appendban(sc, &sc->ban2, ln, ban);
101        }
102}
103
104/*--------------------------------------------------------------------
105 * Attempt to open and read in a ban list
106 */
107
108static int
109smp_open_bans(struct smp_sc *sc, struct smp_signctx *ctx)
110{
111        uint8_t *ptr, *pe;
112        uint32_t length;
113        int i, retval = 0;
114
115        ASSERT_CLI();
116        (void)sc;
117        i = smp_chk_sign(ctx);
118        if (i)
119                return (i);
120        ptr = SIGN_DATA(ctx);
121        pe = ptr + ctx->ss->length;
122
123        while (ptr < pe) {
124                if (memcmp(ptr, "BAN", 4)) {
125                        retval = 1001;
126                        break;
127                }
128                ptr += 4;
129
130                length = vbe32dec(ptr);
131                ptr += 4;
132
133                if (ptr + length > pe) {
134                        retval = 1003;
135                        break;
136                }
137
138                BAN_Reload(ptr, length);
139
140                ptr += length;
141        }
142        assert(ptr <= pe);
143        return (retval);
144}
145
146/*--------------------------------------------------------------------
147 * Attempt to open and read in a segment list
148 */
149
150static int
151smp_open_segs(struct smp_sc *sc, struct smp_signctx *ctx)
152{
153        uint64_t length, l;
154        struct smp_segptr *ss, *se;
155        struct smp_seg *sg, *sg1, *sg2;
156        int i, n = 0;
157
158        ASSERT_CLI();
159        i = smp_chk_sign(ctx);
160        if (i)
161                return (i);
162
163        ss = SIGN_DATA(ctx);
164        length = ctx->ss->length;
165
166        if (length == 0) {
167                /* No segments */
168                sc->free_offset = sc->ident->stuff[SMP_SPC_STUFF];
169                return (0);
170        }
171        se = ss + length / sizeof *ss;
172        se--;
173        assert(ss <= se);
174
175        /*
176         * Locate the free reserve, there are only two basic cases,
177         * but once we start dropping segments, things gets more complicated.
178         */
179
180        sc->free_offset = se->offset + se->length;
181        l = sc->mediasize - sc->free_offset;
182        if (se->offset > ss->offset && l >= sc->free_reserve) {
183                /*
184                 * [__xxxxyyyyzzzz___]
185                 * Plenty of space at tail, do nothing.
186                 */
187        } else if (ss->offset > se->offset) {
188                /*
189                 * [zzzz____xxxxyyyy_]
190                 * (make) space between ends
191                 * We might nuke the entire tail end without getting
192                 * enough space, in which case we fall through to the
193                 * last check.
194                 */
195                while (ss < se && ss->offset > se->offset) {
196                        l = ss->offset - (se->offset + se->length);
197                        if (l > sc->free_reserve)
198                                break;
199                        ss++;
200                        n++;
201                }
202        }
203
204        if (l < sc->free_reserve) {
205                /*
206                 * [__xxxxyyyyzzzz___]
207                 * (make) space at front
208                 */
209                sc->free_offset = sc->ident->stuff[SMP_SPC_STUFF];
210                while (ss < se) {
211                        l = ss->offset - sc->free_offset;
212                        if (l > sc->free_reserve)
213                                break;
214                        ss++;
215                        n++;
216                }
217        }
218
219        assert (l >= sc->free_reserve);
220
221
222        sg1 = NULL;
223        sg2 = NULL;
224        for(; ss <= se; ss++) {
225                ALLOC_OBJ(sg, SMP_SEG_MAGIC);
226                AN(sg);
227                sg->lru = LRU_Alloc();
228                CHECK_OBJ_NOTNULL(sg->lru, LRU_MAGIC);
229                sg->p = *ss;
230
231                sg->flags |= SMP_SEG_MUSTLOAD;
232
233                /*
234                 * HACK: prevent save_segs from nuking segment until we have
235                 * HACK: loaded it.
236                 */
237                sg->nobj = 1;
238                if (sg1 != NULL) {
239                        assert(sg1->p.offset != sg->p.offset);
240                        if (sg1->p.offset < sg->p.offset)
241                                assert(smp_segend(sg1) <= sg->p.offset);
242                        else
243                                assert(smp_segend(sg) <= sg1->p.offset);
244                }
245                if (sg2 != NULL) {
246                        assert(sg2->p.offset != sg->p.offset);
247                        if (sg2->p.offset < sg->p.offset)
248                                assert(smp_segend(sg2) <= sg->p.offset);
249                        else
250                                assert(smp_segend(sg) <= sg2->p.offset);
251                }
252
253                /* XXX: check that they are inside silo */
254                /* XXX: check that they don't overlap */
255                /* XXX: check that they are serial */
256                sg->sc = sc;
257                VTAILQ_INSERT_TAIL(&sc->segments, sg, list);
258                sg2 = sg;
259                if (sg1 == NULL)
260                        sg1 = sg;
261        }
262        printf("Dropped %d segments to make free_reserve\n", n);
263        return (0);
264}
265
266/*--------------------------------------------------------------------
267 * Silo worker thread
268 */
269
270static void *
271smp_thread(struct sess *sp, void *priv)
272{
273        struct smp_sc   *sc;
274        struct smp_seg *sg;
275
276        (void)sp;
277        CAST_OBJ_NOTNULL(sc, priv, SMP_SC_MAGIC);
278
279        /* First, load all the objects from all segments */
280        VTAILQ_FOREACH(sg, &sc->segments, list)
281                if (sg->flags & SMP_SEG_MUSTLOAD)
282                        smp_load_seg(sp, sc, sg);
283
284        sc->flags |= SMP_SC_LOADED;
285        BAN_TailDeref(&sc->tailban);
286        AZ(sc->tailban);
287        printf("Silo completely loaded\n");
288        while (1) {
289                (void)sleep (1);
290                sg = VTAILQ_FIRST(&sc->segments);
291                if (sg != NULL && sg -> sc->cur_seg &&
292                    sg->nobj == 0) {
293                        Lck_Lock(&sc->mtx);
294                        smp_save_segs(sc);
295                        Lck_Unlock(&sc->mtx);
296                }
297        }
298        NEEDLESS_RETURN(NULL);
299}
300
301/*--------------------------------------------------------------------
302 * Open a silo in the worker process
303 */
304
305static void
306smp_open(const struct stevedore *st)
307{
308        struct smp_sc   *sc;
309
310        ASSERT_CLI();
311
312        CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC);
313
314        Lck_New(&sc->mtx, lck_smp);
315        Lck_Lock(&sc->mtx);
316
317        sc->stevedore = st;
318
319        /* We trust the parent to give us a valid silo, for good measure: */
320        AZ(smp_valid_silo(sc));
321
322        AZ(mprotect(sc->base, 4096, PROT_READ));
323
324        sc->ident = SIGN_DATA(&sc->idn);
325
326        /* We attempt ban1 first, and if that fails, try ban2 */
327        if (smp_open_bans(sc, &sc->ban1))
328                AZ(smp_open_bans(sc, &sc->ban2));
329
330        /* We attempt seg1 first, and if that fails, try seg2 */
331        if (smp_open_segs(sc, &sc->seg1))
332                AZ(smp_open_segs(sc, &sc->seg2));
333
334        /*
335         * Grap a reference to the tail of the ban list, until the thread
336         * has loaded all objects, so we can be sure that all of our
337         * proto-bans survive until then.
338         */
339        sc->tailban = BAN_TailRef();
340        AN(sc->tailban);
341
342        /* XXX: save segments to ensure consistency between seg1 & seg2 ? */
343
344        /* XXX: abandon early segments to make sure we have free space ? */
345
346        /* Open a new segment, so we are ready to write */
347        smp_new_seg(sc);
348
349        /* Start the worker silo worker thread, it will load the objects */
350        WRK_BgThread(&sc->thread, "persistence", smp_thread, sc);
351
352        VTAILQ_INSERT_TAIL(&silos, sc, list);
353        Lck_Unlock(&sc->mtx);
354}
355
356/*--------------------------------------------------------------------
357 * Close a silo
358 */
359
360static void
361smp_close(const struct stevedore *st)
362{
363        struct smp_sc   *sc;
364
365        ASSERT_CLI();
366
367        CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC);
368        Lck_Lock(&sc->mtx);
369        smp_close_seg(sc, sc->cur_seg);
370        Lck_Unlock(&sc->mtx);
371
372        /* XXX: reap thread */
373}
374
375/*--------------------------------------------------------------------
376 * Allocate a bite.
377 *
378 * Allocate [min_size...max_size] space from the bottom of the segment,
379 * as is convenient.
380 *
381 * If 'so' + 'idx' is given, also allocate a smp_object from the top
382 * of the segment.
383 *
384 * Return the segment in 'ssg' if given.
385 */
386
387static struct storage *
388smp_allocx(struct stevedore *st, size_t min_size, size_t max_size,
389    struct smp_object **so, unsigned *idx, struct smp_seg **ssg)
390{
391        struct smp_sc *sc;
392        struct storage *ss;
393        struct smp_seg *sg;
394        unsigned tries;
395        uint64_t left, extra;
396
397        CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC);
398        assert(min_size <= max_size);
399
400        max_size = IRNUP(sc, max_size);
401        min_size = IRNUP(sc, min_size);
402
403        extra = IRNUP(sc, sizeof(*ss));
404        if (so != NULL) {
405                extra += sizeof(**so);
406                AN(idx);
407        }
408
409        Lck_Lock(&sc->mtx);
410        sg = NULL;
411        ss = NULL;
412        for (tries = 0; tries < 3; tries++) {
413                left = smp_spaceleft(sc, sc->cur_seg);
414                if (left >= extra + min_size)
415                        break;
416                smp_close_seg(sc, sc->cur_seg);
417                smp_new_seg(sc);
418        }
419        if (left >= extra + min_size)  {
420                if (left < extra + max_size)
421                        max_size = IRNDN(sc, left - extra);
422
423                sg = sc->cur_seg;
424                ss = (void*)(sc->base + sc->next_bot);
425                sc->next_bot += max_size + IRNUP(sc, sizeof(*ss));
426                sg->nalloc++;
427                if (so != NULL) {
428                        sc->next_top -= sizeof(**so);
429                        *so = (void*)(sc->base + sc->next_top);
430                        /* Render this smp_object mostly harmless */
431                        (*so)->ttl = 0.;
432                        (*so)->ban = 0.;
433                        (*so)->ptr = 0;;
434                        sg->objs = *so;
435                        *idx = ++sg->p.lobjlist;
436                }
437                (void)smp_spaceleft(sc, sg);    /* for the assert */
438        }
439        Lck_Unlock(&sc->mtx);
440
441        if (ss == NULL)
442                return (ss);
443        AN(sg);
444        assert(max_size >= min_size);
445
446        /* Fill the storage structure */
447        memset(ss, 0, sizeof *ss);
448        ss->magic = STORAGE_MAGIC;
449        ss->ptr = PRNUP(sc, ss + 1);
450        ss->space = max_size;
451        ss->priv = sc;
452        ss->stevedore = st;
453#ifdef SENDFILE_WORKS
454        ss->fd = sc->fd;
455#endif
456        if (ssg != NULL)
457                *ssg = sg;
458        return (ss);
459}
460
461/*--------------------------------------------------------------------
462 * Allocate an object
463 */
464
465static struct object *
466smp_allocobj(struct stevedore *stv, struct sess *sp, unsigned ltot,
467    const struct stv_objsecrets *soc)
468{
469        struct object *o;
470        struct storage *st;
471        struct smp_sc   *sc;
472        struct smp_seg *sg;
473        struct smp_object *so;
474        struct objcore *oc;
475        unsigned objidx;
476
477        if (sp->objcore == NULL)
478                return (NULL);          /* from cnt_error */
479        CAST_OBJ_NOTNULL(sc, stv->priv, SMP_SC_MAGIC);
480        AN(sp->objcore);
481        AN(sp->wrk->exp.ttl > 0.);
482
483        ltot = IRNUP(sc, ltot);
484
485        st = smp_allocx(stv, ltot, ltot, &so, &objidx, &sg);
486        if (st == NULL)
487                return (NULL);
488
489        assert(st->space >= ltot);
490        ltot = st->len = st->space;
491
492        o = STV_MkObject(sp, st->ptr, ltot, soc);
493        CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
494        o->objstore = st;
495
496        oc = o->objcore;
497        CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
498        oc->flags |= OC_F_LRUDONTMOVE;
499
500        Lck_Lock(&sc->mtx);
501        sg->nfixed++;
502        sg->nobj++;
503
504        /* We have to do this somewhere, might as well be here... */
505        assert(sizeof so->hash == DIGEST_LEN);
506        memcpy(so->hash, oc->objhead->digest, DIGEST_LEN);
507        so->ttl = EXP_Grace(NULL, o);
508        so->ptr = (uint8_t*)o - sc->base;
509        so->ban = BAN_Time(oc->ban);
510
511        smp_init_oc(oc, sg, objidx);
512
513        Lck_Unlock(&sc->mtx);
514        return (o);
515}
516
517/*--------------------------------------------------------------------
518 * Allocate a bite
519 */
520
521static struct storage *
522smp_alloc(struct stevedore *st, size_t size)
523{
524
525        return (smp_allocx(st,
526            size > 4096 ? 4096 : size, size, NULL, NULL, NULL));
527}
528
529/*--------------------------------------------------------------------
530 * Trim a bite
531 * XXX: We could trim the last allocation.
532 */
533
534static void
535smp_trim(struct storage *ss, size_t size)
536{
537
538        (void)ss;
539        (void)size;
540}
541
542/*--------------------------------------------------------------------
543 * We don't track frees of storage, we track the objects which own the
544 * storage and when there are no more objects in in the first segment,
545 * it can be reclaimed.
546 * XXX: We could free the last allocation, but does that happen ?
547 */
548
549static void __match_proto__(storage_free_f)
550smp_free(struct storage *st)
551{
552
553        /* XXX */
554        (void)st;
555}
556
557
558/*--------------------------------------------------------------------*/
559
560const struct stevedore smp_stevedore = {
561        .magic  =       STEVEDORE_MAGIC,
562        .name   =       "persistent",
563        .init   =       smp_mgt_init,
564        .open   =       smp_open,
565        .close  =       smp_close,
566        .alloc  =       smp_alloc,
567        .allocobj =     smp_allocobj,
568        .free   =       smp_free,
569        .trim   =       smp_trim,
570};
571
572/*--------------------------------------------------------------------
573 * Persistence is a bear to test unadultered, so we cheat by adding
574 * a cli command we can use to make it do tricks for us.
575 */
576
577static void
578debug_report_silo(struct cli *cli, const struct smp_sc *sc, int objs)
579{
580        struct smp_seg *sg;
581        struct objcore *oc;
582
583        VCLI_Out(cli, "Silo: %s (%s)\n",
584            sc->stevedore->ident, sc->filename);
585        VTAILQ_FOREACH(sg, &sc->segments, list) {
586                VCLI_Out(cli, "  Seg: [0x%jx ... +0x%jx]\n",
587                   (uintmax_t)sg->p.offset, (uintmax_t)sg->p.length);
588                if (sg == sc->cur_seg)
589                        VCLI_Out(cli,
590                           "    Alloc: [0x%jx ... 0x%jx] = 0x%jx free\n",
591                           (uintmax_t)(sc->next_bot),
592                           (uintmax_t)(sc->next_top),
593                           (uintmax_t)(sc->next_top - sc->next_bot));
594                VCLI_Out(cli, "    %u nobj, %u alloc, %u lobjlist, %u fixed\n",
595                    sg->nobj, sg->nalloc, sg->p.lobjlist, sg->nfixed);
596                if (objs) {
597                        VTAILQ_FOREACH(oc, &sg->lru->lru_head, lru_list)
598                                VCLI_Out(cli, "      OC %p\n", oc);
599                }
600        }
601}
602
603static void
604debug_persistent(struct cli *cli, const char * const * av, void *priv)
605{
606        struct smp_sc *sc;
607
608        (void)priv;
609
610        if (av[2] == NULL) {
611                VTAILQ_FOREACH(sc, &silos, list)
612                        debug_report_silo(cli, sc, 0);
613                return;
614        }
615        VTAILQ_FOREACH(sc, &silos, list)
616                if (!strcmp(av[2], sc->stevedore->ident))
617                        break;
618        if (sc == NULL) {
619                VCLI_Out(cli, "Silo <%s> not found\n", av[2]);
620                VCLI_SetResult(cli, CLIS_PARAM);
621                return;
622        }
623        if (av[3] == NULL) {
624                debug_report_silo(cli, sc, 0);
625                return;
626        }
627        Lck_Lock(&sc->mtx);
628        if (!strcmp(av[3], "sync")) {
629                smp_close_seg(sc, sc->cur_seg);
630                smp_new_seg(sc);
631        } else if (!strcmp(av[3], "dump")) {
632                debug_report_silo(cli, sc, 1);
633        } else {
634                VCLI_Out(cli, "Unknown operation\n");
635                VCLI_SetResult(cli, CLIS_PARAM);
636        }
637        Lck_Unlock(&sc->mtx);
638}
639
640static struct cli_proto debug_cmds[] = {
641        { "debug.persistent", "debug.persistent",
642                "Persistent debugging magic:\n"
643                "\tdebug.persistent [stevedore [cmd]]\n"
644                "With no cmd arg, a summary of the silo is returned.\n"
645                "Possible commands:\n"
646                "\tsync\tClose current segment, open a new one\n"
647                "\tdump\tinclude objcores in silo summary\n"
648                "",
649                0, 2, "d", debug_persistent },
650        { NULL }
651};
652
653/*--------------------------------------------------------------------*/
654
655void
656SMP_Init(void)
657{
658        CLI_AddFuncs(debug_cmds);
659}
660
661/*--------------------------------------------------------------------
662 * Pause until all silos have loaded.
663 */
664
665void
666SMP_Ready(void)
667{
668        struct smp_sc *sc;
669
670        ASSERT_CLI();
671        do {
672                VTAILQ_FOREACH(sc, &silos, list)
673                        if (!(sc->flags & SMP_SC_LOADED))
674                                break;
675                if (sc != NULL)
676                        (void)sleep(1);
677        } while (sc != NULL);
678}
Note: See TracBrowser for help on using the repository browser.