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; 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 NV Cache metadata 227 */ 228 if (offset >= layout->nvc.total_blocks) { 229 goto error; 230 } 231 232 left = layout->nvc.total_blocks - offset; 233 layout->nvc.chunk_data_blocks = 234 FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_band(dev)) / FTL_BLOCK_SIZE; 235 layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE; 236 layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) / 237 FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_band(dev)); 238 layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache); 239 240 if (0 == layout->nvc.chunk_count) { 241 goto error; 242 } 243 region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD]; 244 region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD; 245 region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR; 246 region->name = "nvc_md"; 247 region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT; 248 region->current.offset = offset; 249 region->current.blocks = blocks_region(layout->nvc.chunk_count * 250 sizeof(struct ftl_nv_cache_chunk_md)); 251 region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE; 252 region->num_entries = layout->nvc.chunk_count; 253 set_region_bdev_nvc(region, dev); 254 offset += region->current.blocks; 255 256 /* 257 * Initialize NV Cache metadata mirror 258 */ 259 mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR]; 260 *mirror = *region; 261 mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR; 262 mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; 263 mirror->name = "nvc_md_mirror"; 264 mirror->current.offset += region->current.blocks; 265 offset += mirror->current.blocks; 266 267 /* 268 * Initialize data region on NV cache 269 */ 270 if (offset >= layout->nvc.total_blocks) { 271 goto error; 272 } 273 region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC]; 274 region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC; 275 region->name = "data_nvc"; 276 region->current.version = region->prev.version = 0; 277 region->current.offset = offset; 278 region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks; 279 set_region_bdev_nvc(region, dev); 280 offset += region->current.blocks; 281 282 left = layout->nvc.total_blocks - offset; 283 if (left > layout->nvc.chunk_data_blocks) { 284 FTL_ERRLOG(dev, "Error when setup NV cache layout\n"); 285 return -1; 286 } 287 288 if (offset > layout->nvc.total_blocks) { 289 FTL_ERRLOG(dev, "Error when setup NV cache layout\n"); 290 goto error; 291 } 292 293 return 0; 294 295 error: 296 FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n"); 297 return -1; 298 } 299 300 static ftl_addr 301 layout_base_offset(struct spdk_ftl_dev *dev) 302 { 303 ftl_addr addr; 304 305 addr = dev->num_bands * ftl_get_num_blocks_in_band(dev); 306 return addr; 307 } 308 309 static int 310 setup_layout_base(struct spdk_ftl_dev *dev) 311 { 312 uint64_t left, offset; 313 struct ftl_layout *layout = &dev->layout; 314 struct ftl_layout_region *region; 315 316 layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev); 317 layout->base.user_blocks = ftl_band_user_blocks(dev->bands); 318 319 /* Base device layout is following: 320 * - data 321 * - superblock 322 * - valid map 323 * 324 * Superblock has been already configured, its offset marks the end of the data region 325 */ 326 offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset; 327 328 /* Setup data region on base device */ 329 region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE]; 330 region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE; 331 region->name = "data_btm"; 332 region->current.version = region->prev.version = 0; 333 region->current.offset = 0; 334 region->current.blocks = offset; 335 set_region_bdev_btm(region, dev); 336 337 /* Move offset after base superblock */ 338 offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks; 339 340 /* Setup validity map */ 341 region = &layout->region[FTL_LAYOUT_REGION_TYPE_VALID_MAP]; 342 region->type = FTL_LAYOUT_REGION_TYPE_VALID_MAP; 343 region->name = "vmap"; 344 region->current.version = region->prev.version = 0; 345 region->current.offset = offset; 346 region->current.blocks = blocks_region(spdk_divide_round_up( 347 layout->base.total_blocks + layout->nvc.total_blocks, 8)); 348 set_region_bdev_btm(region, dev); 349 offset += region->current.blocks; 350 351 /* Checking for underflow */ 352 left = layout->base.total_blocks - offset; 353 if (left > layout->base.total_blocks) { 354 FTL_ERRLOG(dev, "Error when setup base device layout\n"); 355 return -1; 356 } 357 358 if (offset > layout->base.total_blocks) { 359 FTL_ERRLOG(dev, "Error when setup base device layout\n"); 360 return -1; 361 } 362 363 return 0; 364 } 365 366 int 367 ftl_layout_setup(struct spdk_ftl_dev *dev) 368 { 369 const struct spdk_bdev *bdev; 370 struct ftl_layout *layout = &dev->layout; 371 uint64_t i; 372 uint64_t num_lbas; 373 374 bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 375 layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev); 376 377 bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 378 layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev); 379 380 /* Initialize mirrors types */ 381 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 382 if (i == FTL_LAYOUT_REGION_TYPE_SB) { 383 /* Super block has been already initialized */ 384 continue; 385 } 386 387 layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; 388 } 389 390 /* 391 * Initialize L2P information 392 */ 393 num_lbas = get_num_user_lbas(dev); 394 if (dev->num_lbas == 0) { 395 assert(dev->conf.mode & SPDK_FTL_MODE_CREATE); 396 dev->num_lbas = num_lbas; 397 dev->sb->lba_cnt = num_lbas; 398 } else if (dev->num_lbas != num_lbas) { 399 FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n"); 400 return -EINVAL; 401 } 402 layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1; 403 layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4; 404 layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size; 405 406 /* Setup P2L ckpt */ 407 layout->p2l.ckpt_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev), dev->xfer_size); 408 409 if (setup_layout_nvc(dev)) { 410 return -EINVAL; 411 } 412 413 if (setup_layout_base(dev)) { 414 return -EINVAL; 415 } 416 417 if (ftl_validate_regions(dev, layout)) { 418 return -EINVAL; 419 } 420 421 FTL_NOTICELOG(dev, "Base device capacity: %.2f MiB\n", 422 blocks2mib(layout->base.total_blocks)); 423 FTL_NOTICELOG(dev, "NV cache device capacity: %.2f MiB\n", 424 blocks2mib(layout->nvc.total_blocks)); 425 FTL_NOTICELOG(dev, "L2P entries: %"PRIu64"\n", dev->num_lbas); 426 FTL_NOTICELOG(dev, "L2P address size: %"PRIu64"\n", layout->l2p.addr_size); 427 FTL_NOTICELOG(dev, "P2L checkpoint pages: %"PRIu64"\n", layout->p2l.ckpt_pages); 428 429 return 0; 430 } 431 432 #ifdef SPDK_FTL_VSS_EMU 433 void 434 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev) 435 { 436 const struct spdk_bdev *bdev; 437 struct ftl_layout *layout = &dev->layout; 438 struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS]; 439 440 assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL); 441 442 region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS]; 443 region->type = FTL_LAYOUT_REGION_TYPE_VSS; 444 region->name = "vss"; 445 region->current.version = region->prev.version = 0; 446 region->current.offset = 0; 447 448 bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 449 layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev); 450 region->current.blocks = blocks_region(dev->nv_cache.md_size * layout->nvc.total_blocks); 451 452 region->vss_blksz = 0; 453 region->bdev_desc = dev->nv_cache.bdev_desc; 454 region->ioch = dev->nv_cache.cache_ioch; 455 456 assert(region->bdev_desc != NULL); 457 assert(region->ioch != NULL); 458 } 459 #endif 460 461 int 462 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev) 463 { 464 struct ftl_layout *layout = &dev->layout; 465 struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; 466 uint64_t total_blocks, offset, left; 467 468 assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL); 469 470 /* Initialize superblock region */ 471 region->type = FTL_LAYOUT_REGION_TYPE_SB; 472 region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE; 473 region->name = "sb"; 474 region->current.version = FTL_METADATA_VERSION_CURRENT; 475 region->prev.version = FTL_METADATA_VERSION_CURRENT; 476 region->current.offset = 0; 477 478 /* 479 * VSS region must go first in case SB to make calculating its relative size easier 480 */ 481 #ifdef SPDK_FTL_VSS_EMU 482 region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset + 483 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks; 484 #endif 485 486 region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); 487 region->vss_blksz = 0; 488 region->bdev_desc = dev->nv_cache.bdev_desc; 489 region->ioch = dev->nv_cache.cache_ioch; 490 491 assert(region->bdev_desc != NULL); 492 assert(region->ioch != NULL); 493 494 region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE]; 495 region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE; 496 region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX; 497 region->name = "sb_mirror"; 498 region->current.version = FTL_METADATA_VERSION_CURRENT; 499 region->prev.version = FTL_METADATA_VERSION_CURRENT; 500 /* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts 501 * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore) 502 */ 503 region->current.offset = layout_base_offset(dev); 504 region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); 505 set_region_bdev_btm(region, dev); 506 507 /* Check if SB can be stored at the end of base device */ 508 total_blocks = spdk_bdev_get_num_blocks( 509 spdk_bdev_desc_get_bdev(dev->base_bdev_desc)); 510 offset = region->current.offset + region->current.blocks; 511 left = total_blocks - offset; 512 if ((left > total_blocks) || (offset > total_blocks)) { 513 FTL_ERRLOG(dev, "Error when setup base device super block\n"); 514 return -1; 515 } 516 517 return 0; 518 } 519 520 void 521 ftl_layout_dump(struct spdk_ftl_dev *dev) 522 { 523 struct ftl_layout *layout = &dev->layout; 524 int i; 525 FTL_NOTICELOG(dev, "NV cache layout:\n"); 526 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 527 if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) { 528 dump_region(dev, &layout->region[i]); 529 } 530 } 531 FTL_NOTICELOG(dev, "Bottom device layout:\n"); 532 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 533 if (layout->region[i].bdev_desc == dev->base_bdev_desc) { 534 dump_region(dev, &layout->region[i]); 535 } 536 } 537 } 538