xref: /spdk/test/unit/lib/ftl/ftl_sb/ftl_sb_ut.c (revision b02581a89058ebaebe03bd0e16e3b58adfe406c1)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2022 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include <sys/queue.h>
7 
8 #include "spdk/stdinc.h"
9 #include "spdk_internal/cunit.h"
10 #include "common/lib/test_env.c"
11 
12 #include "ftl/utils/ftl_layout_tracker_bdev.c"
13 #include "ftl/upgrade/ftl_sb_v3.c"
14 #include "ftl/upgrade/ftl_sb_v5.c"
15 #include "ftl/ftl_sb.c"
16 #include "ftl/ftl_layout.c"
17 #include "ftl/upgrade/ftl_sb_upgrade.c"
18 
19 static struct ftl_layout_upgrade_desc_list layout_upgrade_desc[] = {
20 	[FTL_LAYOUT_REGION_TYPE_SB] = {
21 		.latest_ver = FTL_SB_VERSION_CURRENT,
22 		.count = FTL_SB_VERSION_CURRENT,
23 	},
24 	[FTL_LAYOUT_REGION_TYPE_SB_BASE] = {
25 		.latest_ver = FTL_SB_VERSION_CURRENT,
26 		.count = FTL_SB_VERSION_CURRENT,
27 	},
28 	[FTL_LAYOUT_REGION_TYPE_L2P] = {},
29 	[FTL_LAYOUT_REGION_TYPE_BAND_MD] = {
30 		.latest_ver = FTL_BAND_VERSION_CURRENT,
31 		.count = FTL_BAND_VERSION_CURRENT,
32 	},
33 	[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR] = {
34 		.latest_ver = FTL_BAND_VERSION_CURRENT,
35 		.count = FTL_BAND_VERSION_CURRENT,
36 	},
37 	[FTL_LAYOUT_REGION_TYPE_VALID_MAP] = {},
38 	[FTL_LAYOUT_REGION_TYPE_NVC_MD] = {
39 		.latest_ver = FTL_NVC_VERSION_CURRENT,
40 		.count = FTL_NVC_VERSION_CURRENT,
41 	},
42 	[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR] = {
43 		.latest_ver = FTL_NVC_VERSION_CURRENT,
44 		.count = FTL_NVC_VERSION_CURRENT,
45 	},
46 	[FTL_LAYOUT_REGION_TYPE_DATA_NVC] = {},
47 	[FTL_LAYOUT_REGION_TYPE_DATA_BASE] = {},
48 	[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC] = {
49 		.latest_ver = FTL_P2L_VERSION_CURRENT,
50 		.count = FTL_P2L_VERSION_CURRENT,
51 	},
52 	[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT] = {
53 		.latest_ver = FTL_P2L_VERSION_CURRENT,
54 		.count = FTL_P2L_VERSION_CURRENT,
55 	},
56 	[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP] = {
57 		.latest_ver = FTL_P2L_VERSION_CURRENT,
58 		.count = FTL_P2L_VERSION_CURRENT,
59 	},
60 	[FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT] = {
61 		.latest_ver = FTL_P2L_VERSION_CURRENT,
62 		.count = FTL_P2L_VERSION_CURRENT,
63 	},
64 	[FTL_LAYOUT_REGION_TYPE_TRIM_MD] = {},
65 	[FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = {},
66 };
67 
68 #include "ftl/upgrade/ftl_layout_upgrade.c"
69 #include "ftl/mngt/ftl_mngt_md.c"
70 
71 DEFINE_STUB_V(ftl_mngt_fail_step, (struct ftl_mngt_process *mngt));
72 DEFINE_STUB_V(ftl_mngt_next_step, (struct ftl_mngt_process *mngt));
73 DEFINE_STUB_V(ftl_md_persist, (struct ftl_md *md));
74 DEFINE_STUB(ftl_nv_cache_load_state, int, (struct ftl_nv_cache *nv_cache), 0);
75 DEFINE_STUB_V(ftl_valid_map_load_state, (struct spdk_ftl_dev *dev));
76 DEFINE_STUB_V(ftl_bands_load_state, (struct spdk_ftl_dev *dev));
77 DEFINE_STUB(ftl_md_get_region, const struct ftl_layout_region *, (struct ftl_md *md), 0);
78 DEFINE_STUB_V(ftl_md_restore, (struct ftl_md *md));
79 DEFINE_STUB(ftl_nv_cache_save_state, int, (struct ftl_nv_cache *nv_cache), 0);
80 DEFINE_STUB(ftl_mngt_get_step_ctx, void *, (struct ftl_mngt_process *mngt), 0);
81 DEFINE_STUB_V(ftl_mngt_persist_bands_p2l, (struct ftl_mngt_process *mngt));
82 DEFINE_STUB_V(ftl_band_init_gc_iter, (struct spdk_ftl_dev *dev));
83 DEFINE_STUB(ftl_md_create_region_flags, int, (struct spdk_ftl_dev *dev, int region_type), 0);
84 DEFINE_STUB(ftl_md_create, struct ftl_md *, (struct spdk_ftl_dev *dev, uint64_t blocks,
85 		uint64_t vss_blksz, const char *name, int flags, const struct ftl_layout_region *region), NULL);
86 DEFINE_STUB(ftl_md_destroy_region_flags, int, (struct spdk_ftl_dev *dev, int region_type), 0);
87 DEFINE_STUB(ftl_md_destroy_shm_flags, int, (struct spdk_ftl_dev *dev), 0);
88 DEFINE_STUB_V(ftl_md_destroy, (struct ftl_md *md, int flags));
89 DEFINE_STUB_V(ftl_mngt_call_process, (struct ftl_mngt_process *mngt,
90 				      const struct ftl_mngt_process_desc *process));
91 DEFINE_STUB(ftl_md_get_buffer, void *, (struct ftl_md *md), NULL);
92 DEFINE_STUB(spdk_bdev_desc_get_bdev, struct spdk_bdev *, (struct spdk_bdev_desc *desc), NULL);
93 DEFINE_STUB(spdk_bdev_get_write_unit_size, uint32_t, (const struct spdk_bdev *bdev), 0);
94 DEFINE_STUB(spdk_bdev_get_num_blocks, uint64_t, (const struct spdk_bdev *bdev), 0);
95 DEFINE_STUB(ftl_nv_cache_chunk_tail_md_num_blocks, size_t, (const struct ftl_nv_cache *nv_cache),
96 	    0);
97 DEFINE_STUB(ftl_band_user_blocks, size_t, (const struct ftl_band *band), 0);
98 
99 struct spdk_bdev_desc {
100 	int dummy;
101 };
102 
103 struct spdk_ftl_dev g_dev;
104 struct ftl_superblock_shm g_sb_shm = {0};
105 struct ftl_base_device_type g_base_type = { .name = "base_dev" };
106 struct ftl_nv_cache_device_desc g_nvc_desc = { .name = "nvc_dev" };
107 struct spdk_bdev_desc g_base_bdev_desc = {0};
108 struct spdk_bdev_desc g_nvc_bdev_desc = {0};
109 static uint8_t g_sb_buf[FTL_SUPERBLOCK_SIZE] = {0};
110 
111 struct ftl_region_upgrade_desc p2l_upgrade_desc[0];
112 struct ftl_region_upgrade_desc nvc_upgrade_desc[0];
113 struct ftl_region_upgrade_desc band_upgrade_desc[0];
114 
115 #define TEST_OP 0x1984
116 #define TEST_REG_BLKS 0x10000
117 #define TEST_NVC_BLKS 0x1000000;
118 #define TEST_BASE_BLKS 0x1000000000;
119 
120 static int
121 test_setup(void)
122 {
123 	int regno_nvc = 0, regno_base = 0, *regno_dev;
124 
125 	/* setup a dummy dev: */
126 	g_dev.sb = (void *)g_sb_buf;
127 	g_dev.sb_shm = &g_sb_shm;
128 	g_dev.conf.overprovisioning = TEST_OP;
129 	for (uint64_t n = 0; n < sizeof(g_dev.conf.uuid); n++) {
130 		g_dev.conf.uuid.u.raw[n] = n;
131 	}
132 
133 	g_dev.layout.nvc.total_blocks = TEST_NVC_BLKS;
134 	g_dev.layout.base.total_blocks = TEST_BASE_BLKS;
135 	g_dev.base_type = &g_base_type;
136 	g_dev.nv_cache.nvc_desc = &g_nvc_desc;
137 	g_dev.base_layout_tracker = ftl_layout_tracker_bdev_init(UINT32_MAX);
138 	g_dev.nvc_layout_tracker = ftl_layout_tracker_bdev_init(UINT32_MAX);
139 	g_dev.base_bdev_desc = &g_base_bdev_desc;
140 	g_dev.nv_cache.bdev_desc = &g_nvc_bdev_desc;
141 
142 	for (int regno = 0; regno < FTL_LAYOUT_REGION_TYPE_MAX; regno++) {
143 		struct ftl_layout_region *reg = &g_dev.layout.region[regno];
144 
145 		reg->current.blocks = TEST_REG_BLKS;
146 		regno_dev = sb_v3_md_region_is_nvc(regno) ? &regno_nvc : &regno_base;
147 		reg->current.offset = *regno_dev * TEST_REG_BLKS;
148 		(*regno_dev)++;
149 		reg->current.version = ftl_layout_upgrade_region_get_latest_version(regno);
150 		reg->type = regno;
151 		reg->name = "region_test";
152 		reg->bdev_desc = sb_v3_md_region_is_nvc(regno) ? &g_nvc_bdev_desc : &g_base_bdev_desc;
153 		reg->ioch = 0;
154 	}
155 	return 0;
156 }
157 
158 static int
159 test_teardown(void)
160 {
161 	if (g_dev.base_layout_tracker) {
162 		ftl_layout_tracker_bdev_fini(g_dev.base_layout_tracker);
163 		g_dev.base_layout_tracker = NULL;
164 	}
165 	if (g_dev.nvc_layout_tracker) {
166 		ftl_layout_tracker_bdev_fini(g_dev.nvc_layout_tracker);
167 		g_dev.nvc_layout_tracker = NULL;
168 	}
169 	return 0;
170 }
171 
172 static void
173 test_setup_sb_ver(uint64_t ver, uint64_t clean)
174 {
175 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
176 	uint64_t zero_offs;
177 
178 	memset(&g_sb_buf, 0, sizeof(g_sb_buf));
179 	ftl_mngt_init_default_sb(&g_dev, NULL);
180 	if (ver <= FTL_SB_VERSION_3) {
181 		sb->header.magic = FTL_SUPERBLOCK_MAGIC_V2;
182 	}
183 	sb->header.version = ver;
184 
185 	switch (ver) {
186 	case FTL_SB_VERSION_0:
187 	case FTL_SB_VERSION_1:
188 	case FTL_SB_VERSION_2:
189 		zero_offs = sizeof(struct ftl_superblock_v2);
190 		memset(g_sb_buf + zero_offs, 0, sizeof(g_sb_buf) - zero_offs);
191 		sb->v2.clean = clean;
192 		break;
193 
194 	case FTL_SB_VERSION_3:
195 	case FTL_SB_VERSION_4:
196 		zero_offs = sizeof(struct ftl_superblock_v3);
197 		memset(g_sb_buf + zero_offs, 0, sizeof(g_sb_buf) - zero_offs);
198 		sb->v3.clean = clean;
199 		sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_INVALID;
200 		break;
201 
202 	case FTL_SB_VERSION_5:
203 		zero_offs = sizeof(struct ftl_superblock_v5);
204 		memset(g_sb_buf + zero_offs, 0, sizeof(g_sb_buf) - zero_offs);
205 		sb->v5.clean = clean;
206 		break;
207 	}
208 
209 	sb->header.crc = get_sb_crc(&sb->current);
210 }
211 
212 static void
213 test_setup_sb_v2(uint64_t clean)
214 {
215 	test_setup_sb_ver(FTL_SB_VERSION_2, clean);
216 }
217 
218 static void
219 test_setup_sb_v3(uint64_t clean)
220 {
221 	test_setup_sb_ver(FTL_SB_VERSION_3, clean);
222 }
223 
224 static void
225 test_setup_sb_v5(uint64_t clean)
226 {
227 	test_setup_sb_ver(FTL_SB_VERSION_5, clean);
228 }
229 
230 static void
231 test_sb_crc_v2(void)
232 {
233 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
234 	uint64_t crc;
235 
236 	/* v2-specific crc: it's not really working */
237 	test_setup_sb_v2(true);
238 	crc = sb->header.crc;
239 
240 	sb->header.crc++;
241 	sb->header.crc = get_sb_crc(&sb->current);
242 	CU_ASSERT_EQUAL(crc, sb->header.crc);
243 
244 	g_sb_buf[sizeof(struct ftl_superblock_v2)]++;
245 	sb->header.crc = get_sb_crc(&sb->current);
246 	CU_ASSERT_EQUAL(crc, sb->header.crc);
247 
248 	g_sb_buf[sizeof(g_sb_buf) - 1]++;
249 	sb->header.crc = get_sb_crc(&sb->current);
250 	CU_ASSERT_EQUAL(crc, sb->header.crc);
251 }
252 
253 static void
254 test_sb_crc_v3(void)
255 {
256 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
257 	uint64_t crc;
258 
259 	/* v3 crc: covers the entire buf */
260 	test_setup_sb_v3(true);
261 	crc = sb->header.crc;
262 
263 	sb->header.crc++;
264 	sb->header.crc = get_sb_crc(&sb->current);
265 	CU_ASSERT_EQUAL(crc, sb->header.crc);
266 	crc = sb->header.crc;
267 
268 	g_sb_buf[sizeof(struct ftl_superblock_v2)]++;
269 	sb->header.crc = get_sb_crc(&sb->current);
270 	CU_ASSERT_NOT_EQUAL(crc, sb->header.crc);
271 	crc = sb->header.crc;
272 
273 	g_sb_buf[sizeof(g_sb_buf) - 1]++;
274 	sb->header.crc = get_sb_crc(&sb->current);
275 	CU_ASSERT_NOT_EQUAL(crc, sb->header.crc);
276 	crc = sb->header.crc;
277 
278 	CU_ASSERT_EQUAL(crc, sb->header.crc);
279 }
280 
281 static int
282 test_superblock_v3_md_layout_add(struct spdk_ftl_dev *dev,
283 				 struct ftl_superblock_v3_md_region *sb_reg,
284 				 uint32_t reg_type, uint32_t reg_version, uint64_t blk_offs, uint64_t blk_sz)
285 {
286 	if (ftl_superblock_v3_md_region_overflow(dev, sb_reg)) {
287 		return -EOVERFLOW;
288 	}
289 
290 	sb_reg->type = reg_type;
291 	sb_reg->version = reg_version;
292 	sb_reg->blk_offs = blk_offs;
293 	sb_reg->blk_sz = blk_sz;
294 	return 0;
295 }
296 
297 static int
298 test_superblock_v3_md_layout_add_free(struct spdk_ftl_dev *dev,
299 				      struct ftl_superblock_v3_md_region **sb_reg,
300 				      uint32_t reg_type, uint32_t free_type, uint64_t total_blocks)
301 {
302 	struct ftl_layout *layout = &dev->layout;
303 	struct ftl_layout_region *reg = &layout->region[reg_type];
304 	uint64_t blks_left = total_blocks - reg->current.offset - reg->current.blocks;
305 
306 	if (blks_left == 0) {
307 		return 0;
308 	}
309 
310 	(*sb_reg)->df_next = ftl_df_get_obj_id(dev->sb, (*sb_reg) + 1);
311 	(*sb_reg) = (*sb_reg) + 1;
312 
313 	if (test_superblock_v3_md_layout_add(dev, *sb_reg, free_type, 0,
314 					     reg->current.offset + reg->current.blocks, blks_left)) {
315 		return -1;
316 	}
317 
318 	(*sb_reg)->df_next = FTL_DF_OBJ_ID_INVALID;
319 
320 	return 0;
321 }
322 
323 static int
324 test_ftl_superblock_v3_md_layout_build(struct spdk_ftl_dev *dev)
325 {
326 	union ftl_superblock_ver *sb_ver = (union ftl_superblock_ver *)dev->sb;
327 	struct ftl_layout *layout = &dev->layout;
328 	struct ftl_layout_region *reg;
329 	int n = 0;
330 	bool is_empty = ftl_superblock_v3_md_layout_is_empty(sb_ver);
331 	struct ftl_superblock_v3_md_region *sb_reg = &sb_ver->v3.md_layout_head;
332 
333 	/* TODO: major upgrades: add all free regions being tracked
334 	 * For now SB MD layout must be empty - otherwise md free regions may be lost */
335 	assert(is_empty);
336 
337 	for (; n < FTL_LAYOUT_REGION_TYPE_MAX_V3;) {
338 		reg = ftl_layout_region_get(dev, n);
339 		assert(reg);
340 		if (md_region_is_fixed(reg->type)) {
341 			n++;
342 
343 			if (n >= FTL_LAYOUT_REGION_TYPE_MAX_V3) {
344 				/* For VSS emulation the last layout type is a fixed region, we need to move back the list and end the list on previous entry */
345 				sb_reg--;
346 				break;
347 			}
348 			continue;
349 		}
350 
351 		if (test_superblock_v3_md_layout_add(dev, sb_reg, reg->type, reg->current.version,
352 						     reg->current.offset, reg->current.blocks)) {
353 			return -1;
354 		}
355 
356 		n++;
357 		if (n < FTL_LAYOUT_REGION_TYPE_MAX_V3) {
358 			/* next region */
359 			sb_reg->df_next = ftl_df_get_obj_id(sb_ver, sb_reg + 1);
360 			sb_reg++;
361 		}
362 	}
363 
364 	/* terminate the list */
365 	sb_reg->df_next = FTL_DF_OBJ_ID_INVALID;
366 
367 	/* create free_nvc/free_base regions on the first run */
368 	if (is_empty) {
369 		test_superblock_v3_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_NVC,
370 						      FTL_LAYOUT_REGION_TYPE_FREE_NVC, layout->nvc.total_blocks);
371 
372 		test_superblock_v3_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_BASE,
373 						      FTL_LAYOUT_REGION_TYPE_FREE_BASE, layout->base.total_blocks);
374 	}
375 
376 	return 0;
377 }
378 
379 static void
380 test_sb_v3_region_reinit(void)
381 {
382 	uint32_t reg_type;
383 
384 	for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
385 		g_dev.layout.region[reg_type].type = reg_type;
386 	}
387 }
388 
389 static struct ftl_superblock_v3_md_region *
390 test_sb_v3_find_region_ver(enum ftl_layout_region_type reg_type, uint32_t reg_ver)
391 {
392 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
393 	struct ftl_superblock_v3_md_region *sb_reg = &sb->v3.md_layout_head;
394 
395 	while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
396 		if (sb_reg->type == reg_type && sb_reg->version == reg_ver) {
397 			return sb_reg;
398 		}
399 
400 		if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
401 			break;
402 		}
403 
404 		if (UINT64_MAX - (uintptr_t)sb <= sb_reg->df_next) {
405 			return NULL;
406 		}
407 
408 		sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
409 		if (ftl_superblock_v3_md_region_overflow(&g_dev, sb_reg)) {
410 			return NULL;
411 		}
412 	}
413 
414 	return NULL;
415 }
416 
417 static struct ftl_superblock_v3_md_region *
418 test_sb_v3_find_region_latest(enum ftl_layout_region_type reg_type)
419 {
420 	return test_sb_v3_find_region_ver(reg_type, ftl_layout_upgrade_region_get_latest_version(reg_type));
421 }
422 
423 static void
424 test_sb_v3_md_layout(void)
425 {
426 	struct ftl_superblock_v3_md_region *sb_reg, *sb_reg_next, *sb_reg_next2;
427 	struct ftl_layout_region *reg_head, *reg;
428 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
429 	ftl_df_obj_id df_next_head, df_next_reg;
430 	uint32_t md_type_head;
431 	int rc;
432 
433 	test_setup_sb_v3(false);
434 	CU_ASSERT_EQUAL(ftl_superblock_is_blob_area_empty(&sb->current), true);
435 
436 	/* load failed: empty md list: */
437 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
438 	CU_ASSERT_NOT_EQUAL(rc, 0);
439 	test_sb_v3_region_reinit();
440 
441 	/* create md layout: */
442 	test_ftl_superblock_v3_md_layout_build(&g_dev);
443 	CU_ASSERT_EQUAL(ftl_superblock_is_blob_area_empty(&sb->current), false);
444 
445 	/* buf overflow, sb_reg = 1 byte overflow: */
446 	df_next_head = sb->v3.md_layout_head.df_next;
447 	sb->v3.md_layout_head.df_next = FTL_SUPERBLOCK_SIZE - sizeof(sb->v3.md_layout_head) + 1;
448 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
449 	CU_ASSERT_EQUAL(rc, -EOVERFLOW);
450 	test_sb_v3_region_reinit();
451 
452 	/* buf underflow, sb_reg = -1: */
453 	sb->v3.md_layout_head.df_next = UINTPTR_MAX - (uintptr_t)sb;
454 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
455 	CU_ASSERT_EQUAL(rc, -EOVERFLOW);
456 	test_sb_v3_region_reinit();
457 
458 	/* buf underflow, sb_reg = 2 bytes underflow */
459 	sb->v3.md_layout_head.df_next = UINTPTR_MAX - 1;
460 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
461 	CU_ASSERT_EQUAL(rc, -EOVERFLOW);
462 	test_sb_v3_region_reinit();
463 
464 	/* looping md layout list: */
465 	sb->v3.md_layout_head.df_next = ftl_df_get_obj_id(sb, &sb->v3.md_layout_head);
466 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
467 	CU_ASSERT_NOT_EQUAL(rc, 0);
468 	test_sb_v3_region_reinit();
469 
470 	sb->v3.md_layout_head.df_next = df_next_head;
471 
472 	/* unsupported/fixed md region: */
473 	md_type_head = sb->v3.md_layout_head.type;
474 	sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_SB;
475 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
476 	CU_ASSERT_NOT_EQUAL(rc, 0);
477 	test_sb_v3_region_reinit();
478 
479 	/* unsupported/invalid md region: */
480 	sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_MAX;
481 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
482 	CU_ASSERT_NOT_EQUAL(rc, 0);
483 	test_sb_v3_region_reinit();
484 
485 	/* unsupported/invalid md region: */
486 	sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_MAX_V3;
487 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
488 	CU_ASSERT_NOT_EQUAL(rc, 0);
489 	test_sb_v3_region_reinit();
490 
491 	/* restore the sb: */
492 	sb->v3.md_layout_head.type = md_type_head;
493 
494 	/* load succeeded, no prev version found: */
495 	reg_head = &g_dev.layout.region[md_type_head];
496 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
497 	CU_ASSERT_EQUAL(rc, 0);
498 	CU_ASSERT_EQUAL(reg_head->current.version,
499 			ftl_layout_upgrade_region_get_latest_version(md_type_head));
500 	test_sb_v3_region_reinit();
501 
502 	/* load succeeded, prev (upgrade, i.e. no current) version discovery: */
503 	reg = &g_dev.layout.region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
504 	sb_reg = test_sb_v3_find_region_latest(FTL_LAYOUT_REGION_TYPE_BAND_MD);
505 	CU_ASSERT_NOT_EQUAL(sb_reg, NULL);
506 	CU_ASSERT_EQUAL(reg->type, sb_reg->type);
507 	df_next_reg = sb_reg->df_next;
508 
509 	sb_reg->version--;
510 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
511 	CU_ASSERT_EQUAL(rc, 0);
512 	CU_ASSERT_EQUAL(reg->current.version, sb_reg->version);
513 	sb_reg->version++;
514 	test_sb_v3_region_reinit();
515 
516 	/* load succeeded, newer version found: */
517 	sb_reg->df_next = FTL_SUPERBLOCK_SIZE - sizeof(*sb_reg_next);
518 	sb_reg_next = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
519 	rc = test_superblock_v3_md_layout_add(&g_dev, sb_reg_next, sb_reg->type, sb_reg->version + 1,
520 					      sb_reg->blk_offs, sb_reg->blk_sz);
521 	CU_ASSERT_EQUAL(rc, 0);
522 	sb_reg_next->df_next = df_next_reg;
523 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
524 	CU_ASSERT_EQUAL(rc, 0);
525 	CU_ASSERT_EQUAL(reg->current.version, sb_reg->version);
526 	test_sb_v3_region_reinit();
527 
528 	/* load succeeded, prev version discovery: */
529 	sb_reg_next->version = sb_reg->version - 1;
530 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
531 	CU_ASSERT_EQUAL(rc, 0);
532 	CU_ASSERT_EQUAL(reg->current.version, sb_reg_next->version);
533 	test_sb_v3_region_reinit();
534 
535 	/* looping regions found: */
536 	sb_reg_next->df_next = FTL_SUPERBLOCK_SIZE - 2 * sizeof(*sb_reg_next);
537 	sb_reg_next2 = ftl_df_get_obj_ptr(sb, sb_reg_next->df_next);
538 	rc = test_superblock_v3_md_layout_add(&g_dev, sb_reg_next2, sb_reg_next->type,
539 					      sb_reg_next->version + 2,
540 					      sb_reg_next->blk_offs, sb_reg_next->blk_sz);
541 	CU_ASSERT_EQUAL(rc, 0);
542 	sb_reg_next2->df_next = FTL_SUPERBLOCK_SIZE - 2 * sizeof(*sb_reg_next);
543 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
544 	CU_ASSERT_EQUAL(rc, -ELOOP);
545 	test_sb_v3_region_reinit();
546 
547 	/* multiple (same ver) regions found: */
548 	sb_reg_next2->version = sb_reg_next->version;
549 	sb_reg_next2->df_next = df_next_reg;
550 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
551 	CU_ASSERT_EQUAL(rc, -EAGAIN);
552 	test_sb_v3_region_reinit();
553 
554 	/* multiple current regions found: */
555 	sb_reg_next->version = sb_reg->version;
556 	sb_reg_next->df_next = df_next_reg;
557 	rc = ftl_superblock_v3_md_layout_load_all(&g_dev);
558 	CU_ASSERT_EQUAL(rc, -EAGAIN);
559 
560 	/* restore the sb: */
561 	sb->v3.md_layout_head.df_next = df_next_head;
562 	test_sb_v3_region_reinit();
563 }
564 
565 static void
566 test_sb_v5_md_layout(void)
567 {
568 	struct layout_tracker_blob_entry *tbe;
569 	struct layout_blob_entry *lbe;
570 	struct ftl_layout_region *reg;
571 	union ftl_superblock_ver *sb = (void *)g_sb_buf;
572 	int rc;
573 	const struct ftl_layout_tracker_bdev_region_props *reg_props;
574 	void *blob_nvc, *blob_base, *blob_regs;
575 
576 	test_setup_sb_v5(false);
577 	CU_ASSERT_EQUAL(ftl_superblock_is_blob_area_empty(&sb->current), true);
578 
579 	/* load failed: empty md list: */
580 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
581 	CU_ASSERT_NOT_EQUAL(rc, 0);
582 
583 	/* create md layout: */
584 	for (enum ftl_layout_region_type regno = 0; regno < FTL_LAYOUT_REGION_TYPE_MAX; regno++) {
585 		struct ftl_layout_region *reg = &g_dev.layout.region[regno];
586 		CU_ASSERT_EQUAL(regno, reg->type);
587 		struct ftl_layout_tracker_bdev *tracker = sb_v3_md_region_is_nvc(regno) ? g_dev.nvc_layout_tracker :
588 				g_dev.base_layout_tracker;
589 		const struct ftl_layout_tracker_bdev_region_props *reg_props = ftl_layout_tracker_bdev_add_region(
590 					tracker, reg->type, reg->current.version, reg->current.blocks, TEST_REG_BLKS);
591 
592 		CU_ASSERT_EQUAL(reg->type, reg_props->type);
593 		CU_ASSERT_EQUAL(reg->current.version, reg_props->ver);
594 		CU_ASSERT_EQUAL(reg->current.offset, reg_props->blk_offs);
595 		CU_ASSERT_EQUAL(reg->current.blocks, reg_props->blk_sz);
596 	}
597 	ftl_superblock_v5_store_blob_area(&g_dev);
598 	CU_ASSERT_EQUAL(ftl_superblock_is_blob_area_empty(&sb->current), false);
599 
600 	blob_nvc = ftl_df_get_obj_ptr(sb->v5.blob_area, sb->v5.md_layout_nvc.df_id);
601 	blob_base = ftl_df_get_obj_ptr(sb->v5.blob_area, sb->v5.md_layout_base.df_id);
602 	blob_regs = ftl_df_get_obj_ptr(sb->v5.blob_area, sb->v5.layout_params.df_id);
603 
604 	/* unsupported nvc md region type: */
605 	tbe = blob_nvc;
606 	tbe->type += FTL_LAYOUT_REGION_TYPE_MAX;
607 	sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_SB;
608 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
609 	CU_ASSERT_NOT_EQUAL(rc, 0);
610 	tbe->type -= FTL_LAYOUT_REGION_TYPE_MAX;
611 
612 	/* unsupported base md region type: */
613 	tbe = blob_base;
614 	tbe->type += FTL_LAYOUT_REGION_TYPE_MAX;
615 	sb->v3.md_layout_head.type = FTL_LAYOUT_REGION_TYPE_SB;
616 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
617 	CU_ASSERT_NOT_EQUAL(rc, 0);
618 	tbe->type -= FTL_LAYOUT_REGION_TYPE_MAX;
619 
620 	/* load succeeded, no prev version found: */
621 	reg = &g_dev.layout.region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
622 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
623 	CU_ASSERT_EQUAL(rc, 0);
624 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_latest_region, NULL);
625 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
626 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
627 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_oldest_region, NULL);
628 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
629 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
630 
631 	/* move the sb-stored blobs around: */
632 	CU_ASSERT(blob_nvc < blob_base);
633 	CU_ASSERT(blob_base < blob_regs);
634 	blob_regs = memmove(blob_regs + 8192, blob_regs, sb->v5.layout_params.blob_sz);
635 	sb->v5.layout_params.df_id += 8192;
636 	blob_base = memmove(blob_base + 4096, blob_base, sb->v5.md_layout_base.blob_sz);
637 	sb->v5.md_layout_base.df_id += 4096;
638 
639 	/* load succeeded again, no prev version found: */
640 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
641 	CU_ASSERT_EQUAL(rc, 0);
642 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_latest_region, NULL);
643 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
644 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
645 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_oldest_region, NULL);
646 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
647 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
648 
649 	/* load failed, regs overlap: */
650 	tbe = blob_nvc;
651 	tbe++;
652 	tbe->blk_offs -= tbe->blk_sz;
653 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
654 	CU_ASSERT_NOT_EQUAL(rc, 0);
655 	tbe->blk_offs += tbe->blk_sz;
656 
657 	/* load failed, the same region version found twice: */
658 	tbe = (blob_nvc + sb->v5.md_layout_nvc.blob_sz);
659 	sb->v5.md_layout_nvc.blob_sz += sizeof(*tbe);
660 	tbe->type = reg->type;
661 	tbe->ver = reg->current.version;
662 	tbe->blk_offs = reg->current.offset + FTL_LAYOUT_REGION_TYPE_MAX * reg->current.blocks;
663 	tbe->blk_sz = reg->current.blocks;
664 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
665 	CU_ASSERT_NOT_EQUAL(rc, 0);
666 
667 	/* load succeeded, prev (upgrade, i.e. no current) version discovery: */
668 	tbe->type = reg->type;
669 	tbe->ver = reg->current.version - 1;
670 	tbe->blk_offs = reg->current.offset + FTL_LAYOUT_REGION_TYPE_MAX * reg->current.blocks;
671 	tbe->blk_sz = reg->current.blocks;
672 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
673 	CU_ASSERT_EQUAL(rc, 0);
674 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_latest_region, NULL);
675 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
676 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
677 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_oldest_region, NULL);
678 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
679 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version - 1);
680 
681 	/* load succeeded, newer version found: */
682 	tbe->ver = reg->current.version + 1;
683 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
684 	CU_ASSERT_EQUAL(rc, 0);
685 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_latest_region, NULL);
686 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
687 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version + 1);
688 	reg_props = sb_md_layout_find_region(&g_dev, reg->type, sb_md_layout_find_oldest_region, NULL);
689 	CU_ASSERT_NOT_EQUAL(reg_props, NULL);
690 	CU_ASSERT_EQUAL(reg_props->ver, reg->current.version);
691 
692 	/* load failed, invalid type in layout properties: */
693 	lbe = blob_regs;
694 	lbe += FTL_LAYOUT_REGION_TYPE_BAND_MD;
695 	CU_ASSERT_EQUAL(lbe->type, FTL_LAYOUT_REGION_TYPE_BAND_MD);
696 	lbe->type = FTL_LAYOUT_REGION_TYPE_MAX;
697 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
698 	CU_ASSERT_NOT_EQUAL(rc, 0);
699 	lbe->type = FTL_LAYOUT_REGION_TYPE_BAND_MD;
700 
701 	/* load succeeded, restore layout properties: */
702 	CU_ASSERT_EQUAL(reg->num_entries, 0);
703 	CU_ASSERT_EQUAL(reg->entry_size, 0);
704 	lbe->num_entries = 0x1984;
705 	lbe->entry_size = 0x1405;
706 	rc = ftl_superblock_v5_load_blob_area(&g_dev);
707 	CU_ASSERT_EQUAL(rc, 0);
708 	CU_ASSERT_EQUAL(reg->num_entries, 0x1984);
709 	CU_ASSERT_EQUAL(reg->entry_size, 0x1405);
710 
711 	/* restore the sb: */
712 	sb->v5.md_layout_nvc.blob_sz -= sizeof(*tbe);
713 }
714 
715 int
716 main(int argc, char **argv)
717 {
718 	CU_pSuite suite = NULL;
719 	unsigned int num_failures = 0;
720 
721 	CU_set_error_action(CUEA_ABORT);
722 	CU_initialize_registry();
723 
724 	suite = CU_add_suite("ftl_sb", test_setup, test_teardown);
725 
726 	CU_ADD_TEST(suite, test_sb_crc_v2);
727 	CU_ADD_TEST(suite, test_sb_crc_v3);
728 	CU_ADD_TEST(suite, test_sb_v3_md_layout);
729 	CU_ADD_TEST(suite, test_sb_v5_md_layout);
730 
731 	CU_basic_set_mode(CU_BRM_VERBOSE);
732 	CU_basic_run_tests();
733 	num_failures = CU_get_number_of_failures();
734 	CU_cleanup_registry();
735 
736 	return num_failures;
737 }
738