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