xref: /spdk/lib/ftl/upgrade/ftl_sb_v5.c (revision 83ba9086796471697a4975a58f60e2392bccd08c)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright 2023 Solidigm All Rights Reserved
3  *   All rights reserved.
4  */
5 
6 #include "spdk/string.h"
7 
8 #include "ftl_sb_v5.h"
9 #include "ftl_core.h"
10 #include "ftl_layout.h"
11 #include "ftl_band.h"
12 #include "upgrade/ftl_sb_prev.h"
13 #include "upgrade/ftl_sb_upgrade.h"
14 #include "upgrade/ftl_layout_upgrade.h"
15 #include "utils/ftl_layout_tracker_bdev.h"
16 
17 typedef size_t (*blob_store_fn)(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_buf_sz);
18 typedef int (*blob_load_fn)(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_sz);
19 
20 bool
21 ftl_superblock_v5_is_blob_area_empty(union ftl_superblock_ver *sb_ver)
22 {
23 	return sb_ver->v5.blob_area_end == 0;
24 }
25 
26 static bool
27 validate_blob_area(struct ftl_superblock_v5_md_blob_hdr *sb_blob_hdr,
28 		   ftl_df_obj_id sb_blob_area_end)
29 {
30 	return sb_blob_hdr->df_id <= sb_blob_area_end &&
31 	       (sb_blob_hdr->df_id + sb_blob_hdr->blob_sz) <= sb_blob_area_end;
32 }
33 
34 bool
35 ftl_superblock_v5_validate_blob_area(struct spdk_ftl_dev *dev)
36 {
37 	union ftl_superblock_ver *sb_ver = (union ftl_superblock_ver *)dev->sb;
38 
39 	return validate_blob_area(&sb_ver->v5.md_layout_nvc, sb_ver->v5.blob_area_end) &&
40 	       validate_blob_area(&sb_ver->v5.md_layout_base, sb_ver->v5.blob_area_end) &&
41 	       validate_blob_area(&sb_ver->v5.layout_params, sb_ver->v5.blob_area_end);
42 }
43 
44 static size_t
45 sb_blob_store(struct spdk_ftl_dev *dev, struct ftl_superblock_v5_md_blob_hdr *sb_blob_hdr,
46 	      blob_store_fn blob_store, void *sb_blob_area)
47 {
48 	struct ftl_superblock_v5 *sb = (struct ftl_superblock_v5 *)dev->sb;
49 	uintptr_t sb_end = ((uintptr_t)sb) + FTL_SUPERBLOCK_SIZE;
50 	size_t blob_sz = sb_end - (uintptr_t)sb_blob_area;
51 
52 	/* Test SB blob area overflow */
53 	if ((uintptr_t)sb_blob_area < (uintptr_t)sb->blob_area) {
54 		ftl_bug(true);
55 		return 0;
56 	}
57 	if ((uintptr_t)sb_blob_area >= sb_end) {
58 		ftl_bug(true);
59 		return 0;
60 	}
61 
62 	blob_sz = blob_store(dev, sb_blob_area, blob_sz);
63 	sb_blob_hdr->blob_sz = blob_sz;
64 	sb_blob_hdr->df_id = ftl_df_get_obj_id(sb->blob_area, sb_blob_area);
65 	return blob_sz;
66 }
67 
68 static size_t
69 base_blob_store(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_buf_sz)
70 {
71 	return ftl_layout_tracker_bdev_blob_store(dev->base_layout_tracker, blob_buf, blob_buf_sz);
72 }
73 
74 static size_t
75 nvc_blob_store(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_buf_sz)
76 {
77 	return ftl_layout_tracker_bdev_blob_store(dev->nvc_layout_tracker, blob_buf, blob_buf_sz);
78 }
79 
80 int
81 ftl_superblock_v5_store_blob_area(struct spdk_ftl_dev *dev)
82 {
83 	struct ftl_superblock_v5 *sb = (struct ftl_superblock_v5 *)dev->sb;
84 	void *sb_blob_area;
85 	size_t blob_sz;
86 
87 	/* Store the NVC-backed FTL MD layout info */
88 	sb_blob_area = ftl_df_get_obj_ptr(sb->blob_area, 0);
89 	spdk_strcpy_pad(sb->nvc_dev_name, dev->nv_cache.nvc_type->name,
90 			SPDK_COUNTOF(sb->nvc_dev_name), '\0');
91 	blob_sz = sb_blob_store(dev, &sb->md_layout_nvc, nvc_blob_store, sb_blob_area);
92 	FTL_NOTICELOG(dev, "nvc layout blob store 0x%"PRIx64" bytes\n", blob_sz);
93 	if (!blob_sz) {
94 		return -1;
95 	}
96 
97 	/* Store the base dev-backed FTL MD layout info */
98 	sb_blob_area += blob_sz;
99 	spdk_strcpy_pad(sb->base_dev_name, dev->base_type->name, SPDK_COUNTOF(sb->base_dev_name), '\0');
100 	blob_sz = sb_blob_store(dev, &sb->md_layout_base, base_blob_store, sb_blob_area);
101 	FTL_NOTICELOG(dev, "base layout blob store 0x%"PRIx64" bytes\n", blob_sz);
102 	if (!blob_sz) {
103 		return -1;
104 	}
105 
106 	/* Store the region props */
107 	sb_blob_area += blob_sz;
108 	blob_sz = sb_blob_store(dev, &sb->layout_params, ftl_layout_blob_store, sb_blob_area);
109 	FTL_NOTICELOG(dev, "layout blob store 0x%"PRIx64" bytes\n", blob_sz);
110 	if (!blob_sz) {
111 		return -1;
112 	}
113 
114 	/* Update the blob area end */
115 	sb_blob_area += blob_sz;
116 	sb->blob_area_end = ftl_df_get_obj_id(sb->blob_area, sb_blob_area);
117 
118 	return 0;
119 }
120 
121 static const struct ftl_layout_tracker_bdev_region_props *
122 sb_md_layout_find_oldest_region(struct spdk_ftl_dev *dev,
123 				struct ftl_layout_tracker_bdev *layout_tracker,
124 				enum ftl_layout_region_type reg_type, void *find_filter)
125 {
126 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
127 	const struct ftl_layout_tracker_bdev_region_props *reg_oldest = NULL;
128 	uint32_t ver_oldest;
129 
130 	while (true) {
131 		ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg_type, &reg_search_ctx);
132 		if (!reg_search_ctx) {
133 			break;
134 		}
135 
136 		if (!reg_oldest) {
137 			reg_oldest = reg_search_ctx;
138 			ver_oldest = reg_search_ctx->ver;
139 			continue;
140 		}
141 
142 		ftl_bug(ver_oldest == reg_search_ctx->ver);
143 		if (ver_oldest > reg_search_ctx->ver) {
144 			reg_oldest = reg_search_ctx;
145 			ver_oldest = reg_search_ctx->ver;
146 		}
147 	}
148 
149 	return reg_oldest;
150 }
151 
152 static const struct ftl_layout_tracker_bdev_region_props *
153 sb_md_layout_find_latest_region(struct spdk_ftl_dev *dev,
154 				struct ftl_layout_tracker_bdev *layout_tracker, enum ftl_layout_region_type reg_type,
155 				void *find_filter)
156 {
157 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
158 	const struct ftl_layout_tracker_bdev_region_props *reg_latest = NULL;
159 	uint32_t ver_latest;
160 
161 	while (true) {
162 		ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg_type, &reg_search_ctx);
163 		if (!reg_search_ctx) {
164 			break;
165 		}
166 
167 		if (!reg_latest) {
168 			reg_latest = reg_search_ctx;
169 			ver_latest = reg_search_ctx->ver;
170 			continue;
171 		}
172 
173 		ftl_bug(ver_latest == reg_search_ctx->ver);
174 		if (ver_latest < reg_search_ctx->ver) {
175 			reg_latest = reg_search_ctx;
176 			ver_latest = reg_search_ctx->ver;
177 		}
178 	}
179 
180 	return reg_latest;
181 }
182 
183 static const struct ftl_layout_tracker_bdev_region_props *
184 sb_md_layout_find_region_version(struct spdk_ftl_dev *dev,
185 				 struct ftl_layout_tracker_bdev *layout_tracker, enum ftl_layout_region_type reg_type,
186 				 void *find_filter)
187 {
188 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
189 	uint32_t *reg_ver = find_filter;
190 
191 	assert(reg_ver);
192 
193 	while (true) {
194 		ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg_type, &reg_search_ctx);
195 		if (!reg_search_ctx) {
196 			break;
197 		}
198 
199 		if (reg_search_ctx->ver == *reg_ver) {
200 			break;
201 		}
202 	}
203 
204 	return reg_search_ctx;
205 }
206 
207 typedef const struct ftl_layout_tracker_bdev_region_props *(*sb_md_layout_find_fn)(
208 	struct spdk_ftl_dev *dev, struct ftl_layout_tracker_bdev *layout_tracker,
209 	enum ftl_layout_region_type reg_type, void *find_filter);
210 
211 static const struct ftl_layout_tracker_bdev_region_props *
212 sb_md_layout_find_region(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type,
213 			 sb_md_layout_find_fn find_fn, void *find_filter)
214 {
215 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx;
216 	struct ftl_layout_tracker_bdev *nvc_layout_tracker = dev->nvc_layout_tracker;
217 	struct ftl_layout_tracker_bdev *base_layout_tracker = dev->base_layout_tracker;
218 
219 	reg_search_ctx = find_fn(dev, nvc_layout_tracker, reg_type, find_filter);
220 	if (reg_search_ctx) {
221 		assert(find_fn(dev, base_layout_tracker, reg_type, find_filter) == NULL);
222 		return reg_search_ctx;
223 	}
224 
225 	reg_search_ctx = find_fn(dev, base_layout_tracker, reg_type, find_filter);
226 	return reg_search_ctx;
227 }
228 
229 static int
230 sb_blob_load(struct spdk_ftl_dev *dev, struct ftl_superblock_v5_md_blob_hdr *sb_blob_hdr,
231 	     blob_load_fn blob_load)
232 {
233 	struct ftl_superblock_v5 *sb = (struct ftl_superblock_v5 *)dev->sb;
234 	uintptr_t sb_end = ((uintptr_t)sb) + FTL_SUPERBLOCK_SIZE;
235 	void *blob_area;
236 
237 	if (sb_blob_hdr->df_id == FTL_DF_OBJ_ID_INVALID) {
238 		/* Uninitialized blob */
239 		return -1;
240 	}
241 
242 	blob_area = ftl_df_get_obj_ptr(sb->blob_area, sb_blob_hdr->df_id);
243 
244 	/* Test SB blob area overflow */
245 	if ((uintptr_t)blob_area < (uintptr_t)sb->blob_area) {
246 		ftl_bug(true);
247 		return -1;
248 	}
249 	if ((uintptr_t)blob_area + sb_blob_hdr->blob_sz >= sb_end) {
250 		ftl_bug(true);
251 		return -1;
252 	}
253 
254 	return blob_load(dev, blob_area, sb_blob_hdr->blob_sz);
255 }
256 
257 static int
258 base_blob_load(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_sz)
259 {
260 	return ftl_layout_tracker_bdev_blob_load(dev->base_layout_tracker, blob_buf, blob_sz);
261 }
262 
263 static int
264 nvc_blob_load(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_sz)
265 {
266 	return ftl_layout_tracker_bdev_blob_load(dev->nvc_layout_tracker, blob_buf, blob_sz);
267 }
268 
269 int
270 ftl_superblock_v5_load_blob_area(struct spdk_ftl_dev *dev)
271 {
272 	struct ftl_superblock_v5 *sb = (struct ftl_superblock_v5 *)dev->sb;
273 
274 	/* Load the NVC-backed FTL MD layout info */
275 	if (strncmp(sb->nvc_dev_name, dev->nv_cache.nvc_type->name, SPDK_COUNTOF(sb->nvc_dev_name))) {
276 		return -1;
277 	}
278 	FTL_NOTICELOG(dev, "nvc layout blob load 0x%"PRIx64" bytes\n", (uint64_t)sb->md_layout_nvc.blob_sz);
279 	if (sb_blob_load(dev, &sb->md_layout_nvc, nvc_blob_load)) {
280 		return -1;
281 	}
282 
283 	/* Load the base dev-backed FTL MD layout info */
284 	if (strncmp(sb->base_dev_name, dev->base_type->name, SPDK_COUNTOF(sb->base_dev_name))) {
285 		return -1;
286 	}
287 	FTL_NOTICELOG(dev, "base layout blob load 0x%"PRIx64" bytes\n",
288 		      (uint64_t)sb->md_layout_base.blob_sz);
289 	if (sb_blob_load(dev, &sb->md_layout_base, base_blob_load)) {
290 		return -1;
291 	}
292 
293 	/* Load the region props */
294 	FTL_NOTICELOG(dev, "layout blob load 0x%"PRIx64" bytes\n", (uint64_t)sb->layout_params.blob_sz);
295 	if (sb_blob_load(dev, &sb->layout_params, ftl_layout_blob_load)) {
296 		return -1;
297 	}
298 
299 	return 0;
300 }
301 
302 static struct ftl_layout_tracker_bdev *
303 sb_get_md_layout_tracker(struct spdk_ftl_dev *dev, struct ftl_layout_region *reg)
304 {
305 	return (reg->bdev_desc == dev->base_bdev_desc) ? dev->base_layout_tracker : dev->nvc_layout_tracker;
306 }
307 
308 static void
309 sb_md_layout_delete_prev_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *reg)
310 {
311 	int rc;
312 	struct ftl_layout_tracker_bdev *layout_tracker = sb_get_md_layout_tracker(dev, reg);
313 
314 	rc = ftl_layout_tracker_bdev_rm_region(layout_tracker, reg->type, reg->current.version);
315 	/* Version 0 indicates a placeholder for creation of a new region */
316 	ftl_bug(reg->current.version != 0 && rc != 0);
317 }
318 
319 static void
320 sb_md_layout_update_prev_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *reg,
321 				uint32_t new_version)
322 {
323 	int rc;
324 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
325 	struct ftl_layout_tracker_bdev *layout_tracker = sb_get_md_layout_tracker(dev, reg);
326 	struct ftl_layout_tracker_bdev_region_props reg_props;
327 
328 	/* Get region properties */
329 	ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg->type, &reg_search_ctx);
330 	ftl_bug(reg_search_ctx == NULL);
331 	reg_props = *reg_search_ctx;
332 
333 	/* Delete the region */
334 	rc = ftl_layout_tracker_bdev_rm_region(layout_tracker, reg_props.type, reg_props.ver);
335 	ftl_bug(rc != 0);
336 
337 	/* Insert the same region with new version */
338 	reg_search_ctx = ftl_layout_tracker_bdev_insert_region(layout_tracker, reg_props.type, new_version,
339 			 reg_props.blk_offs, reg_props.blk_sz);
340 	ftl_bug(reg_search_ctx == NULL);
341 
342 	/* Verify the oldest region version stored in the SB is the new_version */
343 	reg_search_ctx = sb_md_layout_find_region(dev, reg_props.type, sb_md_layout_find_oldest_region,
344 			 NULL);
345 	ftl_bug(reg_search_ctx == NULL);
346 	ftl_bug(reg_search_ctx->ver != new_version);
347 }
348 
349 int
350 ftl_superblock_v5_md_layout_upgrade_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *reg,
351 		uint32_t new_version)
352 {
353 	const struct ftl_layout_tracker_bdev_region_props *reg_next = NULL;
354 	uint64_t latest_ver;
355 
356 	ftl_bug(reg->current.version >= new_version);
357 
358 	reg_next = sb_md_layout_find_region(dev, reg->type, sb_md_layout_find_region_version, &new_version);
359 	if (reg_next) {
360 		/**
361 		 * Major upgrade.
362 		 * Found a new MD region allocated for upgrade to the next version.
363 		 * Destroy the previous version now that the upgrade is completed.
364 		 */
365 		ftl_bug(reg_next->ver != new_version);
366 		ftl_bug(reg_next->type != reg->type);
367 		sb_md_layout_delete_prev_region(dev, reg);
368 		reg->current.offset = reg_next->blk_offs;
369 		reg->current.blocks = reg_next->blk_sz;
370 	} else {
371 		/**
372 		 * Minor upgrade.
373 		 * Upgraded the MD region in place.
374 		 * Update the version in place.
375 		 */
376 		sb_md_layout_update_prev_region(dev, reg, new_version);
377 	}
378 
379 	reg->current.version = new_version;
380 	latest_ver = ftl_layout_upgrade_region_get_latest_version(reg->type);
381 	if (new_version == latest_ver) {
382 		/* Audit the only region version stored in the SB */
383 		reg_next = sb_md_layout_find_region(dev, reg->type, sb_md_layout_find_latest_region, NULL);
384 		ftl_bug(reg_next == NULL);
385 		ftl_bug(reg_next->ver != new_version);
386 
387 		reg_next = sb_md_layout_find_region(dev, reg->type, sb_md_layout_find_oldest_region, NULL);
388 		ftl_bug(reg_next == NULL);
389 		ftl_bug(reg_next->ver != new_version);
390 
391 		reg_next = sb_md_layout_find_region(dev, reg->type, sb_md_layout_find_region_version, &new_version);
392 		ftl_bug(reg->type != reg_next->type);
393 		ftl_bug(reg->current.version != reg_next->ver);
394 		ftl_bug(reg->current.offset != reg_next->blk_offs);
395 		ftl_bug(reg->current.blocks != reg_next->blk_sz);
396 	}
397 
398 	return 0;
399 }
400 
401 void
402 ftl_superblock_v5_md_layout_dump(struct spdk_ftl_dev *dev)
403 {
404 	struct ftl_layout_tracker_bdev *nvc_layout_tracker = dev->nvc_layout_tracker;
405 	struct ftl_layout_tracker_bdev *base_layout_tracker = dev->base_layout_tracker;
406 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
407 
408 	FTL_NOTICELOG(dev, "SB metadata layout - nvc:\n");
409 	while (true) {
410 		ftl_layout_tracker_bdev_find_next_region(nvc_layout_tracker, FTL_LAYOUT_REGION_TYPE_INVALID,
411 				&reg_search_ctx);
412 		if (!reg_search_ctx) {
413 			break;
414 		}
415 
416 		FTL_NOTICELOG(dev,
417 			      "Region type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n",
418 			      reg_search_ctx->type, reg_search_ctx->ver, reg_search_ctx->blk_offs, reg_search_ctx->blk_sz);
419 	}
420 
421 	reg_search_ctx = NULL;
422 	FTL_NOTICELOG(dev, "SB metadata layout - base dev:\n");
423 	while (true) {
424 		ftl_layout_tracker_bdev_find_next_region(base_layout_tracker, FTL_LAYOUT_REGION_TYPE_INVALID,
425 				&reg_search_ctx);
426 		if (!reg_search_ctx) {
427 			break;
428 		}
429 
430 		FTL_NOTICELOG(dev,
431 			      "Region type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n",
432 			      reg_search_ctx->type, reg_search_ctx->ver, reg_search_ctx->blk_offs, reg_search_ctx->blk_sz);
433 	}
434 }
435 
436 static int
437 layout_apply_from_sb_blob(struct spdk_ftl_dev *dev, struct ftl_layout_tracker_bdev *layout_tracker,
438 			  int (*filter_region_type_fn)(enum ftl_layout_region_type))
439 {
440 	struct ftl_layout_region *reg;
441 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
442 
443 	while (true) {
444 		ftl_layout_tracker_bdev_find_next_region(layout_tracker, FTL_LAYOUT_REGION_TYPE_INVALID,
445 				&reg_search_ctx);
446 		if (!reg_search_ctx) {
447 			break;
448 		}
449 		if (reg_search_ctx->type == FTL_LAYOUT_REGION_TYPE_FREE) {
450 			continue;
451 		}
452 		if (filter_region_type_fn(reg_search_ctx->type)) {
453 			FTL_ERRLOG(dev, "Unknown region found in layout blob: type 0x%"PRIx32"\n", reg_search_ctx->type);
454 			return -1;
455 		}
456 
457 		reg = &dev->layout.region[reg_search_ctx->type];
458 
459 		/* First region of a given type found */
460 		if (reg->type == FTL_LAYOUT_REGION_TYPE_INVALID) {
461 			reg->type = reg_search_ctx->type;
462 			reg->current.version = reg_search_ctx->ver;
463 			reg->current.offset = reg_search_ctx->blk_offs;
464 			reg->current.blocks = reg_search_ctx->blk_sz;
465 			continue;
466 		}
467 
468 		/* Update to the oldest region version found */
469 		if (reg_search_ctx->ver < reg->current.version) {
470 			reg->current.version = reg_search_ctx->ver;
471 			reg->current.offset = reg_search_ctx->blk_offs;
472 			reg->current.blocks = reg_search_ctx->blk_sz;
473 			continue;
474 		}
475 
476 		/* Skip newer region versions */
477 		if (reg_search_ctx->ver > reg->current.version) {
478 			continue;
479 		}
480 
481 		/* Current region version already found */
482 		assert(reg_search_ctx->ver == reg->current.version);
483 		if (reg->current.offset != reg_search_ctx->blk_offs ||
484 		    reg->current.blocks != reg_search_ctx->blk_sz) {
485 			FTL_ERRLOG(dev, "Corrupted layout blob: reg type 0x%"PRIx32"\n", reg_search_ctx->type);
486 			return -1;
487 		}
488 	}
489 	return 0;
490 }
491 
492 static int
493 layout_region_verify(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type)
494 {
495 	struct ftl_layout_region *reg = ftl_layout_region_get(dev, reg_type);
496 	if (!reg) {
497 		return -ENOENT;
498 	}
499 
500 	/* Unknown version found in the blob */
501 	if (reg->current.version > ftl_layout_upgrade_get_latest_version(reg_type)) {
502 		FTL_ERRLOG(dev, "Unknown region version found in layout blob: reg type 0x%"PRIx32"\n",
503 			   reg_type);
504 		return -EINVAL;
505 	}
506 
507 	return 0;
508 }
509 
510 static int
511 layout_fixup_reg_data_base(struct spdk_ftl_dev *dev)
512 {
513 	const struct ftl_md_layout_ops *base_md_ops = &dev->base_type->ops.md_layout_ops;
514 	struct ftl_layout_region *reg = &dev->layout.region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
515 	const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
516 
517 	assert(reg->type == FTL_LAYOUT_REGION_TYPE_INVALID);
518 
519 	FTL_NOTICELOG(dev, "Adding a region\n");
520 
521 	/* Add the region */
522 	if (base_md_ops->region_create(dev, FTL_LAYOUT_REGION_TYPE_DATA_BASE, 0,
523 				       ftl_layout_base_offset(dev))) {
524 		return -1;
525 	}
526 	if (base_md_ops->region_open(dev, FTL_LAYOUT_REGION_TYPE_DATA_BASE, 0, FTL_BLOCK_SIZE,
527 				     ftl_layout_base_offset(dev), reg)) {
528 		return -1;
529 	}
530 
531 	ftl_layout_tracker_bdev_find_next_region(dev->base_layout_tracker, FTL_LAYOUT_REGION_TYPE_DATA_BASE,
532 			&reg_search_ctx);
533 	assert(reg_search_ctx);
534 	return 0;
535 }
536 
537 static int
538 layout_fixup_base(struct spdk_ftl_dev *dev)
539 {
540 	struct ftl_layout_region_descr {
541 		enum ftl_layout_region_type type;
542 		uint32_t ver;
543 		int (*on_reg_miss)(struct spdk_ftl_dev *dev);
544 	};
545 	struct ftl_layout_region_descr *reg_descr;
546 	static struct ftl_layout_region_descr nvc_regs[] = {
547 		{ .type = FTL_LAYOUT_REGION_TYPE_SB_BASE, .ver = FTL_SB_VERSION_CURRENT },
548 		{ .type = FTL_LAYOUT_REGION_TYPE_DATA_BASE, .ver = 0, .on_reg_miss = layout_fixup_reg_data_base },
549 		{ .type = FTL_LAYOUT_REGION_TYPE_VALID_MAP, .ver = 0 },
550 		{ .type = FTL_LAYOUT_REGION_TYPE_INVALID, .ver = 0 },
551 	};
552 
553 	for (reg_descr = nvc_regs; reg_descr->type != FTL_LAYOUT_REGION_TYPE_INVALID; reg_descr++) {
554 		struct ftl_layout_region *region;
555 
556 		if (layout_region_verify(dev, reg_descr->type) &&
557 		    reg_descr->on_reg_miss && reg_descr->on_reg_miss(dev)) {
558 			return -1;
559 		}
560 
561 		region = &dev->layout.region[reg_descr->type];
562 		region->type = reg_descr->type;
563 		region->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
564 		region->name = ftl_md_region_name(reg_descr->type);
565 
566 		region->bdev_desc = dev->base_bdev_desc;
567 		region->ioch = dev->base_ioch;
568 		region->vss_blksz = 0;
569 	}
570 
571 	return 0;
572 }
573 
574 static int
575 layout_fixup_nvc(struct spdk_ftl_dev *dev)
576 {
577 	int rc;
578 	struct ftl_layout_region_descr {
579 		enum ftl_layout_region_type type;
580 		bool deprecated;
581 		enum ftl_layout_region_type mirror_type;
582 	};
583 	struct ftl_layout_region_descr *reg_descr;
584 	static struct ftl_layout_region_descr nvc_regs[] = {
585 		{ .type = FTL_LAYOUT_REGION_TYPE_SB, .mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE },
586 		{ .type = FTL_LAYOUT_REGION_TYPE_L2P },
587 		{ .type = FTL_LAYOUT_REGION_TYPE_BAND_MD, .mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR },
588 		{ .type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR },
589 		{ .type = FTL_LAYOUT_REGION_TYPE_TRIM_MD, .mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR },
590 		{ .type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR },
591 		{ .type = FTL_LAYOUT_REGION_TYPE_TRIM_LOG, .mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR },
592 		{ .type = FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR },
593 		{ .type = FTL_LAYOUT_REGION_TYPE_NVC_MD, .mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR },
594 		{ .type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR },
595 		{ .type = FTL_LAYOUT_REGION_TYPE_DATA_NVC, .deprecated = true },
596 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC },
597 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT },
598 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP },
599 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT },
600 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_LOG_IO_MIN },
601 		{ .type = FTL_LAYOUT_REGION_TYPE_P2L_LOG_IO_MAX },
602 		{ .type = FTL_LAYOUT_REGION_TYPE_INVALID },
603 	};
604 
605 	for (reg_descr = nvc_regs; reg_descr->type != FTL_LAYOUT_REGION_TYPE_INVALID; reg_descr++) {
606 		struct ftl_layout_region *region;
607 
608 		rc = layout_region_verify(dev, reg_descr->type);
609 		if (rc == -ENOENT) {
610 			if (reg_descr->deprecated) {
611 				continue;
612 			}
613 
614 			ftl_layout_upgrade_add_region_placeholder(dev, dev->nvc_layout_tracker, reg_descr->type);
615 		} else if (rc) {
616 			return -1;
617 		}
618 
619 		if (reg_descr->deprecated) {
620 			rc = ftl_layout_upgrade_drop_region(dev, dev->nvc_layout_tracker, reg_descr->type,
621 							    dev->layout.region[reg_descr->type].current.version);
622 			if (rc) {
623 				return rc;
624 			}
625 			continue;
626 		}
627 
628 		region = &dev->layout.region[reg_descr->type];
629 		region->type = reg_descr->type;
630 		region->mirror_type = (reg_descr->mirror_type) ? reg_descr->mirror_type :
631 				      FTL_LAYOUT_REGION_TYPE_INVALID;
632 		region->name = ftl_md_region_name(reg_descr->type);
633 
634 		region->bdev_desc = dev->nv_cache.bdev_desc;
635 		region->ioch = dev->nv_cache.cache_ioch;
636 		region->vss_blksz = dev->nv_cache.md_size;
637 	}
638 
639 	return 0;
640 }
641 
642 static int
643 filter_region_type_base(enum ftl_layout_region_type reg_type)
644 {
645 	switch (reg_type) {
646 	case FTL_LAYOUT_REGION_TYPE_SB_BASE:
647 	case FTL_LAYOUT_REGION_TYPE_DATA_BASE:
648 	case FTL_LAYOUT_REGION_TYPE_VALID_MAP:
649 		return 0;
650 
651 	default:
652 		return 1;
653 	}
654 }
655 
656 static int
657 filter_region_type_nvc(enum ftl_layout_region_type reg_type)
658 {
659 	return filter_region_type_base(reg_type) ? 0 : 1;
660 }
661 
662 static int
663 layout_apply_nvc(struct spdk_ftl_dev *dev)
664 {
665 	if (layout_apply_from_sb_blob(dev, dev->nvc_layout_tracker, filter_region_type_nvc) ||
666 	    layout_fixup_nvc(dev)) {
667 		return -1;
668 	}
669 	return 0;
670 }
671 
672 static int
673 layout_apply_base(struct spdk_ftl_dev *dev)
674 {
675 	if (layout_apply_from_sb_blob(dev, dev->base_layout_tracker, filter_region_type_base) ||
676 	    layout_fixup_base(dev)) {
677 		return -1;
678 	}
679 	return 0;
680 }
681 
682 int
683 ftl_superblock_v5_md_layout_apply(struct spdk_ftl_dev *dev)
684 {
685 	if (layout_apply_nvc(dev) || layout_apply_base(dev)) {
686 		return -1;
687 	}
688 	return 0;
689 }
690