xref: /spdk/lib/ftl/ftl_layout.c (revision 510f4c134a21b45ff3a5add9ebc6c6cf7e49aeab)
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 	uint64_t left, offset = 0;
131 	struct ftl_layout *layout = &dev->layout;
132 	struct ftl_layout_region *region, *mirror;
133 
134 #ifdef SPDK_FTL_VSS_EMU
135 	/* Skip the already init`d VSS region */
136 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
137 	offset += region->current.blocks;
138 
139 	if (offset >= layout->nvc.total_blocks) {
140 		goto error;
141 	}
142 #endif
143 
144 	/* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */
145 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
146 	offset += region->current.blocks;
147 
148 	/* Initialize L2P region */
149 	if (offset >= layout->nvc.total_blocks) {
150 		goto error;
151 	}
152 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_L2P];
153 	region->type = FTL_LAYOUT_REGION_TYPE_L2P;
154 	region->name = "l2p";
155 	region->current.version = 0;
156 	region->prev.version = 0;
157 	region->current.offset = offset;
158 	region->current.blocks = blocks_region(layout->l2p.addr_size * dev->num_lbas);
159 	set_region_bdev_nvc(region, dev);
160 	offset += region->current.blocks;
161 
162 	/* Initialize band info metadata */
163 	if (offset >= layout->nvc.total_blocks) {
164 		goto error;
165 	}
166 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
167 	region->type = FTL_LAYOUT_REGION_TYPE_BAND_MD;
168 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
169 	region->name = "band_md";
170 	region->current.version = region->prev.version = FTL_BAND_VERSION_CURRENT;
171 	region->current.offset = offset;
172 	region->current.blocks = blocks_region(ftl_get_num_bands(dev) * sizeof(struct ftl_band_md));
173 	region->entry_size = sizeof(struct ftl_band_md) / FTL_BLOCK_SIZE;
174 	region->num_entries = ftl_get_num_bands(dev);
175 	set_region_bdev_nvc(region, dev);
176 	offset += region->current.blocks;
177 
178 	/* Initialize band info metadata mirror */
179 	if (offset >= layout->nvc.total_blocks) {
180 		goto error;
181 	}
182 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR];
183 	*mirror = *region;
184 	mirror->type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
185 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
186 	mirror->name = "band_md_mirror";
187 	mirror->current.offset += region->current.blocks;
188 	offset += mirror->current.blocks;
189 
190 	if (offset >= layout->nvc.total_blocks) {
191 		goto error;
192 	}
193 
194 	/*
195 	 * Initialize NV Cache metadata
196 	 */
197 	if (offset >= layout->nvc.total_blocks) {
198 		goto error;
199 	}
200 
201 	left = layout->nvc.total_blocks - offset;
202 	layout->nvc.chunk_data_blocks =
203 		FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_band(dev)) / FTL_BLOCK_SIZE;
204 	layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE;
205 	layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) /
206 				  FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_band(dev));
207 	layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
208 
209 	if (0 == layout->nvc.chunk_count) {
210 		goto error;
211 	}
212 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD];
213 	region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD;
214 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
215 	region->name = "nvc_md";
216 	region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT;
217 	region->current.offset = offset;
218 	region->current.blocks = blocks_region(layout->nvc.chunk_count *
219 					       sizeof(struct ftl_nv_cache_chunk_md));
220 	region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE;
221 	region->num_entries = layout->nvc.chunk_count;
222 	set_region_bdev_nvc(region, dev);
223 	offset += region->current.blocks;
224 
225 	/*
226 	 * Initialize NV Cache metadata mirror
227 	 */
228 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR];
229 	*mirror = *region;
230 	mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
231 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
232 	mirror->name = "nvc_md_mirror";
233 	mirror->current.offset += region->current.blocks;
234 	offset += mirror->current.blocks;
235 
236 	/*
237 	 * Initialize data region on NV cache
238 	 */
239 	if (offset >= layout->nvc.total_blocks) {
240 		goto error;
241 	}
242 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC];
243 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC;
244 	region->name = "data_nvc";
245 	region->current.version = region->prev.version = 0;
246 	region->current.offset = offset;
247 	region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks;
248 	set_region_bdev_nvc(region, dev);
249 	offset += region->current.blocks;
250 
251 	left = layout->nvc.total_blocks - offset;
252 	if (left > layout->nvc.chunk_data_blocks) {
253 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
254 		return -1;
255 	}
256 
257 	if (offset > layout->nvc.total_blocks) {
258 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
259 		goto error;
260 	}
261 
262 	return 0;
263 
264 error:
265 	FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
266 	return -1;
267 }
268 
269 static ftl_addr
270 layout_base_offset(struct spdk_ftl_dev *dev)
271 {
272 	ftl_addr addr;
273 
274 	addr = dev->num_bands * ftl_get_num_blocks_in_band(dev);
275 	return addr;
276 }
277 
278 static int
279 setup_layout_base(struct spdk_ftl_dev *dev)
280 {
281 	uint64_t left, offset;
282 	struct ftl_layout *layout = &dev->layout;
283 	struct ftl_layout_region *region;
284 
285 	layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev);
286 	layout->base.user_blocks = ftl_band_user_blocks(dev->bands);
287 
288 	/* Base device layout is following:
289 	 * - data
290 	 * - superblock
291 	 * - valid map
292 	 *
293 	 * Superblock has been already configured, its offset marks the end of the data region
294 	 */
295 	offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset;
296 
297 	/* Setup data region on base device */
298 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
299 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE;
300 	region->name = "data_btm";
301 	region->current.version = region->prev.version = 0;
302 	region->current.offset = 0;
303 	region->current.blocks = offset;
304 	set_region_bdev_btm(region, dev);
305 
306 	/* Move offset after base superblock */
307 	offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks;
308 
309 	/* Setup validity map */
310 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VALID_MAP];
311 	region->type = FTL_LAYOUT_REGION_TYPE_VALID_MAP;
312 	region->name = "vmap";
313 	region->current.version = region->prev.version = 0;
314 	region->current.offset = offset;
315 	region->current.blocks = blocks_region(spdk_divide_round_up(
316 			layout->base.total_blocks + layout->nvc.total_blocks, 8));
317 	set_region_bdev_btm(region, dev);
318 	offset += region->current.blocks;
319 
320 	/* Checking for underflow */
321 	left = layout->base.total_blocks - offset;
322 	if (left > layout->base.total_blocks) {
323 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
324 		return -1;
325 	}
326 
327 	if (offset > layout->base.total_blocks) {
328 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
329 		return -1;
330 	}
331 
332 	return 0;
333 }
334 
335 int
336 ftl_layout_setup(struct spdk_ftl_dev *dev)
337 {
338 	const struct spdk_bdev *bdev;
339 	struct ftl_layout *layout = &dev->layout;
340 	uint64_t i;
341 	uint64_t num_lbas;
342 
343 	bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
344 	layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
345 
346 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
347 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
348 
349 	/* Initialize mirrors types */
350 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
351 		if (i == FTL_LAYOUT_REGION_TYPE_SB) {
352 			/* Super block has been already initialized */
353 			continue;
354 		}
355 
356 		layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
357 	}
358 
359 	/*
360 	 * Initialize L2P information
361 	 */
362 	num_lbas = get_num_user_lbas(dev);
363 	if (dev->num_lbas == 0) {
364 		assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
365 		dev->num_lbas = num_lbas;
366 		dev->sb->lba_cnt = num_lbas;
367 	} else if (dev->num_lbas != num_lbas) {
368 		FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
369 		return -EINVAL;
370 	}
371 	layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
372 	layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
373 	layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
374 
375 	if (setup_layout_nvc(dev)) {
376 		return -EINVAL;
377 	}
378 
379 	if (setup_layout_base(dev)) {
380 		return -EINVAL;
381 	}
382 
383 	if (ftl_validate_regions(dev, layout)) {
384 		return -EINVAL;
385 	}
386 
387 	FTL_NOTICELOG(dev, "Base device capacity:         %.2f MiB\n",
388 		      blocks2mib(layout->base.total_blocks));
389 	FTL_NOTICELOG(dev, "NV cache device capacity:       %.2f MiB\n",
390 		      blocks2mib(layout->nvc.total_blocks));
391 	FTL_NOTICELOG(dev, "L2P entries:                    %"PRIu64"\n",
392 		      dev->num_lbas);
393 	FTL_NOTICELOG(dev, "L2P address size:               %"PRIu64"\n",
394 		      layout->l2p.addr_size);
395 
396 	return 0;
397 }
398 
399 #ifdef SPDK_FTL_VSS_EMU
400 void
401 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
402 {
403 	const struct spdk_bdev *bdev;
404 	struct ftl_layout *layout = &dev->layout;
405 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
406 
407 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL);
408 
409 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
410 	region->type = FTL_LAYOUT_REGION_TYPE_VSS;
411 	region->name = "vss";
412 	region->current.version = region->prev.version = 0;
413 	region->current.offset = 0;
414 
415 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
416 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
417 	region->current.blocks = blocks_region(dev->nv_cache.md_size * layout->nvc.total_blocks);
418 
419 	region->vss_blksz = 0;
420 	region->bdev_desc = dev->nv_cache.bdev_desc;
421 	region->ioch = dev->nv_cache.cache_ioch;
422 
423 	assert(region->bdev_desc != NULL);
424 	assert(region->ioch != NULL);
425 }
426 #endif
427 
428 int
429 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
430 {
431 	struct ftl_layout *layout = &dev->layout;
432 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
433 	uint64_t total_blocks, offset, left;
434 
435 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
436 
437 	/* Initialize superblock region */
438 	region->type = FTL_LAYOUT_REGION_TYPE_SB;
439 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
440 	region->name = "sb";
441 	region->current.version = FTL_METADATA_VERSION_CURRENT;
442 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
443 	region->current.offset = 0;
444 
445 	/*
446 	 * VSS region must go first in case SB to make calculating its relative size easier
447 	 */
448 #ifdef SPDK_FTL_VSS_EMU
449 	region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset +
450 				 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks;
451 #endif
452 
453 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
454 	region->vss_blksz = 0;
455 	region->bdev_desc = dev->nv_cache.bdev_desc;
456 	region->ioch = dev->nv_cache.cache_ioch;
457 
458 	assert(region->bdev_desc != NULL);
459 	assert(region->ioch != NULL);
460 
461 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
462 	region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
463 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX;
464 	region->name = "sb_mirror";
465 	region->current.version = FTL_METADATA_VERSION_CURRENT;
466 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
467 	/* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts
468 	 * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore)
469 	 */
470 	region->current.offset = layout_base_offset(dev);
471 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
472 	set_region_bdev_btm(region, dev);
473 
474 	/* Check if SB can be stored at the end of base device */
475 	total_blocks = spdk_bdev_get_num_blocks(
476 			       spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
477 	offset = region->current.offset + region->current.blocks;
478 	left = total_blocks - offset;
479 	if ((left > total_blocks) || (offset > total_blocks)) {
480 		FTL_ERRLOG(dev, "Error when setup base device super block\n");
481 		return -1;
482 	}
483 
484 	return 0;
485 }
486 
487 void
488 ftl_layout_dump(struct spdk_ftl_dev *dev)
489 {
490 	struct ftl_layout *layout = &dev->layout;
491 	int i;
492 	FTL_NOTICELOG(dev, "NV cache layout:\n");
493 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
494 		if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) {
495 			dump_region(dev, &layout->region[i]);
496 		}
497 	}
498 	FTL_NOTICELOG(dev, "Bottom device layout:\n");
499 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
500 		if (layout->region[i].bdev_desc == dev->base_bdev_desc) {
501 			dump_region(dev, &layout->region[i]);
502 		}
503 	}
504 }
505