xref: /spdk/lib/ftl/ftl_layout.c (revision ea8f5b27612fa03698a9ce3ad4bd37765d9cdfa5)
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;
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 NV Cache metadata
227 	 */
228 	if (offset >= layout->nvc.total_blocks) {
229 		goto error;
230 	}
231 
232 	left = layout->nvc.total_blocks - offset;
233 	layout->nvc.chunk_data_blocks =
234 		FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_band(dev)) / FTL_BLOCK_SIZE;
235 	layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE;
236 	layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) /
237 				  FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_band(dev));
238 	layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
239 
240 	if (0 == layout->nvc.chunk_count) {
241 		goto error;
242 	}
243 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD];
244 	region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD;
245 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
246 	region->name = "nvc_md";
247 	region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT;
248 	region->current.offset = offset;
249 	region->current.blocks = blocks_region(layout->nvc.chunk_count *
250 					       sizeof(struct ftl_nv_cache_chunk_md));
251 	region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE;
252 	region->num_entries = layout->nvc.chunk_count;
253 	set_region_bdev_nvc(region, dev);
254 	offset += region->current.blocks;
255 
256 	/*
257 	 * Initialize NV Cache metadata mirror
258 	 */
259 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR];
260 	*mirror = *region;
261 	mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
262 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
263 	mirror->name = "nvc_md_mirror";
264 	mirror->current.offset += region->current.blocks;
265 	offset += mirror->current.blocks;
266 
267 	/*
268 	 * Initialize data region on NV cache
269 	 */
270 	if (offset >= layout->nvc.total_blocks) {
271 		goto error;
272 	}
273 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC];
274 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC;
275 	region->name = "data_nvc";
276 	region->current.version = region->prev.version = 0;
277 	region->current.offset = offset;
278 	region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks;
279 	set_region_bdev_nvc(region, dev);
280 	offset += region->current.blocks;
281 
282 	left = layout->nvc.total_blocks - offset;
283 	if (left > layout->nvc.chunk_data_blocks) {
284 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
285 		return -1;
286 	}
287 
288 	if (offset > layout->nvc.total_blocks) {
289 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
290 		goto error;
291 	}
292 
293 	return 0;
294 
295 error:
296 	FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
297 	return -1;
298 }
299 
300 static ftl_addr
301 layout_base_offset(struct spdk_ftl_dev *dev)
302 {
303 	ftl_addr addr;
304 
305 	addr = dev->num_bands * ftl_get_num_blocks_in_band(dev);
306 	return addr;
307 }
308 
309 static int
310 setup_layout_base(struct spdk_ftl_dev *dev)
311 {
312 	uint64_t left, offset;
313 	struct ftl_layout *layout = &dev->layout;
314 	struct ftl_layout_region *region;
315 
316 	layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev);
317 	layout->base.user_blocks = ftl_band_user_blocks(dev->bands);
318 
319 	/* Base device layout is following:
320 	 * - data
321 	 * - superblock
322 	 * - valid map
323 	 *
324 	 * Superblock has been already configured, its offset marks the end of the data region
325 	 */
326 	offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset;
327 
328 	/* Setup data region on base device */
329 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
330 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE;
331 	region->name = "data_btm";
332 	region->current.version = region->prev.version = 0;
333 	region->current.offset = 0;
334 	region->current.blocks = offset;
335 	set_region_bdev_btm(region, dev);
336 
337 	/* Move offset after base superblock */
338 	offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks;
339 
340 	/* Setup validity map */
341 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VALID_MAP];
342 	region->type = FTL_LAYOUT_REGION_TYPE_VALID_MAP;
343 	region->name = "vmap";
344 	region->current.version = region->prev.version = 0;
345 	region->current.offset = offset;
346 	region->current.blocks = blocks_region(spdk_divide_round_up(
347 			layout->base.total_blocks + layout->nvc.total_blocks, 8));
348 	set_region_bdev_btm(region, dev);
349 	offset += region->current.blocks;
350 
351 	/* Checking for underflow */
352 	left = layout->base.total_blocks - offset;
353 	if (left > layout->base.total_blocks) {
354 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
355 		return -1;
356 	}
357 
358 	if (offset > layout->base.total_blocks) {
359 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
360 		return -1;
361 	}
362 
363 	return 0;
364 }
365 
366 int
367 ftl_layout_setup(struct spdk_ftl_dev *dev)
368 {
369 	const struct spdk_bdev *bdev;
370 	struct ftl_layout *layout = &dev->layout;
371 	uint64_t i;
372 	uint64_t num_lbas;
373 
374 	bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
375 	layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
376 
377 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
378 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
379 
380 	/* Initialize mirrors types */
381 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
382 		if (i == FTL_LAYOUT_REGION_TYPE_SB) {
383 			/* Super block has been already initialized */
384 			continue;
385 		}
386 
387 		layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
388 	}
389 
390 	/*
391 	 * Initialize L2P information
392 	 */
393 	num_lbas = get_num_user_lbas(dev);
394 	if (dev->num_lbas == 0) {
395 		assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
396 		dev->num_lbas = num_lbas;
397 		dev->sb->lba_cnt = num_lbas;
398 	} else if (dev->num_lbas != num_lbas) {
399 		FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
400 		return -EINVAL;
401 	}
402 	layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
403 	layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
404 	layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
405 
406 	/* Setup P2L ckpt */
407 	layout->p2l.ckpt_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev), dev->xfer_size);
408 
409 	if (setup_layout_nvc(dev)) {
410 		return -EINVAL;
411 	}
412 
413 	if (setup_layout_base(dev)) {
414 		return -EINVAL;
415 	}
416 
417 	if (ftl_validate_regions(dev, layout)) {
418 		return -EINVAL;
419 	}
420 
421 	FTL_NOTICELOG(dev, "Base device capacity:         %.2f MiB\n",
422 		      blocks2mib(layout->base.total_blocks));
423 	FTL_NOTICELOG(dev, "NV cache device capacity:       %.2f MiB\n",
424 		      blocks2mib(layout->nvc.total_blocks));
425 	FTL_NOTICELOG(dev, "L2P entries:                    %"PRIu64"\n", dev->num_lbas);
426 	FTL_NOTICELOG(dev, "L2P address size:               %"PRIu64"\n", layout->l2p.addr_size);
427 	FTL_NOTICELOG(dev, "P2L checkpoint pages:           %"PRIu64"\n", layout->p2l.ckpt_pages);
428 
429 	return 0;
430 }
431 
432 #ifdef SPDK_FTL_VSS_EMU
433 void
434 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
435 {
436 	const struct spdk_bdev *bdev;
437 	struct ftl_layout *layout = &dev->layout;
438 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
439 
440 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL);
441 
442 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
443 	region->type = FTL_LAYOUT_REGION_TYPE_VSS;
444 	region->name = "vss";
445 	region->current.version = region->prev.version = 0;
446 	region->current.offset = 0;
447 
448 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
449 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
450 	region->current.blocks = blocks_region(dev->nv_cache.md_size * layout->nvc.total_blocks);
451 
452 	region->vss_blksz = 0;
453 	region->bdev_desc = dev->nv_cache.bdev_desc;
454 	region->ioch = dev->nv_cache.cache_ioch;
455 
456 	assert(region->bdev_desc != NULL);
457 	assert(region->ioch != NULL);
458 }
459 #endif
460 
461 int
462 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
463 {
464 	struct ftl_layout *layout = &dev->layout;
465 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
466 	uint64_t total_blocks, offset, left;
467 
468 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
469 
470 	/* Initialize superblock region */
471 	region->type = FTL_LAYOUT_REGION_TYPE_SB;
472 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
473 	region->name = "sb";
474 	region->current.version = FTL_METADATA_VERSION_CURRENT;
475 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
476 	region->current.offset = 0;
477 
478 	/*
479 	 * VSS region must go first in case SB to make calculating its relative size easier
480 	 */
481 #ifdef SPDK_FTL_VSS_EMU
482 	region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset +
483 				 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks;
484 #endif
485 
486 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
487 	region->vss_blksz = 0;
488 	region->bdev_desc = dev->nv_cache.bdev_desc;
489 	region->ioch = dev->nv_cache.cache_ioch;
490 
491 	assert(region->bdev_desc != NULL);
492 	assert(region->ioch != NULL);
493 
494 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
495 	region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
496 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX;
497 	region->name = "sb_mirror";
498 	region->current.version = FTL_METADATA_VERSION_CURRENT;
499 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
500 	/* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts
501 	 * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore)
502 	 */
503 	region->current.offset = layout_base_offset(dev);
504 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
505 	set_region_bdev_btm(region, dev);
506 
507 	/* Check if SB can be stored at the end of base device */
508 	total_blocks = spdk_bdev_get_num_blocks(
509 			       spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
510 	offset = region->current.offset + region->current.blocks;
511 	left = total_blocks - offset;
512 	if ((left > total_blocks) || (offset > total_blocks)) {
513 		FTL_ERRLOG(dev, "Error when setup base device super block\n");
514 		return -1;
515 	}
516 
517 	return 0;
518 }
519 
520 void
521 ftl_layout_dump(struct spdk_ftl_dev *dev)
522 {
523 	struct ftl_layout *layout = &dev->layout;
524 	int i;
525 	FTL_NOTICELOG(dev, "NV cache layout:\n");
526 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
527 		if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) {
528 			dump_region(dev, &layout->region[i]);
529 		}
530 	}
531 	FTL_NOTICELOG(dev, "Bottom device layout:\n");
532 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
533 		if (layout->region[i].bdev_desc == dev->base_bdev_desc) {
534 			dump_region(dev, &layout->region[i]);
535 		}
536 	}
537 }
538