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