|  |  | 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 | 768 | smp_appendban(const struct smp_sc *sc, struct smp_signspace *spc, | 
| 81 |  |     uint32_t len, const uint8_t *ban) | 
| 82 |  | { | 
| 83 |  |  | 
| 84 | 768 |         (void)sc; | 
| 85 | 768 |         if (SIGNSPACE_FREE(spc) < len) | 
| 86 | 0 |                 return (-1); | 
| 87 |  |  | 
| 88 | 768 |         memcpy(SIGNSPACE_FRONT(spc), ban, len); | 
| 89 | 768 |         smp_append_signspace(spc, len); | 
| 90 |  |  | 
| 91 | 768 |         return (0); | 
| 92 | 768 | } | 
| 93 |  |  | 
| 94 |  | /* Trust that cache_ban.c takes care of locking */ | 
| 95 |  |  | 
| 96 |  | static int | 
| 97 | 552 | smp_baninfo(const struct stevedore *stv, enum baninfo event, | 
| 98 |  |             const uint8_t *ban, unsigned len) | 
| 99 |  | { | 
| 100 |  |         struct smp_sc *sc; | 
| 101 | 552 |         int r = 0; | 
| 102 |  |  | 
| 103 | 552 |         CAST_OBJ_NOTNULL(sc, stv->priv, SMP_SC_MAGIC); | 
| 104 |  |  | 
| 105 | 552 |         switch (event) { | 
| 106 |  |         case BI_NEW: | 
| 107 | 384 |                 r |= smp_appendban(sc, &sc->ban1, len, ban); | 
| 108 | 384 |                 r |= smp_appendban(sc, &sc->ban2, len, ban); | 
| 109 | 384 |                 break; | 
| 110 |  |         default: | 
| 111 |  |                 /* Ignored */ | 
| 112 | 168 |                 break; | 
| 113 |  |         } | 
| 114 |  |  | 
| 115 | 552 |         return (r); | 
| 116 |  | } | 
| 117 |  |  | 
| 118 |  | static void | 
| 119 | 1200 | smp_banexport_spc(struct smp_signspace *spc, const uint8_t *bans, unsigned len) | 
| 120 |  | { | 
| 121 | 1200 |         smp_reset_signspace(spc); | 
| 122 | 1200 |         assert(SIGNSPACE_FREE(spc) >= len); | 
| 123 | 1200 |         memcpy(SIGNSPACE_DATA(spc), bans, len); | 
| 124 | 1200 |         smp_append_signspace(spc, len); | 
| 125 | 1200 |         smp_sync_sign(&spc->ctx); | 
| 126 | 1200 | } | 
| 127 |  |  | 
| 128 |  | static void | 
| 129 | 600 | smp_banexport(const struct stevedore *stv, const uint8_t *bans, unsigned len) | 
| 130 |  | { | 
| 131 |  |         struct smp_sc *sc; | 
| 132 |  |  | 
| 133 | 600 |         CAST_OBJ_NOTNULL(sc, stv->priv, SMP_SC_MAGIC); | 
| 134 | 600 |         smp_banexport_spc(&sc->ban1, bans, len); | 
| 135 | 600 |         smp_banexport_spc(&sc->ban2, bans, len); | 
| 136 | 600 | } | 
| 137 |  |  | 
| 138 |  | /*-------------------------------------------------------------------- | 
| 139 |  |  * Attempt to open and read in a ban list | 
| 140 |  |  */ | 
| 141 |  |  | 
| 142 |  | static int | 
| 143 | 304 | smp_open_bans(const struct smp_sc *sc, struct smp_signspace *spc) | 
| 144 |  | { | 
| 145 |  |         uint8_t *ptr, *pe; | 
| 146 |  |         int i; | 
| 147 |  |  | 
| 148 | 304 |         ASSERT_CLI(); | 
| 149 | 304 |         (void)sc; | 
| 150 | 304 |         i = smp_chk_signspace(spc); | 
| 151 | 304 |         if (i) | 
| 152 | 0 |                 return (i); | 
| 153 |  |  | 
| 154 | 304 |         ptr = SIGNSPACE_DATA(spc); | 
| 155 | 304 |         pe = SIGNSPACE_FRONT(spc); | 
| 156 | 304 |         BAN_Reload(ptr, pe - ptr); | 
| 157 |  |  | 
| 158 | 304 |         return (0); | 
| 159 | 304 | } | 
| 160 |  |  | 
| 161 |  | /*-------------------------------------------------------------------- | 
| 162 |  |  * Attempt to open and read in a segment list | 
| 163 |  |  */ | 
| 164 |  |  | 
| 165 |  | static int | 
| 166 | 304 | 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 | 304 |         int i, n = 0; | 
| 172 |  |  | 
| 173 | 304 |         ASSERT_CLI(); | 
| 174 | 304 |         i = smp_chk_signspace(spc); | 
| 175 | 304 |         if (i) | 
| 176 | 0 |                 return (i); | 
| 177 |  |  | 
| 178 | 304 |         ss = SIGNSPACE_DATA(spc); | 
| 179 | 304 |         length = SIGNSPACE_LEN(spc); | 
| 180 |  |  | 
| 181 | 304 |         if (length == 0) { | 
| 182 |  |                 /* No segments */ | 
| 183 | 184 |                 sc->free_offset = sc->ident->stuff[SMP_SPC_STUFF]; | 
| 184 | 184 |                 return (0); | 
| 185 |  |         } | 
| 186 | 120 |         se = ss + length / sizeof *ss; | 
| 187 | 120 |         se--; | 
| 188 | 120 |         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 | 120 |         sc->free_offset = se->offset + se->length; | 
| 196 | 120 |         l = sc->mediasize - sc->free_offset; | 
| 197 | 120 |         if (se->offset > ss->offset && l >= sc->free_reserve) { | 
| 198 |  |                 /* | 
| 199 |  |                  * [__xxxxyyyyzzzz___] | 
| 200 |  |                  * Plenty of space at tail, do nothing. | 
| 201 |  |                  */ | 
| 202 | 120 |         } 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 | 120 |         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 | 120 |         assert(l >= sc->free_reserve); | 
| 235 |  |  | 
| 236 |  |  | 
| 237 | 120 |         sg1 = NULL; | 
| 238 | 120 |         sg2 = NULL; | 
| 239 | 240 |         for (; ss <= se; ss++) { | 
| 240 | 120 |                 ALLOC_OBJ(sg, SMP_SEG_MAGIC); | 
| 241 | 120 |                 AN(sg); | 
| 242 | 120 |                 VTAILQ_INIT(&sg->objcores); | 
| 243 | 120 |                 sg->p = *ss; | 
| 244 |  |  | 
| 245 | 120 |                 sg->flags |= SMP_SEG_MUSTLOAD; | 
| 246 |  |  | 
| 247 |  |                 /* | 
| 248 |  |                  * HACK: prevent save_segs from nuking segment until we have | 
| 249 |  |                  * HACK: loaded it. | 
| 250 |  |                  */ | 
| 251 | 120 |                 sg->nobj = 1; | 
| 252 | 120 |                 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 | 120 |                 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 | 120 |                 sg->sc = sc; | 
| 271 | 120 |                 VTAILQ_INSERT_TAIL(&sc->segments, sg, list); | 
| 272 | 120 |                 sg2 = sg; | 
| 273 | 120 |                 if (sg1 == NULL) | 
| 274 | 120 |                         sg1 = sg; | 
| 275 | 120 |         } | 
| 276 | 120 |         printf("Dropped %d segments to make free_reserve\n", n); | 
| 277 | 120 |         return (0); | 
| 278 | 304 | } | 
| 279 |  |  | 
| 280 |  | /*-------------------------------------------------------------------- | 
| 281 |  |  * Silo worker thread | 
| 282 |  |  */ | 
| 283 |  |  | 
| 284 |  | static void * v_matchproto_(bgthread_t) | 
| 285 | 304 | smp_thread(struct worker *wrk, void *priv) | 
| 286 |  | { | 
| 287 |  |         struct smp_sc   *sc; | 
| 288 |  |         struct smp_seg *sg; | 
| 289 |  |  | 
| 290 | 304 |         CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); | 
| 291 | 304 |         CAST_OBJ_NOTNULL(sc, priv, SMP_SC_MAGIC); | 
| 292 | 304 |         sc->thread = pthread_self(); | 
| 293 |  |  | 
| 294 |  |         /* First, load all the objects from all segments */ | 
| 295 | 728 |         VTAILQ_FOREACH(sg, &sc->segments, list) | 
| 296 | 544 |                 if (sg->flags & SMP_SEG_MUSTLOAD) | 
| 297 | 120 |                         smp_load_seg(wrk, sc, sg); | 
| 298 |  |  | 
| 299 | 304 |         sc->flags |= SMP_SC_LOADED; | 
| 300 | 304 |         BAN_Release(); | 
| 301 | 304 |         printf("Silo completely loaded\n"); | 
| 302 |  |  | 
| 303 |  |         /* Housekeeping loop */ | 
| 304 | 304 |         Lck_Lock(&sc->mtx); | 
| 305 | 657 |         while (!(sc->flags & SMP_SC_STOP)) { | 
| 306 | 353 |                 sg = VTAILQ_FIRST(&sc->segments); | 
| 307 | 353 |                 if (sg != NULL && sg != sc->cur_seg && sg->nobj == 0) | 
| 308 | 14 |                         smp_save_segs(sc); | 
| 309 |  |  | 
| 310 | 353 |                 Lck_Unlock(&sc->mtx); | 
| 311 | 353 |                 VTIM_sleep(3.14159265359 - 2); | 
| 312 | 353 |                 Lck_Lock(&sc->mtx); | 
| 313 |  |         } | 
| 314 |  |  | 
| 315 | 304 |         smp_save_segs(sc); | 
| 316 |  |  | 
| 317 | 304 |         Lck_Unlock(&sc->mtx); | 
| 318 | 304 |         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 | 304 | smp_open(struct stevedore *st) | 
| 329 |  | { | 
| 330 |  |         struct smp_sc   *sc; | 
| 331 |  |  | 
| 332 | 304 |         ASSERT_CLI(); | 
| 333 |  |  | 
| 334 | 304 |         if (VTAILQ_EMPTY(&silos)) | 
| 335 | 232 |                 smp_init(); | 
| 336 |  |  | 
| 337 | 304 |         CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC); | 
| 338 |  |  | 
| 339 | 304 |         Lck_New(&sc->mtx, lck_smp); | 
| 340 | 304 |         Lck_Lock(&sc->mtx); | 
| 341 |  |  | 
| 342 | 304 |         sc->stevedore = st; | 
| 343 |  |  | 
| 344 |  |         /* We trust the parent to give us a valid silo, for good measure: */ | 
| 345 | 304 |         AZ(smp_valid_silo(sc)); | 
| 346 |  |  | 
| 347 | 304 |         AZ(mprotect((void*)sc->base, 4096, PROT_READ)); | 
| 348 |  |  | 
| 349 | 304 |         sc->ident = SIGN_DATA(&sc->idn); | 
| 350 |  |  | 
| 351 |  |         /* Check ban lists */ | 
| 352 | 304 |         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 | 304 |                 smp_copy_signspace(&sc->ban2, &sc->ban1); | 
| 360 | 304 |                 smp_sync_sign(&sc->ban2.ctx); | 
| 361 |  |         } | 
| 362 | 304 |         AZ(smp_open_bans(sc, &sc->ban1)); | 
| 363 |  |  | 
| 364 |  |         /* We attempt seg1 first, and if that fails, try seg2 */ | 
| 365 | 304 |         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 | 304 |         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 | 304 |         (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 | 304 |         smp_new_seg(sc); | 
| 384 |  |  | 
| 385 |  |         /* Start the worker silo worker thread, it will load the objects */ | 
| 386 | 304 |         WRK_BgThread(&sc->bgthread, "persistence", smp_thread, sc); | 
| 387 |  |  | 
| 388 | 304 |         VTAILQ_INSERT_TAIL(&silos, sc, list); | 
| 389 | 304 |         Lck_Unlock(&sc->mtx); | 
| 390 | 304 | } | 
| 391 |  |  | 
| 392 |  | /*-------------------------------------------------------------------- | 
| 393 |  |  * Close a silo | 
| 394 |  |  */ | 
| 395 |  |  | 
| 396 |  | static void v_matchproto_(storage_close_f) | 
| 397 | 592 | smp_close(const struct stevedore *st, int warn) | 
| 398 |  | { | 
| 399 |  |         struct smp_sc   *sc; | 
| 400 |  |         void *status; | 
| 401 |  |  | 
| 402 | 592 |         ASSERT_CLI(); | 
| 403 |  |  | 
| 404 | 592 |         CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC); | 
| 405 | 592 |         if (warn) { | 
| 406 | 296 |                 Lck_Lock(&sc->mtx); | 
| 407 | 296 |                 if (sc->cur_seg != NULL) | 
| 408 | 296 |                         smp_close_seg(sc, sc->cur_seg); | 
| 409 | 296 |                 AZ(sc->cur_seg); | 
| 410 | 296 |                 sc->flags |= SMP_SC_STOP; | 
| 411 | 296 |                 Lck_Unlock(&sc->mtx); | 
| 412 | 296 |         } else { | 
| 413 | 296 |                 PTOK(pthread_join(sc->bgthread, &status)); | 
| 414 | 296 |                 AZ(status); | 
| 415 |  |         } | 
| 416 | 592 | } | 
| 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 | 224 | 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 | 224 |         CAST_OBJ_NOTNULL(sc, st->priv, SMP_SC_MAGIC); | 
| 440 | 224 |         assert(min_size <= max_size); | 
| 441 |  |  | 
| 442 | 224 |         max_size = IRNUP(sc, max_size); | 
| 443 | 224 |         min_size = IRNUP(sc, min_size); | 
| 444 |  |  | 
| 445 | 224 |         extra = IRNUP(sc, sizeof(*ss)); | 
| 446 | 224 |         if (so != NULL) { | 
| 447 | 176 |                 extra += sizeof(**so); | 
| 448 | 176 |                 AN(idx); | 
| 449 | 176 |         } | 
| 450 |  |  | 
| 451 | 224 |         Lck_Lock(&sc->mtx); | 
| 452 | 224 |         sg = NULL; | 
| 453 | 224 |         ss = NULL; | 
| 454 |  |  | 
| 455 | 224 |         left = 0; | 
| 456 | 224 |         if (sc->cur_seg != NULL) | 
| 457 | 224 |                 left = smp_spaceleft(sc, sc->cur_seg); | 
| 458 | 224 |         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 | 224 |         if (left >= extra + min_size)  { | 
| 469 | 224 |                 AN(sc->cur_seg); | 
| 470 | 224 |                 if (left < extra + max_size) | 
| 471 | 0 |                         max_size = IRNDN(sc, left - extra); | 
| 472 |  |  | 
| 473 | 224 |                 sg = sc->cur_seg; | 
| 474 | 224 |                 ss = (void*)(sc->base + sc->next_bot); | 
| 475 | 224 |                 sc->next_bot += max_size + IRNUP(sc, sizeof(*ss)); | 
| 476 | 224 |                 sg->nalloc++; | 
| 477 | 224 |                 if (so != NULL) { | 
| 478 | 176 |                         sc->next_top -= sizeof(**so); | 
| 479 | 176 |                         *so = (void*)(sc->base + sc->next_top); | 
| 480 |  |                         /* Render this smp_object mostly harmless */ | 
| 481 | 176 |                         EXP_ZERO((*so)); | 
| 482 | 176 |                         (*so)->ban = 0.; | 
| 483 | 176 |                         (*so)->ptr = 0; | 
| 484 | 176 |                         sg->objs = *so; | 
| 485 | 176 |                         *idx = ++sg->p.lobjlist; | 
| 486 | 176 |                 } | 
| 487 | 224 |                 (void)smp_spaceleft(sc, sg);    /* for the assert */ | 
| 488 | 224 |         } | 
| 489 | 224 |         Lck_Unlock(&sc->mtx); | 
| 490 |  |  | 
| 491 | 224 |         if (ss == NULL) | 
| 492 | 0 |                 return (ss); | 
| 493 | 224 |         AN(sg); | 
| 494 | 224 |         assert(max_size >= min_size); | 
| 495 |  |  | 
| 496 |  |         /* Fill the storage structure */ | 
| 497 | 224 |         INIT_OBJ(ss, STORAGE_MAGIC); | 
| 498 | 224 |         ss->ptr = PRNUP(sc, ss + 1); | 
| 499 | 224 |         ss->space = max_size; | 
| 500 | 224 |         ss->priv = sc->base; | 
| 501 | 224 |         if (ssg != NULL) | 
| 502 | 176 |                 *ssg = sg; | 
| 503 | 224 |         return (ss); | 
| 504 | 224 | } | 
| 505 |  |  | 
| 506 |  | /*-------------------------------------------------------------------- | 
| 507 |  |  * Allocate an object | 
| 508 |  |  */ | 
| 509 |  |  | 
| 510 |  | static int v_matchproto_(storage_allocobj_f) | 
| 511 | 176 | 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 | 176 |         CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); | 
| 523 | 176 |         CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); | 
| 524 | 176 |         CAST_OBJ_NOTNULL(sc, stv->priv, SMP_SC_MAGIC); | 
| 525 |  |  | 
| 526 |  |         /* Don't entertain already dead objects */ | 
| 527 | 176 |         if (oc->flags & OC_F_DYING) | 
| 528 | 0 |                 return (0); | 
| 529 | 176 |         if (oc->t_origin <= 0.) | 
| 530 | 0 |                 return (0); | 
| 531 | 176 |         if (oc->ttl + oc->grace + oc->keep <= 0.) | 
| 532 | 0 |                 return (0); | 
| 533 |  |  | 
| 534 | 176 |         ltot = sizeof(struct object) + PRNDUP(wsl); | 
| 535 | 176 |         ltot = IRNUP(sc, ltot); | 
| 536 |  |  | 
| 537 | 176 |         st = NULL; | 
| 538 | 176 |         sg = NULL; | 
| 539 | 176 |         so = NULL; | 
| 540 | 176 |         objidx = 0; | 
| 541 |  |  | 
| 542 | 176 |         do { | 
| 543 | 176 |                 st = smp_allocx(stv, ltot, ltot, &so, &objidx, &sg); | 
| 544 | 176 |                 if (st != NULL && st->space < ltot) { | 
| 545 | 0 |                         stv->sml_free(st);              // NOP | 
| 546 | 0 |                         st = NULL; | 
| 547 | 0 |                 } | 
| 548 | 176 |         } while (st == NULL && LRU_NukeOne(wrk, stv->lru)); | 
| 549 | 176 |         if (st == NULL) | 
| 550 | 0 |                 return (0); | 
| 551 |  |  | 
| 552 | 176 |         AN(st); | 
| 553 | 176 |         AN(sg); | 
| 554 | 176 |         AN(so); | 
| 555 | 176 |         assert(st->space >= ltot); | 
| 556 |  |  | 
| 557 | 176 |         o = SML_MkObject(stv, oc, st->ptr); | 
| 558 | 176 |         AN(oc->stobj->stevedore); | 
| 559 | 176 |         assert(oc->stobj->stevedore == stv); | 
| 560 | 176 |         CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); | 
| 561 | 176 |         o->objstore = st; | 
| 562 | 176 |         st->len = sizeof(*o); | 
| 563 |  |  | 
| 564 | 176 |         Lck_Lock(&sc->mtx); | 
| 565 | 176 |         sg->nfixed++; | 
| 566 | 176 |         sg->nobj++; | 
| 567 |  |  | 
| 568 |  |         /* We have to do this somewhere, might as well be here... */ | 
| 569 | 176 |         assert(sizeof so->hash == DIGEST_LEN); | 
| 570 | 176 |         memcpy(so->hash, oc->objhead->digest, DIGEST_LEN); | 
| 571 | 176 |         EXP_COPY(so, oc); | 
| 572 | 176 |         so->ptr = (uint8_t*)(o->objstore) - sc->base; | 
| 573 | 176 |         so->ban = BAN_Time(oc->ban); | 
| 574 |  |  | 
| 575 | 176 |         smp_init_oc(oc, sg, objidx); | 
| 576 |  |  | 
| 577 | 176 |         VTAILQ_INSERT_TAIL(&sg->objcores, oc, lru_list); | 
| 578 | 176 |         Lck_Unlock(&sc->mtx); | 
| 579 | 176 |         return (1); | 
| 580 | 176 | } | 
| 581 |  |  | 
| 582 |  | /*-------------------------------------------------------------------- | 
| 583 |  |  * Allocate a bite | 
| 584 |  |  */ | 
| 585 |  |  | 
| 586 |  | static struct storage * v_matchproto_(sml_alloc_f) | 
| 587 | 48 | smp_alloc(const struct stevedore *st, size_t size) | 
| 588 |  | { | 
| 589 |  |  | 
| 590 | 96 |         return (smp_allocx(st, | 
| 591 | 48 |             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 | 24 | debug_report_silo(struct cli *cli, const struct smp_sc *sc) | 
| 619 |  | { | 
| 620 |  |         struct smp_seg *sg; | 
| 621 |  |  | 
| 622 | 48 |         VCLI_Out(cli, "Silo: %s (%s)\n", | 
| 623 | 24 |             sc->stevedore->ident, sc->filename); | 
| 624 | 56 |         VTAILQ_FOREACH(sg, &sc->segments, list) { | 
| 625 | 64 |                 VCLI_Out(cli, "  Seg: [0x%jx ... +0x%jx]\n", | 
| 626 | 32 |                    (uintmax_t)sg->p.offset, (uintmax_t)sg->p.length); | 
| 627 | 32 |                 if (sg == sc->cur_seg) | 
| 628 | 48 |                         VCLI_Out(cli, | 
| 629 |  |                            "    Alloc: [0x%jx ... 0x%jx] = 0x%jx free\n", | 
| 630 | 24 |                            (uintmax_t)(sc->next_bot), | 
| 631 | 24 |                            (uintmax_t)(sc->next_top), | 
| 632 | 24 |                            (uintmax_t)(sc->next_top - sc->next_bot)); | 
| 633 | 64 |                 VCLI_Out(cli, "    %u nobj, %u alloc, %u lobjlist, %u fixed\n", | 
| 634 | 32 |                     sg->nobj, sg->nalloc, sg->p.lobjlist, sg->nfixed); | 
| 635 | 32 |         } | 
| 636 | 24 | } | 
| 637 |  |  | 
| 638 |  | static void v_matchproto_(cli_func_t) | 
| 639 | 48 | debug_persistent(struct cli *cli, const char * const * av, void *priv) | 
| 640 |  | { | 
| 641 |  |         struct smp_sc *sc; | 
| 642 |  |  | 
| 643 | 48 |         (void)priv; | 
| 644 |  |  | 
| 645 | 48 |         if (av[2] == NULL) { | 
| 646 | 0 |                 VTAILQ_FOREACH(sc, &silos, list) | 
| 647 | 0 |                         debug_report_silo(cli, sc); | 
| 648 | 0 |                 return; | 
| 649 |  |         } | 
| 650 | 48 |         VTAILQ_FOREACH(sc, &silos, list) | 
| 651 | 48 |                 if (!strcmp(av[2], sc->stevedore->ident)) | 
| 652 | 48 |                         break; | 
| 653 | 48 |         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 | 48 |         if (av[3] == NULL) { | 
| 659 | 0 |                 debug_report_silo(cli, sc); | 
| 660 | 0 |                 return; | 
| 661 |  |         } | 
| 662 | 48 |         Lck_Lock(&sc->mtx); | 
| 663 | 48 |         if (!strcmp(av[3], "sync")) { | 
| 664 | 24 |                 if (sc->cur_seg != NULL) | 
| 665 | 24 |                         smp_close_seg(sc, sc->cur_seg); | 
| 666 | 24 |                 smp_new_seg(sc); | 
| 667 | 48 |         } else if (!strcmp(av[3], "dump")) { | 
| 668 | 24 |                 debug_report_silo(cli, sc); | 
| 669 | 24 |         } else { | 
| 670 | 0 |                 VCLI_Out(cli, "Unknown operation\n"); | 
| 671 | 0 |                 VCLI_SetResult(cli, CLIS_PARAM); | 
| 672 |  |         } | 
| 673 | 48 |         Lck_Unlock(&sc->mtx); | 
| 674 | 48 | } | 
| 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 | 232 | smp_init(void) | 
| 686 |  | { | 
| 687 | 232 |         lck_smp = Lck_CreateClass(NULL, "smp"); | 
| 688 | 232 |         CLI_AddFuncs(debug_cmds); | 
| 689 | 232 |         smp_oc_realmethods.objfree = SML_methods.objfree; | 
| 690 | 232 |         smp_oc_realmethods.objiterator = SML_methods.objiterator; | 
| 691 | 232 |         smp_oc_realmethods.objgetspace = SML_methods.objgetspace; | 
| 692 | 232 |         smp_oc_realmethods.objextend = SML_methods.objextend; | 
| 693 | 232 |         smp_oc_realmethods.objbocdone = SML_methods.objbocdone; | 
| 694 | 232 |         smp_oc_realmethods.objgetattr = SML_methods.objgetattr; | 
| 695 | 232 |         smp_oc_realmethods.objsetattr = SML_methods.objsetattr; | 
| 696 | 232 |         smp_oc_realmethods.objtouch = LRU_Touch; | 
| 697 | 232 |         smp_oc_realmethods.objfree = smp_oc_objfree; | 
| 698 | 232 |         smp_oc_realmethods.vai_init     = SML_methods.vai_init; | 
| 699 | 232 | } | 
| 700 |  |  | 
| 701 |  | /*-------------------------------------------------------------------- | 
| 702 |  |  * Pause until all silos have loaded. | 
| 703 |  |  */ | 
| 704 |  |  | 
| 705 |  | void | 
| 706 | 192 | SMP_Ready(void) | 
| 707 |  | { | 
| 708 |  |         struct smp_sc *sc; | 
| 709 |  |  | 
| 710 | 192 |         ASSERT_CLI(); | 
| 711 | 192 |         do { | 
| 712 | 458 |                 VTAILQ_FOREACH(sc, &silos, list) | 
| 713 | 266 |                         if (!(sc->flags & SMP_SC_LOADED)) | 
| 714 | 1 |                                 break; | 
| 715 | 193 |                 if (sc != NULL) | 
| 716 | 1 |                         (void)sleep(1); | 
| 717 | 193 |         } while (sc != NULL); | 
| 718 | 192 | } |