1 /*-
2  * Copyright (c) 2007-2015 Varnish Software AS
3  * All rights reserved.
4  *
5  * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 
32 #include "config.h"
33 
34 #include "cache/cache_varnishd.h"
35 
36 #include "cache/cache_obj.h"
37 #include "cache/cache_objhead.h"
38 
39 #include "storage/storage.h"
40 #include "storage/storage_simple.h"
41 
42 #include "vtim.h"
43 
44 /* Flags for allocating memory in sml_stv_alloc */
45 #define LESS_MEM_ALLOCED_IS_OK	1
46 
47 /*-------------------------------------------------------------------*/
48 
49 static struct storage *
sml_stv_alloc(const struct stevedore * stv,ssize_t size,int flags)50 sml_stv_alloc(const struct stevedore *stv, ssize_t size, int flags)
51 {
52 	struct storage *st;
53 
54 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
55 	AN(stv->sml_alloc);
56 
57 	if (!(flags & LESS_MEM_ALLOCED_IS_OK)) {
58 		if (size > cache_param->fetch_maxchunksize)
59 			return (NULL);
60 		else
61 			return (stv->sml_alloc(stv, size));
62 	}
63 
64 	if (size > cache_param->fetch_maxchunksize)
65 		size = cache_param->fetch_maxchunksize;
66 
67 	assert(size <= UINT_MAX);	/* field limit in struct storage */
68 
69 	for (;;) {
70 		/* try to allocate from it */
71 		assert(size > 0);
72 		st = stv->sml_alloc(stv, size);
73 		if (st != NULL)
74 			break;
75 
76 		if (size <= cache_param->fetch_chunksize)
77 			break;
78 
79 		size /= 2;
80 	}
81 	CHECK_OBJ_ORNULL(st, STORAGE_MAGIC);
82 	return (st);
83 }
84 
85 static void
sml_stv_free(const struct stevedore * stv,struct storage * st)86 sml_stv_free(const struct stevedore *stv, struct storage *st)
87 {
88 
89 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
90 	CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
91 	if (stv->sml_free != NULL)
92 		stv->sml_free(st);
93 }
94 
95 /*--------------------------------------------------------------------
96  * This function is called by stevedores ->allocobj() method, which
97  * very often will be SML_allocobj() below, to convert a slab
98  * of storage into object which the stevedore can then register in its
99  * internal state, before returning it to STV_NewObject().
100  * As you probably guessed: All this for persistence.
101  */
102 
103 struct object *
SML_MkObject(const struct stevedore * stv,struct objcore * oc,void * ptr)104 SML_MkObject(const struct stevedore *stv, struct objcore *oc, void *ptr)
105 {
106 	struct object *o;
107 
108 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
109 	AN(stv->methods);
110 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
111 
112 	assert(PAOK(ptr));
113 
114 	o = ptr;
115 	INIT_OBJ(o, OBJECT_MAGIC);
116 
117 	VTAILQ_INIT(&o->list);
118 
119 	oc->stobj->stevedore = stv;
120 	oc->stobj->priv = o;
121 	oc->stobj->priv2 = 0;
122 	return (o);
123 }
124 
125 /*--------------------------------------------------------------------
126  * This is the default ->allocobj() which all stevedores who do not
127  * implement persistent storage can rely on.
128  */
129 
v_matchproto_(storage_allocobj_f)130 int v_matchproto_(storage_allocobj_f)
131 SML_allocobj(struct worker *wrk, const struct stevedore *stv,
132     struct objcore *oc, unsigned wsl)
133 {
134 	struct object *o;
135 	struct storage *st = NULL;
136 	unsigned ltot;
137 
138 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
139 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
140 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
141 
142 	AN(stv->sml_alloc);
143 
144 	ltot = sizeof(struct object) + PRNDUP(wsl);
145 
146 	do {
147 		st = stv->sml_alloc(stv, ltot);
148 		if (st != NULL && st->space < ltot) {
149 			stv->sml_free(st);
150 			st = NULL;
151 		}
152 	} while (st == NULL && LRU_NukeOne(wrk, stv->lru));
153 	if (st == NULL)
154 		return (0);
155 
156 	CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
157 	o = SML_MkObject(stv, oc, st->ptr);
158 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
159 	st->len = sizeof(*o);
160 	o->objstore = st;
161 	return (1);
162 }
163 
164 /*---------------------------------------------------------------------
165  */
166 
167 static struct object *
sml_getobj(struct worker * wrk,struct objcore * oc)168 sml_getobj(struct worker *wrk, struct objcore *oc)
169 {
170 	const struct stevedore *stv;
171 	struct object *o;
172 
173 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
174 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
175 	stv = oc->stobj->stevedore;
176 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
177 	if (stv->sml_getobj != NULL)
178 		return (stv->sml_getobj(wrk, oc));
179 	if (oc->stobj->priv == NULL)
180 		return (NULL);
181 	CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
182 	return (o);
183 }
184 
v_matchproto_(objslim_f)185 static void v_matchproto_(objslim_f)
186 sml_slim(struct worker *wrk, struct objcore *oc)
187 {
188 	const struct stevedore *stv;
189 	struct object *o;
190 	struct storage *st, *stn;
191 
192 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
193 
194 	stv = oc->stobj->stevedore;
195 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
196 	o = sml_getobj(wrk, oc);
197 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
198 
199 #define OBJ_AUXATTR(U, l)						\
200 	if (o->aa_##l != NULL) {					\
201 		sml_stv_free(stv, o->aa_##l);				\
202 		o->aa_##l = NULL;					\
203 	}
204 #include "tbl/obj_attr.h"
205 
206 	VTAILQ_FOREACH_SAFE(st, &o->list, list, stn) {
207 		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
208 		VTAILQ_REMOVE(&o->list, st, list);
209 		sml_stv_free(stv, st);
210 	}
211 }
212 
v_matchproto_(objfree_f)213 static void v_matchproto_(objfree_f)
214 sml_objfree(struct worker *wrk, struct objcore *oc)
215 {
216 	struct object *o;
217 
218 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
219 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
220 	sml_slim(wrk, oc);
221 	CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
222 	o->magic = 0;
223 
224 	if (oc->boc == NULL && oc->stobj->stevedore->lru != NULL)
225 		LRU_Remove(oc);
226 
227 	sml_stv_free(oc->stobj->stevedore, o->objstore);
228 
229 	memset(oc->stobj, 0, sizeof oc->stobj);
230 
231 	wrk->stats->n_object--;
232 }
233 
v_matchproto_(objiterate_f)234 static int v_matchproto_(objiterate_f)
235 sml_iterator(struct worker *wrk, struct objcore *oc,
236     void *priv, objiterate_f *func, int final)
237 {
238 	struct boc *boc;
239 	enum boc_state_e state;
240 	struct object *obj;
241 	struct storage *st;
242 	struct storage *checkpoint = NULL;
243 	const struct stevedore *stv;
244 	ssize_t checkpoint_len = 0;
245 	ssize_t len = 0;
246 	int ret = 0, ret2;
247 	ssize_t ol;
248 	ssize_t nl;
249 	ssize_t sl;
250 	void *p;
251 	ssize_t l;
252 	unsigned u;
253 
254 	obj = sml_getobj(wrk, oc);
255 	CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
256 	stv = oc->stobj->stevedore;
257 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
258 
259 	boc = HSH_RefBoc(oc);
260 
261 	if (boc == NULL) {
262 		VTAILQ_FOREACH_SAFE(st, &obj->list, list, checkpoint) {
263 			u = 0;
264 			if (VTAILQ_NEXT(st, list) == NULL)
265 				u |= OBJ_ITER_END;
266 			if (final)
267 				u |= OBJ_ITER_FLUSH;
268 			if (ret == 0 && st->len > 0)
269 				ret = func(priv, u, st->ptr, st->len);
270 			if (final) {
271 				VTAILQ_REMOVE(&obj->list, st, list);
272 				sml_stv_free(stv, st);
273 			} else if (ret)
274 				break;
275 		}
276 		return (ret);
277 	}
278 
279 	p = NULL;
280 	l = 0;
281 
282 	u = 0;
283 	while (1) {
284 		ol = len;
285 		nl = ObjWaitExtend(wrk, oc, ol);
286 		if (boc->state == BOS_FAILED) {
287 			ret = -1;
288 			break;
289 		}
290 		if (nl == ol) {
291 			if (boc->state == BOS_FINISHED)
292 				break;
293 			continue;
294 		}
295 		Lck_Lock(&boc->mtx);
296 		AZ(VTAILQ_EMPTY(&obj->list));
297 		if (checkpoint == NULL) {
298 			st = VTAILQ_FIRST(&obj->list);
299 			sl = 0;
300 		} else {
301 			st = checkpoint;
302 			sl = checkpoint_len;
303 			ol -= checkpoint_len;
304 		}
305 		while (st != NULL) {
306 			if (st->len > ol) {
307 				p = st->ptr + ol;
308 				l = st->len - ol;
309 				len += l;
310 				break;
311 			}
312 			ol -= st->len;
313 			assert(ol >= 0);
314 			nl -= st->len;
315 			assert(nl > 0);
316 			sl += st->len;
317 			st = VTAILQ_NEXT(st, list);
318 			if (VTAILQ_NEXT(st, list) != NULL) {
319 				if (final && checkpoint != NULL) {
320 					VTAILQ_REMOVE(&obj->list,
321 					    checkpoint, list);
322 					sml_stv_free(stv, checkpoint);
323 				}
324 				checkpoint = st;
325 				checkpoint_len = sl;
326 			}
327 		}
328 		CHECK_OBJ_NOTNULL(obj, OBJECT_MAGIC);
329 		CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
330 		st = VTAILQ_NEXT(st, list);
331 		if (st != NULL && st->len == 0)
332 			st = NULL;
333 		state = boc->state;
334 		Lck_Unlock(&boc->mtx);
335 		assert(l > 0 || state == BOS_FINISHED);
336 		u = 0;
337 		if (st == NULL || final)
338 			u |= OBJ_ITER_FLUSH;
339 		if (st == NULL && state == BOS_FINISHED)
340 			u |= OBJ_ITER_END;
341 		ret = func(priv, u, p, l);
342 		if (ret)
343 			break;
344 	}
345 	HSH_DerefBoc(wrk, oc);
346 	if ((u & OBJ_ITER_END) == 0) {
347 		ret2 = func(priv, OBJ_ITER_END, NULL, 0);
348 		if (ret == 0)
349 			ret = ret2;
350 	}
351 	return (ret);
352 }
353 
354 /*--------------------------------------------------------------------
355  */
356 
357 static struct storage *
objallocwithnuke(struct worker * wrk,const struct stevedore * stv,ssize_t size,int flags)358 objallocwithnuke(struct worker *wrk, const struct stevedore *stv, ssize_t size,
359     int flags)
360 {
361 	struct storage *st = NULL;
362 
363 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
364 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
365 
366 	if (size > cache_param->fetch_maxchunksize) {
367 		if (!(flags & LESS_MEM_ALLOCED_IS_OK))
368 			return (NULL);
369 		size = cache_param->fetch_maxchunksize;
370 	}
371 
372 	assert(size <= UINT_MAX);	/* field limit in struct storage */
373 
374 	do {
375 		/* try to allocate from it */
376 		st = sml_stv_alloc(stv, size, flags);
377 		if (st != NULL)
378 			break;
379 
380 		/* no luck; try to free some space and keep trying */
381 		if (stv->lru == NULL)
382 			break;
383 	} while (LRU_NukeOne(wrk, stv->lru));
384 
385 	CHECK_OBJ_ORNULL(st, STORAGE_MAGIC);
386 	return (st);
387 }
388 
v_matchproto_(objgetspace_f)389 static int v_matchproto_(objgetspace_f)
390 sml_getspace(struct worker *wrk, struct objcore *oc, ssize_t *sz,
391     uint8_t **ptr)
392 {
393 	struct object *o;
394 	struct storage *st;
395 
396 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
397 	AN(sz);
398 	AN(ptr);
399 	assert(*sz > 0);
400 
401 	o = sml_getobj(wrk, oc);
402 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
403 	CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
404 
405 	st = VTAILQ_LAST(&o->list, storagehead);
406 	if (st != NULL && st->len < st->space) {
407 		*sz = st->space - st->len;
408 		*ptr = st->ptr + st->len;
409 		assert (*sz > 0);
410 		return (1);
411 	}
412 
413 	st = objallocwithnuke(wrk, oc->stobj->stevedore, *sz,
414 	    LESS_MEM_ALLOCED_IS_OK);
415 	if (st == NULL)
416 		return (0);
417 
418 	CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
419 	Lck_Lock(&oc->boc->mtx);
420 	VTAILQ_INSERT_TAIL(&o->list, st, list);
421 	Lck_Unlock(&oc->boc->mtx);
422 
423 	*sz = st->space - st->len;
424 	assert (*sz > 0);
425 	*ptr = st->ptr + st->len;
426 	return (1);
427 }
428 
v_matchproto_(objextend_f)429 static void v_matchproto_(objextend_f)
430 sml_extend(struct worker *wrk, struct objcore *oc, ssize_t l)
431 {
432 	struct object *o;
433 	struct storage *st;
434 
435 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
436 	assert(l > 0);
437 
438 	o = sml_getobj(wrk, oc);
439 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
440 	st = VTAILQ_LAST(&o->list, storagehead);
441 	CHECK_OBJ_NOTNULL(st, STORAGE_MAGIC);
442 	assert(st->len + l <= st->space);
443 	st->len += l;
444 }
445 
v_matchproto_(objtrimstore_f)446 static void v_matchproto_(objtrimstore_f)
447 sml_trimstore(struct worker *wrk, struct objcore *oc)
448 {
449 	const struct stevedore *stv;
450 	struct storage *st, *st1;
451 	struct object *o;
452 
453 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
454 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
455 	CHECK_OBJ_NOTNULL(oc->boc, BOC_MAGIC);
456 
457 	stv = oc->stobj->stevedore;
458 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
459 
460 	if (stv->sml_free == NULL)
461 		return;
462 
463 	o = sml_getobj(wrk, oc);
464 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
465 	st = VTAILQ_LAST(&o->list, storagehead);
466 
467 	if (st == NULL)
468 		return;
469 
470 	if (st->len == 0) {
471 		Lck_Lock(&oc->boc->mtx);
472 		VTAILQ_REMOVE(&o->list, st, list);
473 		Lck_Unlock(&oc->boc->mtx);
474 		sml_stv_free(stv, st);
475 		return;
476 	}
477 
478 	if (st->space - st->len < 512)
479 		return;
480 
481 	st1 = sml_stv_alloc(stv, st->len, 0);
482 	if (st1 == NULL)
483 		return;
484 	assert(st1->space >= st->len);
485 
486 	memcpy(st1->ptr, st->ptr, st->len);
487 	st1->len = st->len;
488 	Lck_Lock(&oc->boc->mtx);
489 	VTAILQ_REMOVE(&o->list, st, list);
490 	VTAILQ_INSERT_TAIL(&o->list, st1, list);
491 	Lck_Unlock(&oc->boc->mtx);
492 	/* sml_bocdone frees this */
493 	AZ(oc->boc->stevedore_priv);
494 	oc->boc->stevedore_priv = st;
495 }
496 
v_matchproto_(objbocdone_f)497 static void v_matchproto_(objbocdone_f)
498 sml_bocdone(struct worker *wrk, struct objcore *oc, struct boc *boc)
499 {
500 	const struct stevedore *stv;
501 	struct storage *st;
502 
503 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
504 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
505 	CHECK_OBJ_NOTNULL(boc, BOC_MAGIC);
506 	stv = oc->stobj->stevedore;
507 	CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
508 
509 	if (boc->stevedore_priv != NULL) {
510 		/* Free any leftovers from Trim */
511 		CAST_OBJ_NOTNULL(st, boc->stevedore_priv, STORAGE_MAGIC);
512 		boc->stevedore_priv = NULL;
513 		sml_stv_free(stv, st);
514 	}
515 
516 	if (stv->lru != NULL) {
517 		if (isnan(wrk->lastused))
518 			wrk->lastused = VTIM_real();
519 		LRU_Add(oc, wrk->lastused);	// approx timestamp is OK
520 	}
521 }
522 
v_matchproto_(objgetattr_f)523 static const void * v_matchproto_(objgetattr_f)
524 sml_getattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
525    ssize_t *len)
526 {
527 	struct object *o;
528 	ssize_t dummy;
529 
530 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
531 
532 	if (len == NULL)
533 		len = &dummy;
534 	o = sml_getobj(wrk, oc);
535 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
536 
537 	switch (attr) {
538 		/* Fixed size attributes */
539 #define OBJ_FIXATTR(U, l, s)						\
540 	case OA_##U:							\
541 		*len = sizeof o->fa_##l;				\
542 		return (o->fa_##l);
543 #include "tbl/obj_attr.h"
544 
545 		/* Variable size attributes */
546 #define OBJ_VARATTR(U, l)						\
547 	case OA_##U:							\
548 		if (o->va_##l == NULL)					\
549 			return (NULL);					\
550 		*len = o->va_##l##_len;					\
551 		return (o->va_##l);
552 #include "tbl/obj_attr.h"
553 
554 		/* Auxiliary attributes */
555 #define OBJ_AUXATTR(U, l)						\
556 	case OA_##U:							\
557 		if (o->aa_##l == NULL)					\
558 			return (NULL);					\
559 		CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC);		\
560 		*len = o->aa_##l->len;					\
561 		return (o->aa_##l->ptr);
562 #include "tbl/obj_attr.h"
563 
564 	default:
565 		break;
566 	}
567 	WRONG("Unsupported OBJ_ATTR");
568 }
569 
v_matchproto_(objsetattr_f)570 static void * v_matchproto_(objsetattr_f)
571 sml_setattr(struct worker *wrk, struct objcore *oc, enum obj_attr attr,
572     ssize_t len, const void *ptr)
573 {
574 	struct object *o;
575 	void *retval = NULL;
576 	struct storage *st;
577 
578 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
579 
580 	o = sml_getobj(wrk, oc);
581 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
582 	st = o->objstore;
583 
584 	switch (attr) {
585 		/* Fixed size attributes */
586 #define OBJ_FIXATTR(U, l, s)						\
587 	case OA_##U:							\
588 		assert(len == sizeof o->fa_##l);			\
589 		retval = o->fa_##l;					\
590 		break;
591 #include "tbl/obj_attr.h"
592 
593 		/* Variable size attributes */
594 #define OBJ_VARATTR(U, l)						\
595 	case OA_##U:							\
596 		if (o->va_##l##_len > 0) {				\
597 			AN(o->va_##l);					\
598 			assert(len == o->va_##l##_len);			\
599 			retval = o->va_##l;				\
600 		} else if (len > 0) {					\
601 			assert(len <= UINT_MAX);			\
602 			assert(st->len + len <= st->space);		\
603 			o->va_##l = st->ptr + st->len;			\
604 			st->len += len;					\
605 			o->va_##l##_len = len;				\
606 			retval = o->va_##l;				\
607 		}							\
608 		break;
609 #include "tbl/obj_attr.h"
610 
611 		/* Auxiliary attributes */
612 #define OBJ_AUXATTR(U, l)						\
613 	case OA_##U:							\
614 		if (o->aa_##l != NULL) {				\
615 			CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC);	\
616 			assert(len == o->aa_##l->len);			\
617 			retval = o->aa_##l->ptr;			\
618 			break;						\
619 		}							\
620 		if (len == 0)						\
621 			break;						\
622 		o->aa_##l = objallocwithnuke(wrk, oc->stobj->stevedore,	\
623 		    len, 0);						\
624 		if (o->aa_##l == NULL)					\
625 			break;						\
626 		CHECK_OBJ_NOTNULL(o->aa_##l, STORAGE_MAGIC);		\
627 		assert(len <= o->aa_##l->space);			\
628 		o->aa_##l->len = len;					\
629 		retval = o->aa_##l->ptr;				\
630 		break;
631 #include "tbl/obj_attr.h"
632 
633 	default:
634 		WRONG("Unsupported OBJ_ATTR");
635 		break;
636 	}
637 
638 	if (retval != NULL && ptr != NULL)
639 		memcpy(retval, ptr, len);
640 	return (retval);
641 }
642 
643 const struct obj_methods SML_methods = {
644 	.objfree	= sml_objfree,
645 	.objiterator	= sml_iterator,
646 	.objgetspace	= sml_getspace,
647 	.objextend	= sml_extend,
648 	.objtrimstore	= sml_trimstore,
649 	.objbocdone	= sml_bocdone,
650 	.objslim	= sml_slim,
651 	.objgetattr	= sml_getattr,
652 	.objsetattr	= sml_setattr,
653 	.objtouch	= LRU_Touch,
654 };
655 
656 static void
sml_panic_st(struct vsb * vsb,const char * hd,const struct storage * st)657 sml_panic_st(struct vsb *vsb, const char *hd, const struct storage *st)
658 {
659 	VSB_printf(vsb, "%s = %p {priv=%p, ptr=%p, len=%u, space=%u},\n",
660 	    hd, st, st->priv, st->ptr, st->len, st->space);
661 }
662 
663 void
SML_panic(struct vsb * vsb,const struct objcore * oc)664 SML_panic(struct vsb *vsb, const struct objcore *oc)
665 {
666 	struct object *o;
667 	struct storage *st;
668 
669 	VSB_printf(vsb, "Simple = %p,\n", oc->stobj->priv);
670 	if (oc->stobj->priv == NULL)
671 		return;
672 	CAST_OBJ_NOTNULL(o, oc->stobj->priv, OBJECT_MAGIC);
673 	sml_panic_st(vsb, "Obj", o->objstore);
674 
675 #define OBJ_FIXATTR(U, l, sz) \
676 	VSB_printf(vsb, "%s = ", #U); \
677 	VSB_quote(vsb, (const void*)o->fa_##l, sz, VSB_QUOTE_HEX); \
678 	VSB_printf(vsb, ",\n");
679 
680 #define OBJ_VARATTR(U, l) \
681 	VSB_printf(vsb, "%s = {len=%u, ptr=%p},\n", \
682 	    #U, o->va_##l##_len, o->va_##l);
683 
684 #define OBJ_AUXATTR(U, l) \
685 	if (o->aa_##l != NULL) sml_panic_st(vsb, #U, o->aa_##l);
686 
687 #include "tbl/obj_attr.h"
688 
689 	VTAILQ_FOREACH(st, &o->list, list) {
690 		sml_panic_st(vsb, "Body", st);
691 	}
692 }
693