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