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