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