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