Branch data 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 : 972 : blocks2mib(uint64_t blocks)
27 : : {
28 : : float result;
29 : :
30 : 972 : result = blocks;
31 : 972 : result *= FTL_BLOCK_SIZE;
32 : 972 : result /= 1024UL;
33 : 972 : result /= 1024UL;
34 : :
35 : 972 : return result;
36 : : }
37 : :
38 : : static uint64_t
39 : 1470 : superblock_region_size(struct spdk_ftl_dev *dev)
40 : : {
41 : 1470 : const struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
42 : 1470 : uint64_t wus = spdk_bdev_get_write_unit_size(bdev) * FTL_BLOCK_SIZE;
43 : :
44 [ - + ]: 1470 : if (wus > FTL_SUPERBLOCK_SIZE) {
45 : 0 : return wus;
46 : : } else {
47 : 1470 : return wus * spdk_divide_round_up(FTL_SUPERBLOCK_SIZE, wus);
48 : : }
49 : : }
50 : :
51 : : static uint64_t
52 : 1089 : superblock_region_blocks(struct spdk_ftl_dev *dev)
53 : : {
54 : 1089 : return superblock_region_size(dev) / FTL_BLOCK_SIZE;
55 : : }
56 : :
57 : : uint64_t
58 : 327 : ftl_md_region_blocks(struct spdk_ftl_dev *dev, uint64_t bytes)
59 : : {
60 : 327 : const uint64_t alignment = superblock_region_size(dev);
61 : : uint64_t result;
62 : :
63 : 327 : result = spdk_divide_round_up(bytes, alignment);
64 : 327 : result *= alignment;
65 : 327 : result /= FTL_BLOCK_SIZE;
66 : :
67 : 327 : return result;
68 : : }
69 : :
70 : : uint64_t
71 : 144 : ftl_md_region_align_blocks(struct spdk_ftl_dev *dev, uint64_t blocks)
72 : : {
73 : 144 : const uint64_t alignment = superblock_region_blocks(dev);
74 : : uint64_t result;
75 : :
76 : 144 : result = spdk_divide_round_up(blocks, alignment);
77 : 144 : result *= alignment;
78 : :
79 : 144 : return result;
80 : : }
81 : :
82 : : const char *
83 : 507 : 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 : 507 : const char *reg_name = md_region_name[reg_type];
106 : :
107 [ - + ]: 507 : assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
108 [ - + ]: 507 : assert(reg_name != NULL);
109 : 507 : return reg_name;
110 : : }
111 : :
112 : : static void
113 : 459 : dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
114 : : {
115 [ - + - + ]: 459 : assert(!(region->current.offset % superblock_region_blocks(dev)));
116 [ - + - + ]: 459 : assert(!(region->current.blocks % superblock_region_blocks(dev)));
117 : :
118 [ + - ]: 459 : FTL_NOTICELOG(dev, "Region %s\n", region->name);
119 [ + - ]: 459 : FTL_NOTICELOG(dev, " offset: %.2f MiB\n",
120 : : blocks2mib(region->current.offset));
121 [ + - ]: 459 : FTL_NOTICELOG(dev, " blocks: %.2f MiB\n",
122 : : blocks2mib(region->current.blocks));
123 : 459 : }
124 : :
125 : : static bool
126 : 24786 : is_region_disabled(struct ftl_layout_region *region)
127 : : {
128 [ - + - - ]: 24786 : return region->current.blocks == 0 && region->current.offset == FTL_ADDR_INVALID;
129 : : }
130 : :
131 : : int
132 : 81 : 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 [ + + ]: 1539 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; i++) {
138 : 1458 : struct ftl_layout_region *r1 = ftl_layout_region_get(dev, i);
139 : :
140 [ + + - + ]: 1458 : if (!r1 || is_region_disabled(r1)) {
141 : 81 : continue;
142 : : }
143 : :
144 [ + + ]: 26163 : for (j = 0; j < FTL_LAYOUT_REGION_TYPE_MAX; j++) {
145 : 24786 : struct ftl_layout_region *r2 = ftl_layout_region_get(dev, j);
146 : :
147 [ + + - + ]: 24786 : if (!r2 || is_region_disabled(r2)) {
148 : 1377 : continue;
149 : : }
150 : :
151 [ + + ]: 23409 : if (r1->bdev_desc != r2->bdev_desc) {
152 : 6804 : continue;
153 : : }
154 : :
155 [ + + ]: 16605 : if (i == j) {
156 : 1377 : continue;
157 : : }
158 : :
159 : 15228 : uint64_t r1_begin = r1->current.offset;
160 : 15228 : uint64_t r1_end = r1->current.offset + r1->current.blocks - 1;
161 : 15228 : uint64_t r2_begin = r2->current.offset;
162 : 15228 : uint64_t r2_end = r2->current.offset + r2->current.blocks - 1;
163 : :
164 [ - + ]: 15228 : 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 : 81 : return 0;
173 : : }
174 : :
175 : : static uint64_t
176 : 27 : get_num_user_lbas(struct spdk_ftl_dev *dev)
177 : : {
178 : : uint64_t blocks;
179 : :
180 : 27 : blocks = dev->num_bands * ftl_get_num_blocks_in_band(dev);
181 : 27 : blocks = (blocks * (100 - dev->conf.overprovisioning)) / 100;
182 : :
183 : 27 : return blocks;
184 : : }
185 : :
186 : : struct ftl_layout_region *
187 : 28211270 : ftl_layout_region_get(struct spdk_ftl_dev *dev, enum ftl_layout_region_type reg_type)
188 : : {
189 : 28211270 : struct ftl_layout_region *reg = &dev->layout.region[reg_type];
190 : :
191 [ - + ]: 28211270 : assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
192 [ + + ]: 28211270 : return reg->type == reg_type ? reg : NULL;
193 : : }
194 : :
195 : : uint64_t
196 : 6 : ftl_layout_base_offset(struct spdk_ftl_dev *dev)
197 : : {
198 : 6 : return dev->num_bands * ftl_get_num_blocks_in_band(dev);
199 : : }
200 : :
201 : : static int
202 : 105 : 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 : 105 : const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
206 : 105 : size_t reg_blks = ftl_md_region_blocks(dev, entry_count * entry_size);
207 : :
208 [ - + ]: 105 : if (md_ops->region_create(dev, reg_type, reg_version, reg_blks)) {
209 : 0 : return -1;
210 : : }
211 [ - + ]: 210 : if (md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count,
212 : 105 : &dev->layout.region[reg_type])) {
213 : 0 : return -1;
214 : : }
215 : 105 : return 0;
216 : : }
217 : :
218 : : static int
219 : 39 : 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 : 39 : const struct ftl_md_layout_ops *md_ops = &dev->base_type->ops.md_layout_ops;
223 : 39 : size_t reg_blks = ftl_md_region_blocks(dev, entry_count * entry_size);
224 : :
225 [ - + ]: 39 : if (md_ops->region_create(dev, reg_type, reg_version, reg_blks)) {
226 : 0 : return -1;
227 : : }
228 [ - + ]: 78 : if (md_ops->region_open(dev, reg_type, reg_version, entry_size, entry_count,
229 : 39 : &dev->layout.region[reg_type])) {
230 : 0 : return -1;
231 : : }
232 : 39 : 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 : 6 : layout_setup_default_nvc(struct spdk_ftl_dev *dev)
427 : : {
428 : : int region_type;
429 : : uint64_t blocks;
430 : 6 : struct ftl_layout *layout = &dev->layout;
431 : :
432 : : /* Initialize L2P region */
433 : 6 : blocks = ftl_md_region_blocks(dev, layout->l2p.addr_size * dev->num_lbas);
434 [ - + ]: 6 : 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 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : 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 [ # # ]: 6 : for (region_type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN;
455 [ + + ]: 30 : region_type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX;
456 : 24 : region_type++) {
457 [ - + ]: 24 : 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 : 6 : blocks = layout->region[FTL_LAYOUT_REGION_TYPE_L2P].current.blocks;
467 : 6 : blocks = spdk_divide_round_up(blocks * sizeof(uint64_t), FTL_BLOCK_SIZE);
468 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : 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 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : 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 [ - + ]: 6 : if (0 == layout->nvc.chunk_count) {
500 : 0 : goto error;
501 : : }
502 [ - + ]: 6 : 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 [ - + ]: 6 : 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 : 6 : layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD].mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR;
515 : :
516 : 6 : 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 : 6 : layout_setup_default_base(struct spdk_ftl_dev *dev)
525 : : {
526 : 6 : 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 [ - + ]: 6 : 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 : 6 : valid_map_size = spdk_divide_round_up(layout->base.total_blocks + layout->nvc.total_blocks, 8);
540 [ - + ]: 6 : 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 : 6 : return 0;
546 : : }
547 : :
548 : : static int
549 : 6 : layout_setup_default(struct spdk_ftl_dev *dev)
550 : : {
551 [ + - - + ]: 6 : if (layout_setup_default_nvc(dev) || layout_setup_default_base(dev)) {
552 : 0 : return -1;
553 : : }
554 : 6 : return 0;
555 : : }
556 : :
557 : : static int
558 : 21 : layout_load(struct spdk_ftl_dev *dev)
559 : : {
560 [ - + ]: 21 : if (ftl_superblock_load_blob_area(dev)) {
561 : 0 : return -1;
562 : : }
563 [ - + ]: 21 : if (ftl_superblock_md_layout_apply(dev)) {
564 : 0 : return -1;
565 : : }
566 : 21 : return 0;
567 : : }
568 : :
569 : : int
570 : 27 : ftl_layout_setup(struct spdk_ftl_dev *dev)
571 : : {
572 : 27 : 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 [ + + ]: 27 : if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
597 : 6 : setup_mode = FTL_LAYOUT_SETUP_MODE_NO_RESTRICT;
598 [ - + ]: 21 : } else if (ftl_superblock_is_blob_area_empty(dev->sb)) {
599 : 0 : setup_mode = FTL_LAYOUT_SETUP_MODE_LEGACY_DEFAULT;
600 : : } else {
601 : 21 : setup_mode = FTL_LAYOUT_SETUP_MODE_LOAD_CURRENT;
602 : : }
603 [ + - ]: 27 : FTL_NOTICELOG(dev, "FTL layout setup mode %d\n", (int)setup_mode);
604 : :
605 : : /* Invalidate all regions */
606 [ + + ]: 513 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
607 [ + + + + ]: 486 : if (i == FTL_LAYOUT_REGION_TYPE_SB || i == FTL_LAYOUT_REGION_TYPE_SB_BASE) {
608 : : /* Super block has been already initialized */
609 : 54 : continue;
610 : : }
611 : :
612 : 432 : layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID;
613 : : /* Mark the region inactive */
614 : 432 : layout->region[i].type = FTL_LAYOUT_REGION_TYPE_INVALID;
615 : : }
616 : :
617 : : /*
618 : : * Initialize L2P information
619 : : */
620 : 27 : num_lbas = get_num_user_lbas(dev);
621 [ + + ]: 27 : if (dev->num_lbas == 0) {
622 [ - + ]: 6 : assert(dev->conf.mode & SPDK_FTL_MODE_CREATE);
623 : 6 : dev->num_lbas = num_lbas;
624 : 6 : dev->sb->lba_cnt = num_lbas;
625 [ - + ]: 21 : } else if (dev->num_lbas != num_lbas) {
626 [ # # ]: 0 : FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n");
627 : 0 : return -EINVAL;
628 : : }
629 : 27 : layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1;
630 [ - + ]: 27 : layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4;
631 [ - + ]: 27 : layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size;
632 : :
633 : : /* Setup P2L ckpt */
634 : 27 : layout->p2l.pages_per_xfer = spdk_divide_round_up(dev->xfer_size, FTL_NUM_P2L_ENTRIES_NO_VSS);
635 : 54 : layout->p2l.ckpt_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev),
636 : 27 : dev->xfer_size) * layout->p2l.pages_per_xfer;
637 : :
638 : 27 : layout->nvc.chunk_data_blocks = ftl_get_num_blocks_in_band(dev);
639 [ - + ]: 27 : layout->nvc.chunk_count = layout->nvc.total_blocks / ftl_get_num_blocks_in_band(dev);
640 : 27 : layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache);
641 : :
642 : 27 : layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev);
643 : 27 : layout->base.user_blocks = ftl_band_user_blocks(dev->bands);
644 : :
645 [ - + + - ]: 27 : 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 : 21 : case FTL_LAYOUT_SETUP_MODE_LOAD_CURRENT:
653 [ - + ]: 21 : if (layout_load(dev)) {
654 : 0 : return -EINVAL;
655 : : }
656 : 21 : break;
657 : :
658 : 6 : case FTL_LAYOUT_SETUP_MODE_NO_RESTRICT:
659 [ - + ]: 6 : if (layout_setup_default(dev)) {
660 : 0 : return -EINVAL;
661 : : }
662 : 6 : break;
663 : :
664 : 0 : default:
665 : 0 : ftl_abort();
666 : : break;
667 : : }
668 : :
669 [ - + ]: 27 : if (ftl_validate_regions(dev, layout)) {
670 : 0 : return -EINVAL;
671 : : }
672 : :
673 : 27 : rc = ftl_superblock_store_blob_area(dev);
674 : :
675 [ + - ]: 27 : FTL_NOTICELOG(dev, "Base device capacity: %.2f MiB\n",
676 : : blocks2mib(layout->base.total_blocks));
677 [ + - ]: 27 : FTL_NOTICELOG(dev, "NV cache device capacity: %.2f MiB\n",
678 : : blocks2mib(layout->nvc.total_blocks));
679 [ + - ]: 27 : FTL_NOTICELOG(dev, "L2P entries: %"PRIu64"\n", dev->num_lbas);
680 [ + - ]: 27 : FTL_NOTICELOG(dev, "L2P address size: %"PRIu64"\n", layout->l2p.addr_size);
681 [ + - ]: 27 : FTL_NOTICELOG(dev, "P2L checkpoint pages: %"PRIu64"\n", layout->p2l.ckpt_pages);
682 [ + - ]: 27 : FTL_NOTICELOG(dev, "NV cache chunk count %"PRIu64"\n", dev->layout.nvc.chunk_count);
683 : :
684 : 27 : return rc;
685 : : }
686 : :
687 : : int
688 : 27 : ftl_layout_setup_superblock(struct spdk_ftl_dev *dev)
689 : : {
690 : : const struct spdk_bdev *bdev;
691 : 27 : struct ftl_layout *layout = &dev->layout;
692 : 27 : struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
693 : : uint64_t total_blocks, offset, left;
694 : :
695 [ - + ]: 27 : assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL);
696 : :
697 : 27 : bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
698 : 27 : layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev);
699 : :
700 : 27 : bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
701 : 27 : layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev);
702 : :
703 : : /* Initialize superblock region */
704 [ - + ]: 27 : 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 [ - + ]: 27 : assert(region->bdev_desc != NULL);
711 [ - + ]: 27 : assert(region->ioch != NULL);
712 [ - + ]: 27 : assert(region->current.offset == 0);
713 : :
714 [ - + ]: 27 : 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 : 27 : layout->region[FTL_LAYOUT_REGION_TYPE_SB].mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE;
720 : :
721 : 27 : region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE];
722 [ - + ]: 27 : assert(region->current.offset == 0);
723 : :
724 : : /* Check if SB can be stored at the end of base device */
725 : 27 : total_blocks = spdk_bdev_get_num_blocks(
726 : 27 : spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
727 : 27 : offset = region->current.offset + region->current.blocks;
728 : 27 : left = total_blocks - offset;
729 [ + - - + ]: 27 : 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 : 27 : 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 : 27 : ftl_layout_dump(struct spdk_ftl_dev *dev)
754 : : {
755 : : struct ftl_layout_region *reg;
756 : : enum ftl_layout_region_type i;
757 : :
758 [ + - ]: 27 : FTL_NOTICELOG(dev, "NV cache layout:\n");
759 [ + + ]: 513 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
760 : 486 : reg = ftl_layout_region_get(dev, i);
761 [ + + + + ]: 486 : if (reg && reg->bdev_desc == dev->nv_cache.bdev_desc) {
762 : 378 : dump_region(dev, reg);
763 : : }
764 : : }
765 [ + - ]: 27 : FTL_NOTICELOG(dev, "Base device layout:\n");
766 [ + + ]: 513 : for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) {
767 : 486 : reg = ftl_layout_region_get(dev, i);
768 [ + + + + ]: 486 : if (reg && reg->bdev_desc == dev->base_bdev_desc) {
769 : 81 : dump_region(dev, reg);
770 : : }
771 : : }
772 : 27 : }
773 : :
774 : : uint64_t
775 : 27 : ftl_layout_base_md_blocks(struct spdk_ftl_dev *dev)
776 : : {
777 : : const struct spdk_bdev *bdev;
778 : 27 : uint64_t md_blocks = 0, total_blocks = 0;
779 : :
780 : 27 : bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc);
781 : 27 : total_blocks += spdk_bdev_get_num_blocks(bdev);
782 : :
783 : 27 : bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
784 : 27 : total_blocks += spdk_bdev_get_num_blocks(bdev);
785 : :
786 : : /* Count space needed for validity map */
787 : 27 : md_blocks += ftl_md_region_blocks(dev, spdk_divide_round_up(total_blocks, 8));
788 : :
789 : : /* Count space needed for superblock */
790 : 27 : md_blocks += superblock_region_blocks(dev);
791 : 27 : 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 : 35 : ftl_layout_blob_store(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_buf_sz)
802 : : {
803 : 35 : struct layout_blob_entry *blob_entry = blob_buf;
804 : : struct ftl_layout_region *reg;
805 : : enum ftl_layout_region_type reg_type;
806 : 35 : size_t blob_sz = 0;
807 : :
808 [ + + ]: 665 : for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
809 [ - + ]: 630 : if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
810 : 0 : return 0;
811 : : }
812 : :
813 : 630 : reg = &dev->layout.region[reg_type];
814 : 630 : blob_entry->type = reg_type;
815 : 630 : blob_entry->entry_size = reg->entry_size;
816 : 630 : blob_entry->num_entries = reg->num_entries;
817 : :
818 : 630 : blob_entry++;
819 : 630 : blob_sz += sizeof(*blob_entry);
820 : : }
821 : :
822 : 35 : return blob_sz;
823 : : }
824 : :
825 : : int
826 : 49 : ftl_layout_blob_load(struct spdk_ftl_dev *dev, void *blob_buf, size_t blob_sz)
827 : : {
828 : 49 : struct layout_blob_entry *blob_entry = blob_buf;
829 : 49 : size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
830 : 49 : struct layout_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
831 : : struct ftl_layout_region *reg;
832 : :
833 [ - + ]: 49 : if (blob_sz % sizeof(*blob_entry) != 0) {
834 : : /* Invalid blob size */
835 : 0 : return -1;
836 : : }
837 : :
838 [ + + ]: 871 : for (; blob_entry < blob_entry_end; blob_entry++) {
839 : : /* Verify the type */
840 [ + + ]: 826 : if (blob_entry->type >= FTL_LAYOUT_REGION_TYPE_MAX) {
841 : 4 : return -1;
842 : : }
843 : :
844 : : /* Load the entry */
845 : 822 : reg = &dev->layout.region[blob_entry->type];
846 : 822 : reg->entry_size = blob_entry->entry_size;
847 : 822 : reg->num_entries = blob_entry->num_entries;
848 : : }
849 : :
850 : 45 : 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 : : }
|