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