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