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