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