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