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