source: bin/varnishd/storage_persistent.c @ 0e6355

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

First sweep to remove needless #includes.

This is based on output from some scripts and basic sanity testing.

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