xref: /spdk/lib/ftl/ftl_layout.c (revision 3c6897d0c303da6539e2cfe17620553aa22167cf)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2022 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/bdev.h"
7 
8 #include "ftl_core.h"
9 #include "ftl_utils.h"
10 #include "ftl_band.h"
11 #include "ftl_layout.h"
12 #include "ftl_nv_cache.h"
13 #include "ftl_sb.h"
14 
15 #define FTL_NV_CACHE_CHUNK_DATA_SIZE(blocks) ((uint64_t)blocks * FTL_BLOCK_SIZE)
16 #define FTL_NV_CACHE_CHUNK_SIZE(blocks) \
17 	(FTL_NV_CACHE_CHUNK_DATA_SIZE(blocks) + (2 * FTL_NV_CACHE_CHUNK_MD_SIZE))
18 
19 static inline float
20 blocks2mib(uint64_t blocks)
21 {
22 	float result;
23 
24 	result = blocks;
25 	result *= FTL_BLOCK_SIZE;
26 	result /= 1024UL;
27 	result /= 1024UL;
28 
29 	return result;
30 }
31 
32 static uint64_t
33 superblock_region_size(struct spdk_ftl_dev *dev)
34 {
35 	const struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
36 	uint64_t wus = spdk_bdev_get_write_unit_size(bdev) * FTL_BLOCK_SIZE;
37 
38 	if (wus > FTL_SUPERBLOCK_SIZE) {
39 		return wus;
40 	} else {
41 		return wus * spdk_divide_round_up(FTL_SUPERBLOCK_SIZE, wus);
42 	}
43 }
44 
45 static uint64_t
46 superblock_region_blocks(struct spdk_ftl_dev *dev)
47 {
48 	return superblock_region_size(dev) / FTL_BLOCK_SIZE;
49 }
50 
51 static inline uint64_t
52 blocks_region(struct spdk_ftl_dev *dev, uint64_t bytes)
53 {
54 	const uint64_t alignment = superblock_region_size(dev);
55 	uint64_t result;
56 
57 	result = spdk_divide_round_up(bytes, alignment);
58 	result *= alignment;
59 	result /= FTL_BLOCK_SIZE;
60 
61 	return result;
62 }
63 
64 static void
65 dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
66 {
67 	assert(!(region->current.offset % superblock_region_blocks(dev)));
68 	assert(!(region->current.blocks % superblock_region_blocks(dev)));
69 
70 	FTL_NOTICELOG(dev, "Region %s\n", region->name);
71 	FTL_NOTICELOG(dev, "	offset:                      %.2f MiB\n",
72 		      blocks2mib(region->current.offset));
73 	FTL_NOTICELOG(dev, "	blocks:                      %.2f MiB\n",
74 		      blocks2mib(region->current.blocks));
75 }
76 
77 int
78 ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
79 {
80 	uint64_t i, j;
81 
82 	/* Validate if regions doesn't overlap each other  */
83 	/* TODO: major upgrades: keep track of and validate free_nvc/free_btm regions */
84 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
85 		struct ftl_layout_region *r1 = &layout->region[i];
86 
87 		for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
88 			struct ftl_layout_region *r2 = &layout->region[j];
89 
90 			if (r1->bdev_desc != r2->bdev_desc) {
91 				continue;
92 			}
93 
94 			if (i == j) {
95 				continue;
96 			}
97 
98 			uint64_t r1_begin = r1->current.offset;
99 			uint64_t r1_end = r1->current.offset + r1->current.blocks - 1;
100 			uint64_t r2_begin = r2->current.offset;
101 			uint64_t r2_end = r2->current.offset + r2->current.blocks - 1;
102 
103 			if (spdk_max(r1_begin, r2_begin) <= spdk_min(r1_end, r2_end)) {
104 				FTL_ERRLOG(dev, "Layout initialization ERROR, two regions overlap, "
105 					   "%s and %s\n", r1->name, r2->name);
106 				return -1;
107 			}
108 		}
109 	}
110 
111 	return 0;
112 }
113 
114 static uint64_t
115 get_num_user_lbas(struct spdk_ftl_dev *dev)
116 {
117 	uint64_t blocks;
118 
119 	blocks = dev->num_bands * ftl_get_num_blocks_in_band(dev);
120 	blocks = (blocks * (100 - dev->conf.overprovisioning)) / 100;
121 
122 	return blocks;
123 }
124 
125 static void
126 set_region_bdev_nvc(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
127 {
128 	reg->bdev_desc = dev->nv_cache.bdev_desc;
129 	reg->ioch = dev->nv_cache.cache_ioch;
130 	reg->vss_blksz = dev->nv_cache.md_size;
131 }
132 
133 static void
134 set_region_bdev_btm(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
135 {
136 	reg->bdev_desc = dev->base_bdev_desc;
137 	reg->ioch = dev->base_ioch;
138 	reg->vss_blksz = 0;
139 }
140 
141 static int
142 setup_layout_nvc(struct spdk_ftl_dev *dev)
143 {
144 	int region_type;
145 	uint64_t left, offset = 0, l2p_blocks;
146 	struct ftl_layout *layout = &dev->layout;
147 	struct ftl_layout_region *region, *mirror;
148 	static const char *p2l_region_name[] = {
149 		"p2l0",
150 		"p2l1",
151 		"p2l2",
152 		"p2l3"
153 	};
154 
155 #ifdef SPDK_FTL_VSS_EMU
156 	/* Skip the already init`d VSS region */
157 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
158 	offset += region->current.blocks;
159 
160 	if (offset >= layout->nvc.total_blocks) {
161 		goto error;
162 	}
163 #endif
164 
165 	/* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */
166 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
167 	offset += region->current.blocks;
168 
169 	/* Initialize L2P region */
170 	if (offset >= layout->nvc.total_blocks) {
171 		goto error;
172 	}
173 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_L2P];
174 	region->type = FTL_LAYOUT_REGION_TYPE_L2P;
175 	region->name = "l2p";
176 	region->current.version = 0;
177 	region->prev.version = 0;
178 	region->current.offset = offset;
179 	region->current.blocks = blocks_region(dev, layout->l2p.addr_size * dev->num_lbas);
180 	set_region_bdev_nvc(region, dev);
181 	offset += region->current.blocks;
182 
183 	/* Initialize band info metadata */
184 	if (offset >= layout->nvc.total_blocks) {
185 		goto error;
186 	}
187 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
188 	region->type = FTL_LAYOUT_REGION_TYPE_BAND_MD;
189 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
190 	region->name = "band_md";
191 	region->current.version = region->prev.version = FTL_BAND_VERSION_CURRENT;
192 	region->current.offset = offset;
193 	region->current.blocks = blocks_region(dev, ftl_get_num_bands(dev) * sizeof(struct ftl_band_md));
194 	region->entry_size = sizeof(struct ftl_band_md) / FTL_BLOCK_SIZE;
195 	region->num_entries = ftl_get_num_bands(dev);
196 	set_region_bdev_nvc(region, dev);
197 	offset += region->current.blocks;
198 
199 	/* Initialize band info metadata mirror */
200 	if (offset >= layout->nvc.total_blocks) {
201 		goto error;
202 	}
203 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR];
204 	*mirror = *region;
205 	mirror->type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
206 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
207 	mirror->name = "band_md_mirror";
208 	mirror->current.offset += region->current.blocks;
209 	offset += mirror->current.blocks;
210 
211 	if (offset >= layout->nvc.total_blocks) {
212 		goto error;
213 	}
214 
215 	/*
216 	 * Initialize P2L checkpointing regions
217 	 */
218 	SPDK_STATIC_ASSERT(SPDK_COUNTOF(p2l_region_name) == FTL_LAYOUT_REGION_TYPE_P2L_COUNT,
219 			   "Incorrect # of P2L region names");
220 	for (region_type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN;
221 	     region_type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX;
222 	     region_type++) {
223 		if (offset >= layout->nvc.total_blocks) {
224 			goto error;
225 		}
226 		region = &layout->region[region_type];
227 		region->type = region_type;
228 		region->name = p2l_region_name[region_type - FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN];
229 		region->current.version = FTL_P2L_VERSION_CURRENT;
230 		region->prev.version = FTL_P2L_VERSION_CURRENT;
231 		region->current.offset = offset;
232 		region->current.blocks = blocks_region(dev, layout->p2l.ckpt_pages * FTL_BLOCK_SIZE);
233 		region->entry_size = 1;
234 		region->num_entries = region->current.blocks;
235 		set_region_bdev_nvc(region, dev);
236 		offset += region->current.blocks;
237 	}
238 
239 	/*
240 	 * Initialize trim metadata region
241 	 */
242 	if (offset >= layout->nvc.total_blocks) {
243 		goto error;
244 	}
245 	l2p_blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
246 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_MD];
247 	region->type = FTL_LAYOUT_REGION_TYPE_TRIM_MD;
248 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR;
249 	region->name = "trim_md";
250 	region->current.version = 0;
251 	region->prev.version = 0;
252 	region->current.offset = offset;
253 	region->current.blocks = blocks_region(dev, l2p_blocks * sizeof(uint64_t));
254 	region->entry_size = 1;
255 	region->num_entries = region->current.blocks;
256 	set_region_bdev_nvc(region, dev);
257 	offset += region->current.blocks;
258 
259 	/* Initialize trim metadata mirror region */
260 	if (offset >= layout->nvc.total_blocks) {
261 		goto error;
262 	}
263 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR];
264 	*mirror = *region;
265 	mirror->type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR;
266 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
267 	mirror->name = "trim_md_mirror";
268 	mirror->current.offset += region->current.blocks;
269 	offset += mirror->current.blocks;
270 
271 	/*
272 	 * Initialize NV Cache metadata
273 	 */
274 	if (offset >= layout->nvc.total_blocks) {
275 		goto error;
276 	}
277 
278 	left = layout->nvc.total_blocks - offset;
279 	layout->nvc.chunk_data_blocks =
280 		FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_band(dev)) / FTL_BLOCK_SIZE;
281 	layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE;
282 	layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) /
283 				  FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_band(dev));
284 	layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
285 
286 	if (0 == layout->nvc.chunk_count) {
287 		goto error;
288 	}
289 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD];
290 	region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD;
291 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
292 	region->name = "nvc_md";
293 	region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT;
294 	region->current.offset = offset;
295 	region->current.blocks = blocks_region(dev, layout->nvc.chunk_count *
296 					       sizeof(struct ftl_nv_cache_chunk_md));
297 	region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE;
298 	region->num_entries = layout->nvc.chunk_count;
299 	set_region_bdev_nvc(region, dev);
300 	offset += region->current.blocks;
301 
302 	/*
303 	 * Initialize NV Cache metadata mirror
304 	 */
305 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR];
306 	*mirror = *region;
307 	mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
308 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
309 	mirror->name = "nvc_md_mirror";
310 	mirror->current.offset += region->current.blocks;
311 	offset += mirror->current.blocks;
312 
313 	/*
314 	 * Initialize data region on NV cache
315 	 */
316 	if (offset >= layout->nvc.total_blocks) {
317 		goto error;
318 	}
319 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC];
320 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC;
321 	region->name = "data_nvc";
322 	region->current.version = region->prev.version = 0;
323 	region->current.offset = offset;
324 	region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks;
325 	set_region_bdev_nvc(region, dev);
326 	offset += region->current.blocks;
327 
328 	left = layout->nvc.total_blocks - offset;
329 	if (left > layout->nvc.chunk_data_blocks) {
330 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
331 		return -1;
332 	}
333 
334 	if (offset > layout->nvc.total_blocks) {
335 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
336 		goto error;
337 	}
338 
339 	return 0;
340 
341 error:
342 	FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
343 	return -1;
344 }
345 
346 static ftl_addr
347 layout_base_offset(struct spdk_ftl_dev *dev)
348 {
349 	ftl_addr addr;
350 
351 	addr = dev->num_bands * ftl_get_num_blocks_in_band(dev);
352 	return addr;
353 }
354 
355 static int
356 setup_layout_base(struct spdk_ftl_dev *dev)
357 {
358 	uint64_t left, offset;
359 	struct ftl_layout *layout = &dev->layout;
360 	struct ftl_layout_region *region;
361 	uint64_t data_base_alignment = 8 * ftl_bitmap_buffer_alignment;
362 	/* Allocating a ftl_bitmap requires a 8B input buffer alignment, since we're reusing the global valid map md buffer
363 	 * this means that each band starting address needs to be aligned too - each device sector takes 1b in the valid map,
364 	 * so 64 sectors (8*8) is the needed alignment
365 	 */
366 
367 	layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev);
368 	layout->base.user_blocks = ftl_band_user_blocks(dev->bands);
369 
370 	/* Base device layout is following:
371 	 * - superblock
372 	 * - data
373 	 * - valid map
374 	 */
375 	offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks;
376 	offset = SPDK_ALIGN_CEIL(offset, data_base_alignment);
377 
378 	/* Setup data region on base device */
379 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
380 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE;
381 	region->name = "data_btm";
382 	region->current.version = region->prev.version = 0;
383 	region->current.offset = offset;
384 	region->current.blocks = layout_base_offset(dev);
385 	set_region_bdev_btm(region, dev);
386 
387 	offset += region->current.blocks;
388 
389 	/* Setup validity map */
390 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VALID_MAP];
391 	region->type = FTL_LAYOUT_REGION_TYPE_VALID_MAP;
392 	region->name = "vmap";
393 	region->current.version = region->prev.version = 0;
394 	region->current.offset = offset;
395 	region->current.blocks = blocks_region(dev, spdk_divide_round_up(
396 			layout->base.total_blocks + layout->nvc.total_blocks, 8));
397 	set_region_bdev_btm(region, dev);
398 	offset += region->current.blocks;
399 
400 	/* Checking for underflow */
401 	left = layout->base.total_blocks - offset;
402 	if (left > layout->base.total_blocks) {
403 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
404 		return -1;
405 	}
406 
407 	if (offset > layout->base.total_blocks) {
408 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
409 		return -1;
410 	}
411 
412 	return 0;
413 }
414 
415 int
416 ftl_layout_setup(struct spdk_ftl_dev *dev)
417 {
418 	const struct spdk_bdev *bdev;
419 	struct ftl_layout *layout = &dev->layout;
420 	uint64_t i;
421 	uint64_t num_lbas;
422 
423 	bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
424 	layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
425 
426 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
427 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
428 
429 	/* Initialize mirrors types */
430 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
431 		if (i == FTL_LAYOUT_REGION_TYPE_SB) {
432 			/* Super block has been already initialized */
433 			continue;
434 		}
435 
436 		layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
437 	}
438 
439 	/*
440 	 * Initialize L2P information
441 	 */
442 	num_lbas = get_num_user_lbas(dev);
443 	if (dev->num_lbas == 0) {
444 		assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
445 		dev->num_lbas = num_lbas;
446 		dev->sb->lba_cnt = num_lbas;
447 	} else if (dev->num_lbas != num_lbas) {
448 		FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
449 		return -EINVAL;
450 	}
451 	layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
452 	layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
453 	layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
454 
455 	/* Setup P2L ckpt */
456 	layout->p2l.ckpt_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev), dev->xfer_size);
457 
458 	if (setup_layout_nvc(dev)) {
459 		return -EINVAL;
460 	}
461 
462 	if (setup_layout_base(dev)) {
463 		return -EINVAL;
464 	}
465 
466 	if (ftl_validate_regions(dev, layout)) {
467 		return -EINVAL;
468 	}
469 
470 	FTL_NOTICELOG(dev, "Base device capacity:         %.2f MiB\n",
471 		      blocks2mib(layout->base.total_blocks));
472 	FTL_NOTICELOG(dev, "NV cache device capacity:       %.2f MiB\n",
473 		      blocks2mib(layout->nvc.total_blocks));
474 	FTL_NOTICELOG(dev, "L2P entries:                    %"PRIu64"\n", dev->num_lbas);
475 	FTL_NOTICELOG(dev, "L2P address size:               %"PRIu64"\n", layout->l2p.addr_size);
476 	FTL_NOTICELOG(dev, "P2L checkpoint pages:           %"PRIu64"\n", layout->p2l.ckpt_pages);
477 
478 	return 0;
479 }
480 
481 #ifdef SPDK_FTL_VSS_EMU
482 void
483 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
484 {
485 	const struct spdk_bdev *bdev;
486 	struct ftl_layout *layout = &dev->layout;
487 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
488 
489 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL);
490 
491 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
492 	region->type = FTL_LAYOUT_REGION_TYPE_VSS;
493 	region->name = "vss";
494 	region->current.version = region->prev.version = 0;
495 	region->current.offset = 0;
496 
497 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
498 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
499 	region->current.blocks = blocks_region(dev, dev->nv_cache.md_size * layout->nvc.total_blocks);
500 
501 	region->vss_blksz = 0;
502 	region->bdev_desc = dev->nv_cache.bdev_desc;
503 	region->ioch = dev->nv_cache.cache_ioch;
504 
505 	assert(region->bdev_desc != NULL);
506 	assert(region->ioch != NULL);
507 }
508 #endif
509 
510 int
511 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
512 {
513 	struct ftl_layout *layout = &dev->layout;
514 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
515 	uint64_t total_blocks, offset, left;
516 
517 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
518 
519 	/* Initialize superblock region */
520 	region->type = FTL_LAYOUT_REGION_TYPE_SB;
521 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
522 	region->name = "sb";
523 	region->current.version = FTL_SB_VERSION_CURRENT;
524 	region->prev.version = FTL_SB_VERSION_CURRENT;
525 	region->current.offset = 0;
526 
527 	/*
528 	 * VSS region must go first in case SB to make calculating its relative size easier
529 	 */
530 #ifdef SPDK_FTL_VSS_EMU
531 	region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset +
532 				 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks;
533 #endif
534 
535 	region->current.blocks = superblock_region_blocks(dev);
536 	region->vss_blksz = 0;
537 	region->bdev_desc = dev->nv_cache.bdev_desc;
538 	region->ioch = dev->nv_cache.cache_ioch;
539 
540 	assert(region->bdev_desc != NULL);
541 	assert(region->ioch != NULL);
542 
543 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
544 	region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
545 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
546 	region->name = "sb_mirror";
547 	region->current.version = FTL_SB_VERSION_CURRENT;
548 	region->prev.version = FTL_SB_VERSION_CURRENT;
549 	region->current.offset = 0;
550 	region->current.blocks = superblock_region_blocks(dev);
551 	set_region_bdev_btm(region, dev);
552 
553 	/* Check if SB can be stored at the end of base device */
554 	total_blocks = spdk_bdev_get_num_blocks(
555 			       spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
556 	offset = region->current.offset + region->current.blocks;
557 	left = total_blocks - offset;
558 	if ((left > total_blocks) || (offset > total_blocks)) {
559 		FTL_ERRLOG(dev, "Error when setup base device super block\n");
560 		return -1;
561 	}
562 
563 	return 0;
564 }
565 
566 void
567 ftl_layout_dump(struct spdk_ftl_dev *dev)
568 {
569 	struct ftl_layout *layout = &dev->layout;
570 	int i;
571 
572 	FTL_NOTICELOG(dev, "NV cache layout:\n");
573 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
574 		if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) {
575 			dump_region(dev, &layout->region[i]);
576 		}
577 	}
578 	FTL_NOTICELOG(dev, "Base device layout:\n");
579 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
580 		if (layout->region[i].bdev_desc == dev->base_bdev_desc) {
581 			dump_region(dev, &layout->region[i]);
582 		}
583 	}
584 }
585 
586 uint64_t
587 ftl_layout_base_md_blocks(struct spdk_ftl_dev *dev)
588 {
589 	const struct spdk_bdev *bdev;
590 	uint64_t md_blocks = 0, total_blocks = 0;
591 
592 	bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
593 	total_blocks += spdk_bdev_get_num_blocks(bdev);
594 
595 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
596 	total_blocks += spdk_bdev_get_num_blocks(bdev);
597 
598 	/* Count space needed for validity map */
599 	md_blocks += blocks_region(dev, spdk_divide_round_up(total_blocks, 8));
600 
601 	/* Count space needed for superblock */
602 	md_blocks += superblock_region_blocks(dev);
603 	return md_blocks;
604 }
605