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