1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "ftl_sb.h" 7 #include "ftl_core.h" 8 #include "ftl_layout.h" 9 #include "upgrade/ftl_sb_prev.h" 10 11 bool 12 ftl_superblock_check_magic(struct ftl_superblock *sb) 13 { 14 if (sb->header.version >= FTL_SB_VERSION_3) { 15 return sb->header.magic == FTL_SUPERBLOCK_MAGIC; 16 } else { 17 return sb->header.magic == FTL_SUPERBLOCK_MAGIC_V2; 18 } 19 } 20 bool 21 ftl_superblock_md_layout_is_empty(struct ftl_superblock *sb) 22 { 23 return sb->md_layout_head.type == FTL_LAYOUT_REGION_TYPE_INVALID; 24 } 25 26 static bool 27 md_region_is_fixed(int reg_type) 28 { 29 switch (reg_type) { 30 #ifdef SPDK_FTL_VSS_EMU 31 case FTL_LAYOUT_REGION_TYPE_VSS: 32 #endif 33 case FTL_LAYOUT_REGION_TYPE_SB: 34 case FTL_LAYOUT_REGION_TYPE_SB_BASE: 35 case FTL_LAYOUT_REGION_TYPE_DATA_BASE: 36 return true; 37 38 default: 39 return false; 40 } 41 } 42 43 static uint32_t 44 md_regions_count(void) 45 { 46 uint32_t reg_cnt = 0, reg_type; 47 48 for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) { 49 if (md_region_is_fixed(reg_type)) { 50 continue; 51 } 52 53 reg_cnt++; 54 } 55 return reg_cnt; 56 } 57 58 static bool 59 superblock_md_region_overflow(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg) 60 { 61 /* sb_reg is part of the sb structure - the pointer should be at a positive offset */ 62 if ((uintptr_t)sb_reg < (uintptr_t)dev->sb) { 63 return true; 64 } 65 66 /* Make sure the entry doesn't overflow the pointer value (probably overkill to check) */ 67 if (UINT64_MAX - (uintptr_t)sb_reg <= sizeof(*sb_reg)) { 68 return true; 69 } 70 71 /* There's only a finite (FTL_SUPERBLOCK_SIZE) amount of space in the superblock. Make sure the region wholly fits in that space. */ 72 if ((uintptr_t)(sb_reg + 1) > ((uintptr_t)(dev->sb) + FTL_SUPERBLOCK_SIZE)) { 73 return true; 74 } 75 76 return false; 77 } 78 79 static int 80 superblock_md_layout_add(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg, 81 uint32_t reg_type, uint32_t reg_version, uint64_t blk_offs, uint64_t blk_sz) 82 { 83 if (superblock_md_region_overflow(dev, sb_reg)) { 84 FTL_ERRLOG(dev, "Buffer overflow\n"); 85 return -EOVERFLOW; 86 } 87 88 sb_reg->type = reg_type; 89 sb_reg->version = reg_version; 90 sb_reg->blk_offs = blk_offs; 91 sb_reg->blk_sz = blk_sz; 92 return 0; 93 } 94 95 static int 96 superblock_md_layout_add_free(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region **sb_reg, 97 uint32_t reg_type, uint32_t free_type, uint64_t total_blocks) 98 { 99 struct ftl_layout *layout = &dev->layout; 100 struct ftl_layout_region *reg = &layout->region[reg_type]; 101 uint64_t blks_left = total_blocks - reg->current.offset - reg->current.blocks; 102 103 if (blks_left == 0) { 104 return 0; 105 } 106 107 (*sb_reg)->df_next = ftl_df_get_obj_id(dev->sb, (*sb_reg) + 1); 108 (*sb_reg) = (*sb_reg) + 1; 109 110 if (superblock_md_layout_add(dev, *sb_reg, free_type, 0, 111 reg->current.offset + reg->current.blocks, blks_left)) { 112 return -1; 113 } 114 115 (*sb_reg)->df_next = FTL_DF_OBJ_ID_INVALID; 116 117 return 0; 118 } 119 120 int 121 ftl_superblock_md_layout_build(struct spdk_ftl_dev *dev) 122 { 123 struct ftl_superblock *sb = dev->sb; 124 struct ftl_layout *layout = &dev->layout; 125 struct ftl_layout_region *reg; 126 int n = 0; 127 bool is_empty = ftl_superblock_md_layout_is_empty(dev->sb); 128 struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head; 129 130 /* TODO: major upgrades: add all free regions being tracked 131 * For now SB MD layout must be empty - otherwise md free regions may be lost */ 132 assert(is_empty); 133 134 for (; n < FTL_LAYOUT_REGION_TYPE_MAX;) { 135 reg = &layout->region[n]; 136 if (md_region_is_fixed(reg->type)) { 137 reg->current.sb_md_reg = NULL; 138 n++; 139 140 if (n >= FTL_LAYOUT_REGION_TYPE_MAX) { 141 /* For VSS emulation the last layout type is a fixed region, we need to move back the list and end the list on previous entry */ 142 sb_reg--; 143 break; 144 } 145 continue; 146 } 147 148 if (superblock_md_layout_add(dev, sb_reg, reg->type, reg->current.version, 149 reg->current.offset, reg->current.blocks)) { 150 return -1; 151 } 152 reg->current.sb_md_reg = sb_reg; 153 154 n++; 155 if (n < FTL_LAYOUT_REGION_TYPE_MAX) { 156 /* next region */ 157 sb_reg->df_next = ftl_df_get_obj_id(sb, sb_reg + 1); 158 sb_reg++; 159 } 160 } 161 162 /* terminate the list */ 163 sb_reg->df_next = FTL_DF_OBJ_ID_INVALID; 164 165 /* create free_nvc/free_base regions on the first run */ 166 if (is_empty) { 167 superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_NVC, 168 FTL_LAYOUT_REGION_TYPE_FREE_NVC, layout->nvc.total_blocks); 169 170 superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_BASE, 171 FTL_LAYOUT_REGION_TYPE_FREE_BASE, layout->base.total_blocks); 172 } 173 174 return 0; 175 } 176 177 int 178 ftl_superblock_md_layout_load_all(struct spdk_ftl_dev *dev) 179 { 180 struct ftl_superblock *sb = dev->sb; 181 struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head; 182 struct ftl_layout *layout = &dev->layout; 183 uint32_t regs_found = 0; 184 185 for (int n = 0; n < FTL_LAYOUT_REGION_TYPE_MAX; n++) { 186 if (md_region_is_fixed(sb_reg->type)) { 187 continue; 188 } 189 190 layout->region[n].current.sb_md_reg = NULL; 191 layout->region[n].prev = layout->region[n].current; 192 } 193 194 while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) { 195 struct ftl_layout_region *reg; 196 197 /* TODO: major upgrades: add free regions tracking */ 198 if (sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_NVC || 199 sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_BASE) { 200 goto next_sb_reg; 201 } 202 203 if (sb_reg->type >= FTL_LAYOUT_REGION_TYPE_MAX) { 204 FTL_ERRLOG(dev, "Invalid MD region type found\n"); 205 return -1; 206 } 207 208 if (md_region_is_fixed(sb_reg->type)) { 209 FTL_ERRLOG(dev, "Unsupported MD region type found\n"); 210 return -1; 211 } 212 213 reg = &layout->region[sb_reg->type]; 214 if (sb_reg->version == reg->current.version) { 215 if (reg->current.sb_md_reg) { 216 FTL_ERRLOG(dev, "Multiple/looping current regions found\n"); 217 return -1; 218 } 219 220 reg->current.offset = sb_reg->blk_offs; 221 reg->current.blocks = sb_reg->blk_sz; 222 reg->current.sb_md_reg = sb_reg; 223 224 if (!reg->prev.sb_md_reg) { 225 regs_found++; 226 } 227 } else { 228 if (sb_reg->version > reg->current.version) { 229 FTL_ERRLOG(dev, "Unknown region version found\n"); 230 return -1; 231 } 232 /* 233 * The metadata regions are kept as a linked list, it's therefore possible to have it corrupted and contain loops. 234 * If the prev region has already been updated to the oldest found version (see following comment/block) and we see 235 * the same version again, it's a loop and error. 236 */ 237 if (sb_reg->version == reg->prev.version) { 238 FTL_ERRLOG(dev, "Multiple/looping prev regions found\n"); 239 return -1; 240 } 241 242 /* 243 * The following check is in preparation for major metadata upgrade. 244 * It's possible that a region will need to be increased in size. It will allocate an additional region in this case 245 * and upgrade each entries one by one. If in the middle of this upgrade process a dirty shutdown occurs, there will be 246 * multiple entries of the same metadata region in the superblock (old original and partially updated one). This will then 247 * effectively rollback the upgrade to the oldest found metadata section and start from the beginning. 248 */ 249 if (sb_reg->version < reg->prev.version) { 250 if ((!reg->current.sb_md_reg) && (!reg->prev.sb_md_reg)) { 251 regs_found++; 252 } 253 254 reg->prev.offset = sb_reg->blk_offs; 255 reg->prev.blocks = sb_reg->blk_sz; 256 reg->prev.version = sb_reg->version; 257 reg->prev.sb_md_reg = sb_reg; 258 } 259 } 260 261 next_sb_reg: 262 if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) { 263 break; 264 } 265 266 if (UINT64_MAX - (uintptr_t)sb <= sb_reg->df_next) { 267 FTL_ERRLOG(dev, "Buffer overflow\n"); 268 return -EOVERFLOW; 269 } 270 271 sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next); 272 if (superblock_md_region_overflow(dev, sb_reg)) { 273 FTL_ERRLOG(dev, "Buffer overflow\n"); 274 return -EOVERFLOW; 275 } 276 } 277 278 if (regs_found != md_regions_count()) { 279 FTL_ERRLOG(dev, "Missing regions\n"); 280 return -1; 281 } 282 283 return 0; 284 } 285 286 static void 287 ftl_superblock_md_layout_free_region(struct spdk_ftl_dev *dev, 288 struct ftl_superblock_md_region *sb_reg) 289 { 290 /* TODO: major upgrades: implement */ 291 sb_reg->type = FTL_LAYOUT_REGION_TYPE_FREE_NVC; 292 } 293 294 int 295 ftl_superblock_md_layout_upgrade_region(struct spdk_ftl_dev *dev, 296 struct ftl_superblock_md_region *sb_reg, uint32_t new_version) 297 { 298 struct ftl_superblock *sb = dev->sb; 299 struct ftl_superblock_md_region *sb_reg_iter = &sb->md_layout_head; 300 struct ftl_layout *layout = &dev->layout; 301 struct ftl_layout_region *reg = &layout->region[sb_reg->type]; 302 uint32_t old_version = sb_reg->version; 303 304 assert(sb_reg); 305 assert(reg->prev.sb_md_reg == sb_reg); 306 assert(new_version > old_version); 307 308 while (sb_reg_iter->type != FTL_LAYOUT_REGION_TYPE_INVALID) { 309 if (sb_reg_iter->type != sb_reg->type) { 310 goto next_sb_reg_iter; 311 } 312 313 /* Verify all region versions up to new_version are updated: */ 314 if (sb_reg_iter->version != old_version && sb_reg_iter->version < new_version) { 315 FTL_ERRLOG(dev, "Region upgrade skipped\n"); 316 return -1; 317 } 318 319 if (sb_reg_iter->version == new_version) { 320 /* Major upgrades: update region prev version to the new version region found */ 321 assert(sb_reg != sb_reg_iter); 322 reg->prev.offset = sb_reg_iter->blk_offs; 323 reg->prev.blocks = sb_reg_iter->blk_sz; 324 reg->prev.version = sb_reg_iter->version; 325 reg->prev.sb_md_reg = sb_reg_iter; 326 327 ftl_superblock_md_layout_free_region(dev, sb_reg); 328 goto exit; 329 } 330 331 next_sb_reg_iter: 332 if (sb_reg_iter->df_next == FTL_DF_OBJ_ID_INVALID) { 333 break; 334 } 335 336 sb_reg_iter = ftl_df_get_obj_ptr(sb, sb_reg_iter->df_next); 337 if (superblock_md_region_overflow(dev, sb_reg_iter)) { 338 FTL_ERRLOG(dev, "Buffer overflow\n"); 339 return -EOVERFLOW; 340 } 341 } 342 343 /* Minor upgrades: update the region in place (only the new version) */ 344 assert(sb_reg == reg->prev.sb_md_reg); 345 sb_reg->version = new_version; 346 reg->prev.version = new_version; 347 348 exit: 349 /* Update the region current version */ 350 if (new_version == reg->current.version) { 351 reg->current.offset = sb_reg->blk_offs; 352 reg->current.blocks = sb_reg->blk_sz; 353 reg->current.version = sb_reg->version; 354 reg->current.sb_md_reg = sb_reg; 355 } 356 357 return 0; 358 } 359 360 void 361 ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev) 362 { 363 struct ftl_superblock *sb = dev->sb; 364 struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head; 365 366 FTL_NOTICELOG(dev, "SB metadata layout:\n"); 367 while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) { 368 FTL_NOTICELOG(dev, 369 "Region df:0x%"PRIx64" type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n", 370 ftl_df_get_obj_id(sb, sb_reg), sb_reg->type, sb_reg->version, sb_reg->blk_offs, sb_reg->blk_sz); 371 372 if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) { 373 break; 374 } 375 376 sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next); 377 if (superblock_md_region_overflow(dev, sb_reg)) { 378 FTL_ERRLOG(dev, "Buffer overflow\n"); 379 return; 380 } 381 } 382 } 383