Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright 2023 Solidigm All Rights Reserved
3 : * Copyright (C) 2022 Intel Corporation.
4 : * All rights reserved.
5 : */
6 :
7 : #include "spdk/bdev.h"
8 :
9 : #include "ftl_core.h"
10 : #include "ftl_utils.h"
11 : #include "ftl_band.h"
12 : #include "ftl_layout.h"
13 : #include "ftl_nv_cache.h"
14 : #include "ftl_sb.h"
15 : #include "nvc/ftl_nvc_dev.h"
16 : #include "utils/ftl_layout_tracker_bdev.h"
17 : #include "upgrade/ftl_layout_upgrade.h"
18 :
19 : enum ftl_layout_setup_mode {
20 : FTL_LAYOUT_SETUP_MODE_LOAD_CURRENT = 0,
21 : FTL_LAYOUT_SETUP_MODE_NO_RESTRICT,
22 : FTL_LAYOUT_SETUP_MODE_LEGACY_DEFAULT,
23 : };
24 :
25 : static inline float
26 0 : blocks2mib(uint64_t blocks)
27 : {
28 : float result;
29 :
30 0 : result = blocks;
31 0 : result *= FTL_BLOCK_SIZE;
32 0 : result /= 1024UL;
33 0 : result /= 1024UL;
34 :
35 0 : return result;
36 : }
37 :
38 : static uint64_t
39 0 : superblock_region_size(struct spdk_ftl_dev *dev)
40 : {
41 0 : const struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
42 0 : uint64_t wus = spdk_bdev_get_write_unit_size(bdev) * FTL_BLOCK_SIZE;
43 :
44 0 : if (wus > FTL_SUPERBLOCK_SIZE) {
45 0 : return wus;
46 : } else {
47 0 : return wus * spdk_divide_round_up(FTL_SUPERBLOCK_SIZE, wus);
48 : }
49 : }
50 :
51 : static uint64_t
52 0 : superblock_region_blocks(struct spdk_ftl_dev *dev)
53 : {
54 0 : return superblock_region_size(dev) / FTL_BLOCK_SIZE;
55 : }
56 :
57 : uint64_t
58 0 : ftl_md_region_blocks(struct spdk_ftl_dev *dev, uint64_t bytes)
59 : {
60 0 : const uint64_t alignment = superblock_region_size(dev);
61 : uint64_t result;
62 :
63 0 : result = spdk_divide_round_up(bytes, alignment);
64 0 : result *= alignment;
65 0 : result /= FTL_BLOCK_SIZE;
66 :
67 0 : return result;
68 : }
69 :
70 : uint64_t
71 0 : ftl_md_region_align_blocks(struct spdk_ftl_dev *dev, uint64_t blocks)
72 : {
73 0 : const uint64_t alignment = superblock_region_blocks(dev);
74 : uint64_t result;
75 :
76 0 : result = spdk_divide_round_up(blocks, alignment);
77 0 : result *= alignment;
78 :
79 0 : return result;
80 : }
81 :
82 : const char *
83 0 : ftl_md_region_name(enum ftl_layout_region_type reg_type)
84 : {
85 : static const char *md_region_name[FTL_LAYOUT_REGION_TYPE_MAX] = {
86 : [FTL_LAYOUT_REGION_TYPE_SB] = "sb",
87 : [FTL_LAYOUT_REGION_TYPE_SB_BASE] = "sb_mirror",
88 : [FTL_LAYOUT_REGION_TYPE_L2P] = "l2p",
89 : [FTL_LAYOUT_REGION_TYPE_BAND_MD] = "band_md",
90 : [FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR] = "band_md_mirror",
91 : [FTL_LAYOUT_REGION_TYPE_VALID_MAP] = "vmap",
92 : [FTL_LAYOUT_REGION_TYPE_NVC_MD] = "nvc_md",
93 : [FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR] = "nvc_md_mirror",
94 : [FTL_LAYOUT_REGION_TYPE_DATA_NVC] = "data_nvc",
95 : [FTL_LAYOUT_REGION_TYPE_DATA_BASE] = "data_btm",
96 : [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC] = "p2l0",
97 : [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT] = "p2l1",
98 : [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP] = "p2l2",
99 : [FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT] = "p2l3",
100 : [FTL_LAYOUT_REGION_TYPE_TRIM_MD] = "trim_md",
101 : [FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR] = "trim_md_mirror",
102 : [FTL_LAYOUT_REGION_TYPE_TRIM_LOG] = "trim_log",
103 : [FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR] = "trim_log_mirror"
104 : };
105 0 : const char *reg_name = md_region_name[reg_type];
106 :
107 0 : assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
108 0 : assert(reg_name != NULL);
109 0 : return reg_name;
110 : }
111 :
112 : static void
113 0 : dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
114 : {
115 0 : assert(!(region->current.offset % superblock_region_blocks(dev)));
116 0 : assert(!(region->current.blocks % superblock_region_blocks(dev)));
117 :
118 0 : FTL_NOTICELOG(dev, "Region %s\n", region->name);
119 0 : FTL_NOTICELOG(dev, " offset: %.2f MiB\n",
120 : blocks2mib(region->current.offset));
121 0 : FTL_NOTICELOG(dev, " blocks: %.2f MiB\n",
122 : blocks2mib(region->current.blocks));
123 0 : }
124 :
125 : static bool
126 0 : is_region_disabled(struct ftl_layout_region *region)
127 : {
128 0 : return region->current.blocks == 0 && region->current.offset == FTL_ADDR_INVALID;
129 : }
130 :
131 : int
132 0 : ftl_validate_regions(struct spdk_ftl_dev *dev, struct ftl_layout *layout)
133 : {
134 : enum ftl_layout_region_type i, j;
135 :
136 : /* Validate if regions doesn't overlap each other */
137 0 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
138 0 : struct ftl_layout_region *r1 = ftl_layout_region_get(dev, i);
139 :
140 0 : if (!r1 || is_region_disabled(r1)) {
141 0 : continue;
142 : }
143 :
144 0 : for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
145 0 : struct ftl_layout_region *r2 = ftl_layout_region_get(dev, j);
146 :
147 0 : if (!r2 || is_region_disabled(r2)) {
148 0 : continue;
149 : }
150 :
151 0 : if (r1->bdev_desc != r2->bdev_desc) {
152 0 : continue;
153 : }
154 :
155 0 : if (i == j) {
156 0 : continue;
157 : }
158 :
159 0 : uint64_t r1_begin = r1->current.offset;
160 0 : uint64_t r1_end = r1->current.offset + r1->current.blocks - 1;
161 0 : uint64_t r2_begin = r2->current.offset;
162 0 : uint64_t r2_end = r2->current.offset + r2->current.blocks - 1;
163 :
164 0 : if (spdk_max(r1_begin, r2_begin) <= spdk_min(r1_end, r2_end)) {
165 0 : FTL_ERRLOG(dev, "Layout initialization ERROR, two regions overlap, "
166 : "%s and %s\n", r1->name, r2->name);
167 0 : return -1;
168 : }
169 : }
170 : }
171 :
172 0 : return 0;
173 : }
174 :
175 : static uint64_t
176 0 : get_num_user_lbas(struct spdk_ftl_dev *dev)
177 : {
178 : uint64_t blocks;
179 :
180 0 : blocks = dev->num_bands * ftl_get_num_blocks_in_band(dev);
181 0 : blocks = (blocks * (100 - dev->conf.overprovisioning)) / 100;
182 :
183 0 : return blocks;
184 : }
185 :
186 : struct ftl_layout_region *
187 22 : ftl_layout_region_get(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type)
188 : {
189 22 : struct ftl_layout_region *reg = &dev->layout.region[reg_type];
190 :
191 22 : assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
192 22 : return reg->type == reg_type ? reg : NULL;
193 : }
194 :
195 : uint64_t
196 0 : ftl_layout_base_offset(struct spdk_ftl_dev *dev)
197 : {
198 0 : return dev->num_bands * ftl_get_num_blocks_in_band(dev);
199 : }
200 :
201 : static int
202 0 : layout_region_create_nvc(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type,
203 : uint32_t reg_version, size_t entry_size, size_t entry_count)
204 : {
205 0 : const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
206 0 : size_t reg_blks = ftl_md_region_blocks(dev, entry_count * entry_size);
207 :
208 0 : if (md_ops->region_create(dev, reg_type, reg_version, reg_blks)) {
209 0 : return -1;
210 : }
211 0 : if (md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count,
212 : &dev->layout.region[reg_type])) {
213 0 : return -1;
214 : }
215 0 : return 0;
216 : }
217 :
218 : static int
219 0 : layout_region_create_base(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type,
220 : uint32_t reg_version, size_t entry_size, size_t entry_count)
221 : {
222 0 : const struct ftl_md_layout_ops *md_ops = &dev->base_type->ops.md_layout_ops;
223 0 : size_t reg_blks = ftl_md_region_blocks(dev, entry_count * entry_size);
224 :
225 0 : if (md_ops->region_create(dev, reg_type, reg_version, reg_blks)) {
226 0 : return -1;
227 : }
228 0 : if (md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count,
229 : &dev->layout.region[reg_type])) {
230 0 : return -1;
231 : }
232 0 : return 0;
233 : }
234 :
235 : static void
236 0 : legacy_layout_verify_region(struct ftl_layout_tracker_bdev *layout_tracker,
237 : enum ftl_layout_region_type reg_type, uint32_t reg_version)
238 : {
239 0 : const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
240 0 : const struct ftl_layout_tracker_bdev_region_props *reg_found = NULL;
241 :
242 : while (true) {
243 0 : ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg_type, ®_search_ctx);
244 0 : if (!reg_search_ctx) {
245 0 : break;
246 : }
247 :
248 : /* Only a single region version is present in upgrade from the legacy layout */
249 0 : ftl_bug(reg_search_ctx->ver != reg_version);
250 0 : ftl_bug(reg_found != NULL);
251 :
252 0 : reg_found = reg_search_ctx;
253 : }
254 0 : }
255 :
256 : static int
257 0 : legacy_layout_region_open_nvc(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type,
258 : uint32_t reg_version, size_t entry_size, size_t entry_count)
259 : {
260 0 : struct ftl_layout_region *reg = &dev->layout.region[reg_type];
261 0 : const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
262 :
263 0 : legacy_layout_verify_region(dev->nvc_layout_tracker, reg_type, reg_version);
264 0 : return md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count, reg);
265 : }
266 :
267 : static int
268 0 : legacy_layout_region_open_base(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type,
269 : uint32_t reg_version, size_t entry_size, size_t entry_count)
270 : {
271 0 : struct ftl_layout_region *reg = &dev->layout.region[reg_type];
272 0 : const struct ftl_md_layout_ops *md_ops = &dev->base_type->ops.md_layout_ops;
273 :
274 0 : legacy_layout_verify_region(dev->nvc_layout_tracker, reg_type, reg_version);
275 0 : return md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count, reg);
276 : }
277 :
278 : static int
279 0 : layout_setup_legacy_default_nvc(struct spdk_ftl_dev *dev)
280 : {
281 : int region_type;
282 : uint64_t blocks, chunk_count;
283 0 : struct ftl_layout *layout = &dev->layout;
284 0 : const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
285 :
286 : /* Initialize L2P region */
287 0 : blocks = ftl_md_region_blocks(dev, layout->l2p.addr_size * dev->num_lbas);
288 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_L2P, 0, FTL_BLOCK_SIZE,
289 : blocks)) {
290 0 : goto error;
291 : }
292 :
293 : /* Initialize band info metadata */
294 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_BAND_MD, FTL_BAND_VERSION_1,
295 : sizeof(struct ftl_band_md), ftl_get_num_bands(dev))) {
296 0 : goto error;
297 : }
298 :
299 : /* Initialize band info metadata mirror */
300 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR, FTL_BAND_VERSION_1,
301 : sizeof(struct ftl_band_md), ftl_get_num_bands(dev))) {
302 0 : goto error;
303 : }
304 0 : layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
305 :
306 : /*
307 : * Initialize P2L checkpointing regions
308 : */
309 0 : for (region_type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN;
310 0 : region_type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX;
311 0 : region_type++) {
312 0 : const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
313 :
314 : /* Get legacy number of blocks */
315 0 : ftl_layout_tracker_bdev_find_next_region(dev->nvc_layout_tracker, region_type, ®_search_ctx);
316 0 : if (!reg_search_ctx || reg_search_ctx->ver != FTL_P2L_VERSION_1) {
317 0 : goto error;
318 : }
319 0 : blocks = reg_search_ctx->blk_sz;
320 :
321 0 : if (legacy_layout_region_open_nvc(dev, region_type, FTL_P2L_VERSION_1, FTL_BLOCK_SIZE, blocks)) {
322 0 : goto error;
323 : }
324 : }
325 :
326 : /*
327 : * Initialize trim metadata region
328 : */
329 0 : blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
330 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD, 0, sizeof(uint64_t),
331 : blocks)) {
332 0 : goto error;
333 : }
334 :
335 : /* Initialize trim metadata mirror region */
336 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR, 0, sizeof(uint64_t),
337 : blocks)) {
338 0 : goto error;
339 : }
340 0 : layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR;
341 :
342 : /* Restore chunk count */
343 0 : ftl_layout_tracker_bdev_find_next_region(dev->nvc_layout_tracker, FTL_LAYOUT_REGION_TYPE_DATA_NVC,
344 : ®_search_ctx);
345 0 : if (!reg_search_ctx || reg_search_ctx->ver != 0) {
346 0 : goto error;
347 : }
348 0 : blocks = reg_search_ctx->blk_sz;
349 0 : chunk_count = blocks / ftl_get_num_blocks_in_band(dev);
350 0 : if (0 == chunk_count) {
351 0 : goto error;
352 : }
353 :
354 : /*
355 : * Initialize NV Cache metadata
356 : */
357 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_NVC_MD, FTL_NVC_VERSION_1,
358 : sizeof(struct ftl_nv_cache_chunk_md), chunk_count)) {
359 0 : goto error;
360 : }
361 :
362 : /*
363 : * Initialize NV Cache metadata mirror
364 : */
365 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR, FTL_NVC_VERSION_1,
366 : sizeof(struct ftl_nv_cache_chunk_md), chunk_count)) {
367 0 : goto error;
368 : }
369 0 : layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
370 :
371 : /*
372 : * Initialize data region on NV cache
373 : */
374 0 : if (legacy_layout_region_open_nvc(dev, FTL_LAYOUT_REGION_TYPE_DATA_NVC, 0,
375 0 : layout->nvc.chunk_data_blocks * FTL_BLOCK_SIZE, chunk_count)) {
376 0 : goto error;
377 : }
378 :
379 : /* Here is the place to add necessary region placeholders for the creation of new regions */
380 0 : ftl_layout_upgrade_add_region_placeholder(dev, dev->nvc_layout_tracker,
381 : FTL_LAYOUT_REGION_TYPE_TRIM_LOG);
382 0 : ftl_layout_upgrade_add_region_placeholder(dev, dev->nvc_layout_tracker,
383 : FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR);
384 :
385 0 : return 0;
386 :
387 0 : error:
388 0 : FTL_ERRLOG(dev, "Invalid legacy NV Cache metadata layout\n");
389 0 : return -1;
390 : }
391 :
392 : static int
393 0 : layout_setup_legacy_default_base(struct spdk_ftl_dev *dev)
394 : {
395 0 : struct ftl_layout *layout = &dev->layout;
396 :
397 : /* Base device layout is as follows:
398 : * - superblock
399 : * - data
400 : * - valid map
401 : */
402 0 : if (layout_region_create_base(dev, FTL_LAYOUT_REGION_TYPE_DATA_BASE, 0, FTL_BLOCK_SIZE,
403 : ftl_layout_base_offset(dev))) {
404 0 : return -1;
405 : }
406 :
407 0 : if (legacy_layout_region_open_base(dev, FTL_LAYOUT_REGION_TYPE_VALID_MAP, 0, FTL_BLOCK_SIZE,
408 0 : ftl_md_region_blocks(dev, spdk_divide_round_up(layout->base.total_blocks + layout->nvc.total_blocks,
409 : 8)))) {
410 0 : return -1;
411 : }
412 :
413 0 : return 0;
414 : }
415 :
416 : static int
417 0 : layout_setup_legacy_default(struct spdk_ftl_dev *dev)
418 : {
419 0 : if (layout_setup_legacy_default_nvc(dev) || layout_setup_legacy_default_base(dev)) {
420 0 : return -1;
421 : }
422 0 : return 0;
423 : }
424 :
425 : static int
426 0 : layout_setup_default_nvc(struct spdk_ftl_dev *dev)
427 : {
428 : int region_type;
429 : uint64_t blocks;
430 0 : struct ftl_layout *layout = &dev->layout;
431 :
432 : /* Initialize L2P region */
433 0 : blocks = ftl_md_region_blocks(dev, layout->l2p.addr_size * dev->num_lbas);
434 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_L2P, 0, FTL_BLOCK_SIZE, blocks)) {
435 0 : goto error;
436 : }
437 :
438 : /* Initialize band info metadata */
439 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_BAND_MD, FTL_BAND_VERSION_CURRENT,
440 : sizeof(struct ftl_band_md), ftl_get_num_bands(dev))) {
441 0 : goto error;
442 : }
443 :
444 : /* Initialize band info metadata mirror */
445 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR, FTL_BAND_VERSION_CURRENT,
446 : sizeof(struct ftl_band_md), ftl_get_num_bands(dev))) {
447 0 : goto error;
448 : }
449 0 : layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR;
450 :
451 : /*
452 : * Initialize P2L checkpointing regions
453 : */
454 0 : for (region_type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN;
455 0 : region_type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX;
456 0 : region_type++) {
457 0 : if (layout_region_create_nvc(dev, region_type, FTL_P2L_VERSION_CURRENT, FTL_BLOCK_SIZE,
458 : layout->p2l.ckpt_pages)) {
459 0 : goto error;
460 : }
461 : }
462 :
463 : /*
464 : * Initialize trim metadata region
465 : */
466 0 : blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
467 0 : blocks = spdk_divide_round_up(blocks * sizeof(uint64_t), FTL_BLOCK_SIZE);
468 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD, 0, FTL_BLOCK_SIZE, blocks)) {
469 0 : goto error;
470 : }
471 :
472 : /* Initialize trim metadata mirror region */
473 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR, 0, FTL_BLOCK_SIZE,
474 : blocks)) {
475 0 : goto error;
476 : }
477 0 : layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_TRIM_MD_MIRROR;
478 :
479 : /*
480 : * Initialize trim log region
481 : */
482 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_LOG, FTL_TRIM_LOG_VERSION_CURRENT,
483 : sizeof(struct ftl_trim_log), 1)) {
484 0 : goto error;
485 : }
486 :
487 : /* Initialize trim log mirror region */
488 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR,
489 : FTL_TRIM_LOG_VERSION_CURRENT,
490 : sizeof(struct ftl_trim_log), 1)) {
491 0 : goto error;
492 : }
493 0 : layout->region[FTL_LAYOUT_REGION_TYPE_TRIM_LOG].mirror_type =
494 : FTL_LAYOUT_REGION_TYPE_TRIM_LOG_MIRROR;
495 :
496 : /*
497 : * Initialize NV Cache metadata
498 : */
499 0 : if (0 == layout->nvc.chunk_count) {
500 0 : goto error;
501 : }
502 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_NVC_MD, FTL_NVC_VERSION_CURRENT,
503 : sizeof(struct ftl_nv_cache_chunk_md), layout->nvc.chunk_count)) {
504 0 : goto error;
505 : }
506 :
507 : /*
508 : * Initialize NV Cache metadata mirror
509 : */
510 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR, FTL_NVC_VERSION_CURRENT,
511 : sizeof(struct ftl_nv_cache_chunk_md), layout->nvc.chunk_count)) {
512 0 : goto error;
513 : }
514 0 : layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
515 :
516 0 : return 0;
517 :
518 0 : error:
519 0 : FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n");
520 0 : return -1;
521 : }
522 :
523 : static int
524 0 : layout_setup_default_base(struct spdk_ftl_dev *dev)
525 : {
526 0 : struct ftl_layout *layout = &dev->layout;
527 : uint64_t valid_map_size;
528 :
529 : /* Base device layout is as follows:
530 : * - superblock
531 : * - data
532 : * - valid map
533 : */
534 0 : if (layout_region_create_base(dev, FTL_LAYOUT_REGION_TYPE_DATA_BASE, 0, FTL_BLOCK_SIZE,
535 : ftl_layout_base_offset(dev))) {
536 0 : return -1;
537 : }
538 :
539 0 : valid_map_size = spdk_divide_round_up(layout->base.total_blocks + layout->nvc.total_blocks, 8);
540 0 : if (layout_region_create_base(dev, FTL_LAYOUT_REGION_TYPE_VALID_MAP, 0, FTL_BLOCK_SIZE,
541 : ftl_md_region_blocks(dev, valid_map_size))) {
542 0 : return -1;
543 : }
544 :
545 0 : return 0;
546 : }
547 :
548 : static int
549 0 : layout_setup_default(struct spdk_ftl_dev *dev)
550 : {
551 0 : if (layout_setup_default_nvc(dev) || layout_setup_default_base(dev)) {
552 0 : return -1;
553 : }
554 0 : return 0;
555 : }
556 :
557 : static int
558 0 : layout_load(struct spdk_ftl_dev *dev)
559 : {
560 0 : if (ftl_superblock_load_blob_area(dev)) {
561 0 : return -1;
562 : }
563 0 : if (ftl_superblock_md_layout_apply(dev)) {
564 0 : return -1;
565 : }
566 0 : return 0;
567 : }
568 :
569 : int
570 0 : ftl_layout_setup(struct spdk_ftl_dev *dev)
571 : {
572 0 : struct ftl_layout *layout = &dev->layout;
573 : uint64_t i;
574 : uint64_t num_lbas;
575 : enum ftl_layout_setup_mode setup_mode;
576 : int rc;
577 :
578 : /*
579 : * SB v5 adds the ability to create MD regions dynamically, i.e. depending on the underlying device type.
580 : * For compatibility reasons:
581 : * 1. When upgrading from pre-v5 SB, only the legacy default layout is created.
582 : * Pre-v5: some regions were static and not stored in the SB layout. These must be created to match
583 : * the legacy default layout.
584 : * v5: all regions are stored in the SB layout. Upon the SB upgrade, the legacy default layout
585 : * is updated with pre-v5 layout stored in the SB. The whole layout is then stored in v5 SB.
586 : *
587 : * 2. When SB v5 or later was loaded, the layout is instantiated from the nvc and base layout blobs.
588 : * No default layout is created.
589 : *
590 : * 3. When the FTL layout is being created for the first time, there are no restrictions.
591 : *
592 : * Any new regions to be created in cases (1) and (2) can only be placed in the unallocated area
593 : * of the underlying device.
594 : */
595 :
596 0 : if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
597 0 : setup_mode = FTL_LAYOUT_SETUP_MODE_NO_RESTRICT;
598 0 : } else if (ftl_superblock_is_blob_area_empty(dev->sb)) {
599 0 : setup_mode = FTL_LAYOUT_SETUP_MODE_LEGACY_DEFAULT;
600 : } else {
601 0 : setup_mode = FTL_LAYOUT_SETUP_MODE_LOAD_CURRENT;
602 : }
603 0 : FTL_NOTICELOG(dev, "FTL layout setup mode %d\n", (int)setup_mode);
604 :
605 : /* Invalidate all regions */
606 0 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
607 0 : if (i == FTL_LAYOUT_REGION_TYPE_SB || i == FTL_LAYOUT_REGION_TYPE_SB_BASE) {
608 : /* Super block has been already initialized */
609 0 : continue;
610 : }
611 :
612 0 : layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
613 : /* Mark the region inactive */
614 0 : layout->region[i].type = FTL_LAYOUT_REGION_TYPE_INVALID;
615 : }
616 :
617 : /*
618 : * Initialize L2P information
619 : */
620 0 : num_lbas = get_num_user_lbas(dev);
621 0 : if (dev->num_lbas == 0) {
622 0 : assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
623 0 : dev->num_lbas = num_lbas;
624 0 : dev->sb->lba_cnt = num_lbas;
625 0 : } else if (dev->num_lbas != num_lbas) {
626 0 : FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
627 0 : return -EINVAL;
628 : }
629 0 : layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
630 0 : layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
631 0 : layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
632 :
633 : /* Setup P2L ckpt */
634 0 : layout->p2l.pages_per_xfer = spdk_divide_round_up(dev->xfer_size, FTL_NUM_P2L_ENTRIES_NO_VSS);
635 0 : layout->p2l.ckpt_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev),
636 0 : dev->xfer_size) * layout->p2l.pages_per_xfer;
637 :
638 0 : layout->nvc.chunk_data_blocks = ftl_get_num_blocks_in_band(dev);
639 0 : layout->nvc.chunk_count = layout->nvc.total_blocks / ftl_get_num_blocks_in_band(dev);
640 0 : layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
641 :
642 0 : layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev);
643 0 : layout->base.user_blocks = ftl_band_user_blocks(dev->bands);
644 :
645 0 : switch (setup_mode) {
646 0 : case FTL_LAYOUT_SETUP_MODE_LEGACY_DEFAULT:
647 0 : if (layout_setup_legacy_default(dev)) {
648 0 : return -EINVAL;
649 : }
650 0 : break;
651 :
652 0 : case FTL_LAYOUT_SETUP_MODE_LOAD_CURRENT:
653 0 : if (layout_load(dev)) {
654 0 : return -EINVAL;
655 : }
656 0 : break;
657 :
658 0 : case FTL_LAYOUT_SETUP_MODE_NO_RESTRICT:
659 0 : if (layout_setup_default(dev)) {
660 0 : return -EINVAL;
661 : }
662 0 : break;
663 :
664 0 : default:
665 0 : ftl_abort();
666 : break;
667 : }
668 :
669 0 : if (ftl_validate_regions(dev, layout)) {
670 0 : return -EINVAL;
671 : }
672 :
673 0 : rc = ftl_superblock_store_blob_area(dev);
674 :
675 0 : FTL_NOTICELOG(dev, "Base device capacity: %.2f MiB\n",
676 : blocks2mib(layout->base.total_blocks));
677 0 : FTL_NOTICELOG(dev, "NV cache device capacity: %.2f MiB\n",
678 : blocks2mib(layout->nvc.total_blocks));
679 0 : FTL_NOTICELOG(dev, "L2P entries: %"PRIu64"\n", dev->num_lbas);
680 0 : FTL_NOTICELOG(dev, "L2P address size: %"PRIu64"\n", layout->l2p.addr_size);
681 0 : FTL_NOTICELOG(dev, "P2L checkpoint pages: %"PRIu64"\n", layout->p2l.ckpt_pages);
682 0 : FTL_NOTICELOG(dev, "NV cache chunk count %"PRIu64"\n", dev->layout.nvc.chunk_count);
683 :
684 0 : return rc;
685 : }
686 :
687 : int
688 0 : ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
689 : {
690 : const struct spdk_bdev *bdev;
691 0 : struct ftl_layout *layout = &dev->layout;
692 0 : struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
693 : uint64_t total_blocks, offset, left;
694 :
695 0 : assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
696 :
697 0 : bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
698 0 : layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
699 :
700 0 : bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
701 0 : layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
702 :
703 : /* Initialize superblock region */
704 0 : if (layout_region_create_nvc(dev, FTL_LAYOUT_REGION_TYPE_SB, FTL_SB_VERSION_CURRENT,
705 : superblock_region_size(dev), 1)) {
706 0 : FTL_ERRLOG(dev, "Error when setting up primary super block\n");
707 0 : return -1;
708 : }
709 :
710 0 : assert(region->bdev_desc != NULL);
711 0 : assert(region->ioch != NULL);
712 0 : assert(region->current.offset == 0);
713 :
714 0 : if (layout_region_create_base(dev, FTL_LAYOUT_REGION_TYPE_SB_BASE, FTL_SB_VERSION_CURRENT,
715 : superblock_region_size(dev), 1)) {
716 0 : FTL_ERRLOG(dev, "Error when setting up secondary super block\n");
717 0 : return -1;
718 : }
719 0 : layout->region[FTL_LAYOUT_REGION_TYPE_SB].mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
720 :
721 0 : region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
722 0 : assert(region->current.offset == 0);
723 :
724 : /* Check if SB can be stored at the end of base device */
725 0 : total_blocks = spdk_bdev_get_num_blocks(
726 0 : spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
727 0 : offset = region->current.offset + region->current.blocks;
728 0 : left = total_blocks - offset;
729 0 : if ((left > total_blocks) || (offset > total_blocks)) {
730 0 : FTL_ERRLOG(dev, "Error when setup base device super block\n");
731 0 : return -1;
732 : }
733 :
734 0 : return 0;
735 : }
736 :
737 : int
738 0 : ftl_layout_clear_superblock(struct spdk_ftl_dev *dev)
739 : {
740 : int rc;
741 :
742 0 : rc = ftl_layout_tracker_bdev_rm_region(dev->nvc_layout_tracker, FTL_LAYOUT_REGION_TYPE_SB,
743 : FTL_SB_VERSION_CURRENT);
744 0 : if (rc) {
745 0 : return rc;
746 : }
747 :
748 0 : return ftl_layout_tracker_bdev_rm_region(dev->base_layout_tracker, FTL_LAYOUT_REGION_TYPE_SB_BASE,
749 : FTL_SB_VERSION_CURRENT);
750 : }
751 :
752 : void
753 0 : ftl_layout_dump(struct spdk_ftl_dev *dev)
754 : {
755 : struct ftl_layout_region *reg;
756 : enum ftl_layout_region_type i;
757 :
758 0 : FTL_NOTICELOG(dev, "NV cache layout:\n");
759 0 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
760 0 : reg = ftl_layout_region_get(dev, i);
761 0 : if (reg && reg->bdev_desc == dev->nv_cache.bdev_desc) {
762 0 : dump_region(dev, reg);
763 : }
764 : }
765 0 : FTL_NOTICELOG(dev, "Base device layout:\n");
766 0 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
767 0 : reg = ftl_layout_region_get(dev, i);
768 0 : if (reg && reg->bdev_desc == dev->base_bdev_desc) {
769 0 : dump_region(dev, reg);
770 : }
771 : }
772 0 : }
773 :
774 : uint64_t
775 0 : ftl_layout_base_md_blocks(struct spdk_ftl_dev *dev)
776 : {
777 : const struct spdk_bdev *bdev;
778 0 : uint64_t md_blocks = 0, total_blocks = 0;
779 :
780 0 : bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
781 0 : total_blocks += spdk_bdev_get_num_blocks(bdev);
782 :
783 0 : bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
784 0 : total_blocks += spdk_bdev_get_num_blocks(bdev);
785 :
786 : /* Count space needed for validity map */
787 0 : md_blocks += ftl_md_region_blocks(dev, spdk_divide_round_up(total_blocks, 8));
788 :
789 : /* Count space needed for superblock */
790 0 : md_blocks += superblock_region_blocks(dev);
791 0 : return md_blocks;
792 : }
793 :
794 : struct layout_blob_entry {
795 : uint32_t type;
796 : uint64_t entry_size;
797 : uint64_t num_entries;
798 : } __attribute__((packed));
799 :
800 : size_t
801 2 : ftl_layout_blob_store(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_buf_sz)
802 : {
803 2 : struct layout_blob_entry *blob_entry = blob_buf;
804 : struct ftl_layout_region *reg;
805 : enum ftl_layout_region_type reg_type;
806 2 : size_t blob_sz = 0;
807 :
808 38 : for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
809 36 : if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
810 0 : return 0;
811 : }
812 :
813 36 : reg = &dev->layout.region[reg_type];
814 36 : blob_entry->type = reg_type;
815 36 : blob_entry->entry_size = reg->entry_size;
816 36 : blob_entry->num_entries = reg->num_entries;
817 :
818 36 : blob_entry++;
819 36 : blob_sz += sizeof(*blob_entry);
820 : }
821 :
822 2 : return blob_sz;
823 : }
824 :
825 : int
826 7 : ftl_layout_blob_load(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_sz)
827 : {
828 7 : struct layout_blob_entry *blob_entry = blob_buf;
829 7 : size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
830 7 : struct layout_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
831 : struct ftl_layout_region *reg;
832 :
833 7 : if (blob_sz % sizeof(*blob_entry) != 0) {
834 : /* Invalid blob size */
835 0 : return -1;
836 : }
837 :
838 118 : for (; blob_entry < blob_entry_end; blob_entry++) {
839 : /* Verify the type */
840 112 : if (blob_entry->type >= FTL_LAYOUT_REGION_TYPE_MAX) {
841 1 : return -1;
842 : }
843 :
844 : /* Load the entry */
845 111 : reg = &dev->layout.region[blob_entry->type];
846 111 : reg->entry_size = blob_entry->entry_size;
847 111 : reg->num_entries = blob_entry->num_entries;
848 : }
849 :
850 6 : return 0;
851 : }
852 :
853 : void
854 0 : ftl_layout_upgrade_add_region_placeholder(struct spdk_ftl_dev *dev,
855 : struct ftl_layout_tracker_bdev *layout_tracker, enum ftl_layout_region_type reg_type)
856 : {
857 0 : const struct ftl_layout_tracker_bdev_region_props *reg_search_ctx = NULL;
858 :
859 0 : ftl_layout_tracker_bdev_find_next_region(layout_tracker, reg_type, ®_search_ctx);
860 0 : if (reg_search_ctx) {
861 0 : return;
862 : }
863 :
864 0 : dev->layout.region[reg_type].type = reg_type;
865 0 : dev->layout.region[reg_type].current.version = 0;
866 0 : dev->layout.region[reg_type].current.offset = UINT64_MAX;
867 0 : dev->layout.region[reg_type].current.blocks = 0;
868 : }
|