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 uint64_t left, offset = 0; 131 struct ftl_layout *layout = &dev->layout; 132 struct ftl_layout_region *region, *mirror; 133 134 #ifdef SPDK_FTL_VSS_EMU 135 /* Skip the already init`d VSS region */ 136 region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS]; 137 offset += region->current.blocks; 138 139 if (offset >= layout->nvc.total_blocks) { 140 goto error; 141 } 142 #endif 143 144 /* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */ 145 region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; 146 offset += region->current.blocks; 147 148 /* Initialize L2P region */ 149 if (offset >= layout->nvc.total_blocks) { 150 goto error; 151 } 152 region = &layout->region[FTL_LAYOUT_REGION_TYPE_L2P]; 153 region->type = FTL_LAYOUT_REGION_TYPE_L2P; 154 region->name = "l2p"; 155 region->current.version = 0; 156 region->prev.version = 0; 157 region->current.offset = offset; 158 region->current.blocks = blocks_region(layout->l2p.addr_size * dev->num_lbas); 159 set_region_bdev_nvc(region, dev); 160 offset += region->current.blocks; 161 162 /* Initialize band info metadata */ 163 if (offset >= layout->nvc.total_blocks) { 164 goto error; 165 } 166 region = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD]; 167 region->type = FTL_LAYOUT_REGION_TYPE_BAND_MD; 168 region->mirror_type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR; 169 region->name = "band_md"; 170 region->current.version = region->prev.version = FTL_BAND_VERSION_CURRENT; 171 region->current.offset = offset; 172 region->current.blocks = blocks_region(ftl_get_num_bands(dev) * sizeof(struct ftl_band_md)); 173 region->entry_size = sizeof(struct ftl_band_md) / FTL_BLOCK_SIZE; 174 region->num_entries = ftl_get_num_bands(dev); 175 set_region_bdev_nvc(region, dev); 176 offset += region->current.blocks; 177 178 /* Initialize band info metadata mirror */ 179 if (offset >= layout->nvc.total_blocks) { 180 goto error; 181 } 182 mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR]; 183 *mirror = *region; 184 mirror->type = FTL_LAYOUT_REGION_TYPE_BAND_MD_MIRROR; 185 mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; 186 mirror->name = "band_md_mirror"; 187 mirror->current.offset += region->current.blocks; 188 offset += mirror->current.blocks; 189 190 if (offset >= layout->nvc.total_blocks) { 191 goto error; 192 } 193 194 /* 195 * Initialize NV Cache metadata 196 */ 197 if (offset >= layout->nvc.total_blocks) { 198 goto error; 199 } 200 201 left = layout->nvc.total_blocks - offset; 202 layout->nvc.chunk_data_blocks = 203 FTL_NV_CACHE_CHUNK_DATA_SIZE(ftl_get_num_blocks_in_band(dev)) / FTL_BLOCK_SIZE; 204 layout->nvc.chunk_meta_size = FTL_NV_CACHE_CHUNK_MD_SIZE; 205 layout->nvc.chunk_count = (left * FTL_BLOCK_SIZE) / 206 FTL_NV_CACHE_CHUNK_SIZE(ftl_get_num_blocks_in_band(dev)); 207 layout->nvc.chunk_tail_md_num_blocks = ftl_nv_cache_chunk_tail_md_num_blocks(&dev->nv_cache); 208 209 if (0 == layout->nvc.chunk_count) { 210 goto error; 211 } 212 region = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD]; 213 region->type = FTL_LAYOUT_REGION_TYPE_NVC_MD; 214 region->mirror_type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR; 215 region->name = "nvc_md"; 216 region->current.version = region->prev.version = FTL_NVC_VERSION_CURRENT; 217 region->current.offset = offset; 218 region->current.blocks = blocks_region(layout->nvc.chunk_count * 219 sizeof(struct ftl_nv_cache_chunk_md)); 220 region->entry_size = sizeof(struct ftl_nv_cache_chunk_md) / FTL_BLOCK_SIZE; 221 region->num_entries = layout->nvc.chunk_count; 222 set_region_bdev_nvc(region, dev); 223 offset += region->current.blocks; 224 225 /* 226 * Initialize NV Cache metadata mirror 227 */ 228 mirror = &layout->region[FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR]; 229 *mirror = *region; 230 mirror->type = FTL_LAYOUT_REGION_TYPE_NVC_MD_MIRROR; 231 mirror->mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; 232 mirror->name = "nvc_md_mirror"; 233 mirror->current.offset += region->current.blocks; 234 offset += mirror->current.blocks; 235 236 /* 237 * Initialize data region on NV cache 238 */ 239 if (offset >= layout->nvc.total_blocks) { 240 goto error; 241 } 242 region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC]; 243 region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC; 244 region->name = "data_nvc"; 245 region->current.version = region->prev.version = 0; 246 region->current.offset = offset; 247 region->current.blocks = layout->nvc.chunk_count * layout->nvc.chunk_data_blocks; 248 set_region_bdev_nvc(region, dev); 249 offset += region->current.blocks; 250 251 left = layout->nvc.total_blocks - offset; 252 if (left > layout->nvc.chunk_data_blocks) { 253 FTL_ERRLOG(dev, "Error when setup NV cache layout\n"); 254 return -1; 255 } 256 257 if (offset > layout->nvc.total_blocks) { 258 FTL_ERRLOG(dev, "Error when setup NV cache layout\n"); 259 goto error; 260 } 261 262 return 0; 263 264 error: 265 FTL_ERRLOG(dev, "Insufficient NV Cache capacity to preserve metadata\n"); 266 return -1; 267 } 268 269 static ftl_addr 270 layout_base_offset(struct spdk_ftl_dev *dev) 271 { 272 ftl_addr addr; 273 274 addr = dev->num_bands * ftl_get_num_blocks_in_band(dev); 275 return addr; 276 } 277 278 static int 279 setup_layout_base(struct spdk_ftl_dev *dev) 280 { 281 uint64_t left, offset; 282 struct ftl_layout *layout = &dev->layout; 283 struct ftl_layout_region *region; 284 285 layout->base.num_usable_blocks = ftl_get_num_blocks_in_band(dev); 286 layout->base.user_blocks = ftl_band_user_blocks(dev->bands); 287 288 /* Base device layout is following: 289 * - data 290 * - superblock 291 * - valid map 292 * 293 * Superblock has been already configured, its offset marks the end of the data region 294 */ 295 offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset; 296 297 /* Setup data region on base device */ 298 region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE]; 299 region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE; 300 region->name = "data_btm"; 301 region->current.version = region->prev.version = 0; 302 region->current.offset = 0; 303 region->current.blocks = offset; 304 set_region_bdev_btm(region, dev); 305 306 /* Move offset after base superblock */ 307 offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks; 308 309 /* Setup validity map */ 310 region = &layout->region[FTL_LAYOUT_REGION_TYPE_VALID_MAP]; 311 region->type = FTL_LAYOUT_REGION_TYPE_VALID_MAP; 312 region->name = "vmap"; 313 region->current.version = region->prev.version = 0; 314 region->current.offset = offset; 315 region->current.blocks = blocks_region(spdk_divide_round_up( 316 layout->base.total_blocks + layout->nvc.total_blocks, 8)); 317 set_region_bdev_btm(region, dev); 318 offset += region->current.blocks; 319 320 /* Checking for underflow */ 321 left = layout->base.total_blocks - offset; 322 if (left > layout->base.total_blocks) { 323 FTL_ERRLOG(dev, "Error when setup base device layout\n"); 324 return -1; 325 } 326 327 if (offset > layout->base.total_blocks) { 328 FTL_ERRLOG(dev, "Error when setup base device layout\n"); 329 return -1; 330 } 331 332 return 0; 333 } 334 335 int 336 ftl_layout_setup(struct spdk_ftl_dev *dev) 337 { 338 const struct spdk_bdev *bdev; 339 struct ftl_layout *layout = &dev->layout; 340 uint64_t i; 341 uint64_t num_lbas; 342 343 bdev = spdk_bdev_desc_get_bdev(dev->base_bdev_desc); 344 layout->base.total_blocks = spdk_bdev_get_num_blocks(bdev); 345 346 bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 347 layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev); 348 349 /* Initialize mirrors types */ 350 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 351 if (i == FTL_LAYOUT_REGION_TYPE_SB) { 352 /* Super block has been already initialized */ 353 continue; 354 } 355 356 layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; 357 } 358 359 /* 360 * Initialize L2P information 361 */ 362 num_lbas = get_num_user_lbas(dev); 363 if (dev->num_lbas == 0) { 364 assert(dev->conf.mode & SPDK_FTL_MODE_CREATE); 365 dev->num_lbas = num_lbas; 366 dev->sb->lba_cnt = num_lbas; 367 } else if (dev->num_lbas != num_lbas) { 368 FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n"); 369 return -EINVAL; 370 } 371 layout->l2p.addr_length = spdk_u64log2(layout->base.total_blocks + layout->nvc.total_blocks) + 1; 372 layout->l2p.addr_size = layout->l2p.addr_length > 32 ? 8 : 4; 373 layout->l2p.lbas_in_page = FTL_BLOCK_SIZE / layout->l2p.addr_size; 374 375 if (setup_layout_nvc(dev)) { 376 return -EINVAL; 377 } 378 379 if (setup_layout_base(dev)) { 380 return -EINVAL; 381 } 382 383 if (ftl_validate_regions(dev, layout)) { 384 return -EINVAL; 385 } 386 387 FTL_NOTICELOG(dev, "Base device capacity: %.2f MiB\n", 388 blocks2mib(layout->base.total_blocks)); 389 FTL_NOTICELOG(dev, "NV cache device capacity: %.2f MiB\n", 390 blocks2mib(layout->nvc.total_blocks)); 391 FTL_NOTICELOG(dev, "L2P entries: %"PRIu64"\n", 392 dev->num_lbas); 393 FTL_NOTICELOG(dev, "L2P address size: %"PRIu64"\n", 394 layout->l2p.addr_size); 395 396 return 0; 397 } 398 399 #ifdef SPDK_FTL_VSS_EMU 400 void 401 ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev) 402 { 403 const struct spdk_bdev *bdev; 404 struct ftl_layout *layout = &dev->layout; 405 struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS]; 406 407 assert(layout->md[FTL_LAYOUT_REGION_TYPE_VSS] == NULL); 408 409 region = &layout->region[FTL_LAYOUT_REGION_TYPE_VSS]; 410 region->type = FTL_LAYOUT_REGION_TYPE_VSS; 411 region->name = "vss"; 412 region->current.version = region->prev.version = 0; 413 region->current.offset = 0; 414 415 bdev = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc); 416 layout->nvc.total_blocks = spdk_bdev_get_num_blocks(bdev); 417 region->current.blocks = blocks_region(dev->nv_cache.md_size * layout->nvc.total_blocks); 418 419 region->vss_blksz = 0; 420 region->bdev_desc = dev->nv_cache.bdev_desc; 421 region->ioch = dev->nv_cache.cache_ioch; 422 423 assert(region->bdev_desc != NULL); 424 assert(region->ioch != NULL); 425 } 426 #endif 427 428 int 429 ftl_layout_setup_superblock(struct spdk_ftl_dev *dev) 430 { 431 struct ftl_layout *layout = &dev->layout; 432 struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; 433 uint64_t total_blocks, offset, left; 434 435 assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL); 436 437 /* Initialize superblock region */ 438 region->type = FTL_LAYOUT_REGION_TYPE_SB; 439 region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE; 440 region->name = "sb"; 441 region->current.version = FTL_METADATA_VERSION_CURRENT; 442 region->prev.version = FTL_METADATA_VERSION_CURRENT; 443 region->current.offset = 0; 444 445 /* 446 * VSS region must go first in case SB to make calculating its relative size easier 447 */ 448 #ifdef SPDK_FTL_VSS_EMU 449 region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset + 450 layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks; 451 #endif 452 453 region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); 454 region->vss_blksz = 0; 455 region->bdev_desc = dev->nv_cache.bdev_desc; 456 region->ioch = dev->nv_cache.cache_ioch; 457 458 assert(region->bdev_desc != NULL); 459 assert(region->ioch != NULL); 460 461 region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE]; 462 region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE; 463 region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX; 464 region->name = "sb_mirror"; 465 region->current.version = FTL_METADATA_VERSION_CURRENT; 466 region->prev.version = FTL_METADATA_VERSION_CURRENT; 467 /* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts 468 * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore) 469 */ 470 region->current.offset = layout_base_offset(dev); 471 region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); 472 set_region_bdev_btm(region, dev); 473 474 /* Check if SB can be stored at the end of base device */ 475 total_blocks = spdk_bdev_get_num_blocks( 476 spdk_bdev_desc_get_bdev(dev->base_bdev_desc)); 477 offset = region->current.offset + region->current.blocks; 478 left = total_blocks - offset; 479 if ((left > total_blocks) || (offset > total_blocks)) { 480 FTL_ERRLOG(dev, "Error when setup base device super block\n"); 481 return -1; 482 } 483 484 return 0; 485 } 486 487 void 488 ftl_layout_dump(struct spdk_ftl_dev *dev) 489 { 490 struct ftl_layout *layout = &dev->layout; 491 int i; 492 FTL_NOTICELOG(dev, "NV cache layout:\n"); 493 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 494 if (layout->region[i].bdev_desc == dev->nv_cache.bdev_desc) { 495 dump_region(dev, &layout->region[i]); 496 } 497 } 498 FTL_NOTICELOG(dev, "Bottom device layout:\n"); 499 for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { 500 if (layout->region[i].bdev_desc == dev->base_bdev_desc) { 501 dump_region(dev, &layout->region[i]); 502 } 503 } 504 } 505