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_nv_cache.h" 8 #include "ftl_layout_upgrade.h" 9 #include "ftl_utils.h" 10 11 struct upgrade_ctx { 12 struct ftl_md *md_v2; 13 struct ftl_layout_region reg_v2; 14 }; 15 16 static void 17 v1_to_v2_upgrade_cleanup(struct ftl_layout_upgrade_ctx *lctx) 18 { 19 struct upgrade_ctx *ctx = lctx->ctx; 20 21 if (ctx->md_v2) { 22 ftl_md_destroy(ctx->md_v2, 0); 23 ctx->md_v2 = NULL; 24 } 25 } 26 27 static void 28 v1_to_v2_upgrade_finish(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, int status) 29 { 30 struct upgrade_ctx *ctx = lctx->ctx; 31 32 v1_to_v2_upgrade_cleanup(lctx); 33 ftl_region_upgrade_completed(dev, lctx, ctx->reg_v2.entry_size, ctx->reg_v2.num_entries, status); 34 } 35 36 static void 37 v1_to_v2_upgrade_set(struct ftl_layout_upgrade_ctx *lctx) 38 { 39 struct upgrade_ctx *ctx = lctx->ctx; 40 struct ftl_nv_cache_chunk_md *md = ftl_md_get_buffer(ctx->md_v2); 41 42 assert(sizeof(struct ftl_nv_cache_chunk_md) == FTL_BLOCK_SIZE); 43 for (uint64_t i = 0; i < ctx->reg_v2.current.blocks; i++, md++) { 44 ftl_nv_cache_chunk_md_initialize(md); 45 } 46 } 47 48 static void 49 v1_to_v2_upgrade_md_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status) 50 { 51 struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx; 52 53 v1_to_v2_upgrade_finish(dev, lctx, status); 54 } 55 56 static int 57 v1_to_v2_upgrade_setup_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, 58 uint32_t type) 59 { 60 struct upgrade_ctx *ctx = lctx->ctx; 61 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops; 62 63 assert(sizeof(struct ftl_nv_cache_chunk_md) == FTL_BLOCK_SIZE); 64 65 /* Create the new NV cache metadata region - v2 */ 66 if (md_ops->region_open(dev, type, FTL_NVC_VERSION_2, sizeof(struct ftl_nv_cache_chunk_md), 67 dev->layout.nvc.chunk_count, &ctx->reg_v2)) { 68 return -1; 69 } 70 ctx->md_v2 = ftl_md_create(dev, ctx->reg_v2.current.blocks, 0, ctx->reg_v2.name, FTL_MD_CREATE_HEAP, 71 &ctx->reg_v2); 72 if (!ctx->md_v2) { 73 return -1; 74 } 75 76 ctx->md_v2->owner.cb_ctx = lctx; 77 ctx->md_v2->cb = v1_to_v2_upgrade_md_cb; 78 v1_to_v2_upgrade_set(lctx); 79 80 return 0; 81 } 82 83 static int 84 v1_to_v2_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx) 85 { 86 struct upgrade_ctx *ctx = lctx->ctx; 87 88 /* 89 * Chunks at this point should be fully drained of user data (major upgrade). This means that it's safe to reinitialize 90 * the MD and fully change the structure layout (we're not interpreting the metadata contents at this point). 91 * Once we're done the version of the region in the superblock will be updated. 92 */ 93 94 if (v1_to_v2_upgrade_setup_ctx(dev, lctx, lctx->reg->type)) { 95 goto error; 96 } 97 ftl_md_persist(ctx->md_v2); 98 return 0; 99 100 error: 101 v1_to_v2_upgrade_cleanup(lctx); 102 return -1; 103 } 104 105 static int 106 v1_to_v2_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region) 107 { 108 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops; 109 110 assert(sizeof(struct ftl_nv_cache_chunk_md) == FTL_BLOCK_SIZE); 111 112 if (ftl_region_major_upgrade_enabled(dev, region)) { 113 return -1; 114 } 115 116 /* Create the new NV cache metadata region (v2) up front - this allocates a separate entry in the superblock and 117 * area on the cache for us. This is to reserve space for other region upgrades allocating new regions and it 118 * allows us to do an atomic upgrade of the whole region. 119 * 120 * If the upgrade is stopped by power failure/crash after the V2 region has been added, then the upgrade process 121 * will start again (since V1 still exists), but region_create will fail (since the v2 region has already been 122 * created). In such a case only verification of the region length by region_open is needed. 123 * 124 * Once the upgrade is fully done, the old v1 region entry will be removed from the SB and its area on the cache 125 * freed. 126 */ 127 if (md_ops->region_create(dev, region->type, FTL_NVC_VERSION_2, dev->layout.nvc.chunk_count) && 128 md_ops->region_open(dev, region->type, FTL_NVC_VERSION_2, sizeof(struct ftl_nv_cache_chunk_md), 129 dev->layout.nvc.chunk_count, NULL)) { 130 return -1; 131 } 132 133 return 0; 134 } 135 136 struct ftl_region_upgrade_desc nvc_upgrade_desc[] = { 137 [FTL_NVC_VERSION_0] = { 138 .verify = ftl_region_upgrade_disabled, 139 }, 140 [FTL_NVC_VERSION_1] = { 141 .verify = v1_to_v2_upgrade_enabled, 142 .ctx_size = sizeof(struct upgrade_ctx), 143 .new_version = FTL_NVC_VERSION_2, 144 .upgrade = v1_to_v2_upgrade, 145 }, 146 }; 147 148 SPDK_STATIC_ASSERT(SPDK_COUNTOF(nvc_upgrade_desc) == FTL_NVC_VERSION_CURRENT, 149 "Missing NVC region upgrade descriptors"); 150