xref: /spdk/lib/ftl/ftl_layout.c (revision 081f080a49a3dbe31acb2a8557db90b08bbeaf9c)
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_layout.h"
11 #include "ftl_nv_cache.h"
12 #include "ftl_sb.h"
13 
14 #define FTL_NV_CACHE_CHUNK_DATA_SIZE(blocks) ((uint64_t)blocks * FTL_BLOCK_SIZE)
15 #define FTL_NV_CACHE_CHUNK_SIZE(blocks) \
16 	(FTL_NV_CACHE_CHUNK_DATA_SIZE(blocks) + (2 * FTL_NV_CACHE_CHUNK_MD_SIZE))
17 
18 static inline float
19 blocks2mib(uint64_t blocks)
20 {
21 	float result;
22 
23 	result = blocks;
24 	result *= FTL_BLOCK_SIZE;
25 	result /= 1024UL;
26 	result /= 1024UL;
27 
28 	return result;
29 }
30 /* TODO: This should be aligned to the write unit size of the device a given piece of md is on.
31  * The tricky part is to make sure interpreting old alignment values will still be valid...
32  */
33 #define FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS 32ULL
34 #define FTL_LAYOUT_REGION_ALIGNMENT_BYTES (FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS * FTL_BLOCK_SIZE)
35 
36 static inline uint64_t
37 blocks_region(uint64_t bytes)
38 {
39 	const uint64_t alignment = FTL_LAYOUT_REGION_ALIGNMENT_BYTES;
40 	uint64_t result;
41 
42 	result = spdk_divide_round_up(bytes, alignment);
43 	result *= alignment;
44 	result /= FTL_BLOCK_SIZE;
45 
46 	return result;
47 }
48 
49 static void
50 dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
51 {
52 	assert(!(region->current.offset % FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS));
53 	assert(!(region->current.blocks % FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS));
54 
55 	FTL_NOTICELOG(dev, "Region %s\n", region->name);
56 	FTL_NOTICELOG(dev, "	offset:                      %.2f MiB\n",
57 		      blocks2mib(region->current.offset));
58 	FTL_NOTICELOG(dev, "	blocks:                      %.2f MiB\n",
59 		      blocks2mib(region->current.blocks));
60 }
61 
62 int
63 ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
64 {
65 	uint64_t i, j;
66 
67 	/* Validate if regions doesn't overlap each other  */
68 	/* TODO: major upgrades: keep track of and validate free_nvc/free_btm regions */
69 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
70 		struct ftl_layout_region *r1 = &layout->region[i];
71 
72 		for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
73 			struct ftl_layout_region *r2 = &layout->region[j];
74 
75 			if (r1->bdev_desc != r2->bdev_desc) {
76 				continue;
77 			}
78 
79 			if (i == j) {
80 				continue;
81 			}
82 
83 			uint64_t r1_begin = r1->current.offset;
84 			uint64_t r1_end = r1->current.offset + r1->current.blocks - 1;
85 			uint64_t r2_begin = r2->current.offset;
86 			uint64_t r2_end = r2->current.offset + r2->current.blocks - 1;
87 
88 			if (spdk_max(r1_begin, r2_begin) <= spdk_min(r1_end, r2_end)) {
89 				FTL_ERRLOG(dev, "Layout initialization ERROR, two regions overlap, "
90 					   "%s and %s\n", r1->name, r2->name);
91 				return -1;
92 			}
93 		}
94 	}
95 
96 	return 0;
97 }
98 
99 static uint64_t
100 get_num_user_lbas(struct spdk_ftl_dev *dev)
101 {
102 	uint64_t blocks = 0;
103 
104 	blocks = dev->layout.base.total_blocks;
105 	blocks = (blocks * (100 - dev->conf.overprovisioning)) / 100;
106 
107 	return blocks;
108 }
109 
110 static void
111 set_region_bdev_nvc(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
112 {
113 	reg->bdev_desc = dev->nv_cache.bdev_desc;
114 	reg->ioch = dev->nv_cache.cache_ioch;
115 	reg->vss_blksz = dev->nv_cache.md_size;
116 }
117 
118 static void
119 set_region_bdev_btm(struct ftl_layout_region *reg, struct spdk_ftl_dev *dev)
120 {
121 	reg->bdev_desc = dev->base_bdev_desc;
122 	reg->ioch = dev->base_ioch;
123 	reg->vss_blksz = 0;
124 }
125 
126 static int
127 setup_layout_nvc(struct spdk_ftl_dev *dev)
128 {
129 	uint64_t left, offset = 0;
130 	struct ftl_layout *layout = &dev->layout;
131 	struct ftl_layout_region *region, *mirror;
132 
133 #ifdef SPDK_FTL_VSS_EMU
134 	/* Skip the already init`d VSS region */
135 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
136 	offset += region->current.blocks;
137 
138 	if (offset >= layout->nvc.total_blocks) {
139 		goto error;
140 	}
141 #endif
142 
143 	/* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */
144 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
145 	offset += region->current.blocks;
146 
147 	/* Initialize L2P region */
148 	if (offset >= layout->nvc.total_blocks) {
149 		goto error;
150 	}
151 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_L2P];
152 	region->type = FTL_LAYOUT_REGION_TYPE_L2P;
153 	region->name = "l2p";
154 	region->current.version = 0;
155 	region->prev.version = 0;
156 	region->current.offset = offset;
157 	region->current.blocks = blocks_region(layout->l2p.addr_size * dev->num_lbas);
158 	set_region_bdev_nvc(region, dev);
159 	offset += region->current.blocks;
160 
161 	if (offset >= layout->nvc.total_blocks) {
162 		goto error;
163 	}
164 
165 	/*
166 	 * Initialize NV Cache metadata
167 	 */
168 	if (offset >= layout->nvc.total_blocks) {
169 		goto error;
170 	}
171 
172 	left = layout->nvc.total_blocks - offset;
173 	layout->nvc.chunk_data_blocks =
174 		FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_zone(dev)) / FTL_BLOCK_SIZE;
175 	layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE;
176 	layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) /
177 				  FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_zone(dev));
178 	layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
179 
180 	if (0 == layout->nvc.chunk_count) {
181 		goto error;
182 	}
183 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD];
184 	region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD;
185 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
186 	region->name = "nvc_md";
187 	region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT;
188 	region->current.offset = offset;
189 	region->current.blocks = blocks_region(layout->nvc.chunk_count *
190 					       sizeof(struct ftl_nv_cache_chunk_md));
191 	region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE;
192 	region->num_entries = layout->nvc.chunk_count;
193 	set_region_bdev_nvc(region, dev);
194 	offset += region->current.blocks;
195 
196 	/*
197 	 * Initialize NV Cache metadata mirror
198 	 */
199 	mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR];
200 	*mirror = *region;
201 	mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
202 	mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
203 	mirror->name = "nvc_md_mirror";
204 	mirror->current.offset += region->current.blocks;
205 	offset += mirror->current.blocks;
206 
207 	/*
208 	 * Initialize data region on NV cache
209 	 */
210 	if (offset >= layout->nvc.total_blocks) {
211 		goto error;
212 	}
213 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC];
214 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC;
215 	region->name = "data_nvc";
216 	region->current.version = region->prev.version = 0;
217 	region->current.offset = offset;
218 	region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks;
219 	set_region_bdev_nvc(region, dev);
220 	offset += region->current.blocks;
221 
222 	left = layout->nvc.total_blocks - offset;
223 	if (left > layout->nvc.chunk_data_blocks) {
224 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
225 		return -1;
226 	}
227 
228 	if (offset > layout->nvc.total_blocks) {
229 		FTL_ERRLOG(dev, "Error when setup NV cache layout\n");
230 		goto error;
231 	}
232 
233 	return 0;
234 
235 error:
236 	FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
237 	return -1;
238 }
239 
240 static ftl_addr
241 layout_base_offset(struct spdk_ftl_dev *dev)
242 {
243 	ftl_addr addr;
244 
245 	addr = dev->num_bands * ftl_get_num_blocks_in_band(dev);
246 	return addr;
247 }
248 
249 static int
250 setup_layout_base(struct spdk_ftl_dev *dev)
251 {
252 	uint64_t left, offset;
253 	struct ftl_layout *layout = &dev->layout;
254 	struct ftl_layout_region *region;
255 
256 	/* Base device layout is following:
257 	 * - data
258 	 * - superblock
259 	 * - valid map
260 	 *
261 	 * Superblock has been already configured, its offset marks the end of the data region
262 	 */
263 	offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset;
264 
265 	/* Setup data region on base device */
266 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE];
267 	region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE;
268 	region->name = "data_btm";
269 	region->current.version = region->prev.version = 0;
270 	region->current.offset = 0;
271 	region->current.blocks = offset;
272 	set_region_bdev_btm(region, dev);
273 
274 	/* Move offset after base superblock */
275 	offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks;
276 
277 	/* Checking for underflow */
278 	left = layout->base.total_blocks - offset;
279 	if (left > layout->base.total_blocks) {
280 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
281 		return -1;
282 	}
283 
284 	if (offset > layout->base.total_blocks) {
285 		FTL_ERRLOG(dev, "Error when setup base device layout\n");
286 		return -1;
287 	}
288 
289 	return 0;
290 }
291 
292 int
293 ftl_layout_setup(struct spdk_ftl_dev *dev)
294 {
295 	const struct spdk_bdev *bdev;
296 	struct ftl_layout *layout = &dev->layout;
297 	uint64_t i;
298 	uint64_t num_lbas;
299 
300 	bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
301 	layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
302 
303 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
304 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
305 
306 	/* Initialize mirrors types */
307 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
308 		if (i == FTL_LAYOUT_REGION_TYPE_SB) {
309 			/* Super block has been already initialized */
310 			continue;
311 		}
312 
313 		layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
314 	}
315 
316 	/*
317 	 * Initialize L2P information
318 	 */
319 	num_lbas = get_num_user_lbas(dev);
320 	if (dev->num_lbas == 0) {
321 		assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
322 		dev->num_lbas = num_lbas;
323 		dev->sb->lba_cnt = num_lbas;
324 	} else if (dev->num_lbas != num_lbas) {
325 		FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
326 		return -EINVAL;
327 	}
328 	layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
329 	layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
330 	layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
331 
332 	if (setup_layout_nvc(dev)) {
333 		return -EINVAL;
334 	}
335 
336 	if (setup_layout_base(dev)) {
337 		return -EINVAL;
338 	}
339 
340 	if (ftl_validate_regions(dev, layout)) {
341 		return -EINVAL;
342 	}
343 
344 	FTL_NOTICELOG(dev, "Base device capacity:         %.2f MiB\n",
345 		      blocks2mib(layout->base.total_blocks));
346 	FTL_NOTICELOG(dev, "NV cache device capacity:       %.2f MiB\n",
347 		      blocks2mib(layout->nvc.total_blocks));
348 	FTL_NOTICELOG(dev, "L2P entries:                    %"PRIu64"\n",
349 		      dev->num_lbas);
350 	FTL_NOTICELOG(dev, "L2P address size:               %"PRIu64"\n",
351 		      layout->l2p.addr_size);
352 
353 	return 0;
354 }
355 
356 #ifdef SPDK_FTL_VSS_EMU
357 void
358 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev)
359 {
360 	const struct spdk_bdev *bdev;
361 	struct ftl_layout *layout = &dev->layout;
362 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
363 
364 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL);
365 
366 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS];
367 	region->type = FTL_LAYOUT_REGION_TYPE_VSS;
368 	region->name = "vss";
369 	region->current.version = region->prev.version = 0;
370 	region->current.offset = 0;
371 
372 	bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
373 	layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
374 	region->current.blocks = blocks_region(dev->nv_cache.md_size * layout->nvc.total_blocks);
375 
376 	region->vss_blksz = 0;
377 	region->bdev_desc = dev->nv_cache.bdev_desc;
378 	region->ioch = dev->nv_cache.cache_ioch;
379 
380 	assert(region->bdev_desc != NULL);
381 	assert(region->ioch != NULL);
382 }
383 #endif
384 
385 int
386 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
387 {
388 	struct ftl_layout *layout = &dev->layout;
389 	struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
390 	uint64_t total_blocks, offset, left;
391 
392 	assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
393 
394 	/* Initialize superblock region */
395 	region->type = FTL_LAYOUT_REGION_TYPE_SB;
396 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
397 	region->name = "sb";
398 	region->current.version = FTL_METADATA_VERSION_CURRENT;
399 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
400 	region->current.offset = 0;
401 
402 	/*
403 	 * VSS region must go first in case SB to make calculating its relative size easier
404 	 */
405 #ifdef SPDK_FTL_VSS_EMU
406 	region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset +
407 				 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks;
408 #endif
409 
410 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
411 	region->vss_blksz = 0;
412 	region->bdev_desc = dev->nv_cache.bdev_desc;
413 	region->ioch = dev->nv_cache.cache_ioch;
414 
415 	assert(region->bdev_desc != NULL);
416 	assert(region->ioch != NULL);
417 
418 	region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
419 	region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
420 	region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX;
421 	region->name = "sb_mirror";
422 	region->current.version = FTL_METADATA_VERSION_CURRENT;
423 	region->prev.version = FTL_METADATA_VERSION_CURRENT;
424 	/* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts
425 	 * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore)
426 	 */
427 	region->current.offset = layout_base_offset(dev);
428 	region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE);
429 	set_region_bdev_btm(region, dev);
430 
431 	/* Check if SB can be stored at the end of base device */
432 	total_blocks = spdk_bdev_get_num_blocks(
433 			       spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
434 	offset = region->current.offset + region->current.blocks;
435 	left = total_blocks - offset;
436 	if ((left > total_blocks) || (offset > total_blocks)) {
437 		FTL_ERRLOG(dev, "Error when setup base device super block\n");
438 		return -1;
439 	}
440 
441 	return 0;
442 }
443 
444 void
445 ftl_layout_dump(struct spdk_ftl_dev *dev)
446 {
447 	struct ftl_layout *layout = &dev->layout;
448 	int i;
449 	FTL_NOTICELOG(dev, "NV cache layout:\n");
450 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
451 		if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) {
452 			dump_region(dev, &layout->region[i]);
453 		}
454 	}
455 	FTL_NOTICELOG(dev, "Bottom device layout:\n");
456 	for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
457 		if (layout->region[i].bdev_desc == dev->base_bdev_desc) {
458 			dump_region(dev, &layout->region[i]);
459 		}
460 	}
461 }
462