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_band.h"
8 #include "ftl_layout_upgrade.h"
9
10 struct upgrade_ctx {
11 struct ftl_md *md;
12 struct ftl_layout_region reg;
13 };
14
15 static void
v2_upgrade_cleanup(struct ftl_layout_upgrade_ctx * lctx)16 v2_upgrade_cleanup(struct ftl_layout_upgrade_ctx *lctx)
17 {
18 struct upgrade_ctx *ctx = lctx->ctx;
19
20 if (ctx->md) {
21 ftl_md_destroy(ctx->md, 0);
22 ctx->md = NULL;
23 }
24 }
25
26 static void
v2_upgrade_finish(struct spdk_ftl_dev * dev,struct ftl_layout_upgrade_ctx * lctx,int status)27 v2_upgrade_finish(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, int status)
28 {
29 struct upgrade_ctx *ctx = lctx->ctx;
30
31 v2_upgrade_cleanup(lctx);
32 ftl_region_upgrade_completed(dev, lctx, ctx->reg.entry_size, ctx->reg.num_entries, status);
33 }
34
35 static void
v2_upgrade_md_persist_cb(struct spdk_ftl_dev * dev,struct ftl_md * md,int status)36 v2_upgrade_md_persist_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
37 {
38 struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx;
39
40 v2_upgrade_finish(dev, lctx, status);
41 }
42
43 static void
v2_upgrade_md_restore_cb(struct spdk_ftl_dev * dev,struct ftl_md * md,int status)44 v2_upgrade_md_restore_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
45 {
46 struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx;
47 struct upgrade_ctx *ctx = lctx->ctx;
48 struct ftl_band_md *band = ftl_md_get_buffer(md);
49 uint64_t move = sizeof(band->version);
50
51 if (status) {
52 v2_upgrade_finish(dev, lctx, status);
53 return;
54 }
55
56 /* If the upgrade process is interrupted while only part of the update persisted,
57 * then the V1 version will be read from again and this section will rewrite the whole band md.
58 */
59 for (uint64_t i = 0; i < dev->num_bands; i++, band++) {
60 char *buffer = (char *)band;
61
62 memmove(buffer + move, buffer, sizeof(*band) - move);
63 band->version = FTL_BAND_VERSION_2;
64
65 if (band->state != FTL_BAND_STATE_CLOSED && band->state != FTL_BAND_STATE_FREE) {
66 v2_upgrade_finish(dev, lctx, -EINVAL);
67 return;
68 }
69 }
70
71 ctx->md->cb = v2_upgrade_md_persist_cb;
72 ftl_md_set_region(ctx->md, &ctx->reg);
73 ftl_md_persist(ctx->md);
74 }
75
76 static int
v2_upgrade_setup_ctx(struct spdk_ftl_dev * dev,struct ftl_layout_upgrade_ctx * lctx)77 v2_upgrade_setup_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
78 {
79 struct upgrade_ctx *ctx = lctx->ctx;
80 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
81
82 assert(sizeof(struct ftl_band_md) == FTL_BLOCK_SIZE);
83
84 if (lctx->reg->num_entries != dev->num_bands) {
85 return -1;
86 }
87
88 /* Open metadata region */
89 if (md_ops->region_open(dev, lctx->reg->type, FTL_BAND_VERSION_2, sizeof(struct ftl_band_md),
90 dev->num_bands, &ctx->reg)) {
91 return -1;
92 }
93
94 if (lctx->reg->current.blocks != ctx->reg.current.blocks) {
95 return -1;
96 }
97
98 ctx->md = ftl_md_create(dev, lctx->reg->current.blocks, 0, ctx->reg.name, FTL_MD_CREATE_HEAP,
99 lctx->reg);
100 if (!ctx->md) {
101 return -1;
102 }
103
104 ctx->md->owner.cb_ctx = lctx;
105 ctx->md->cb = v2_upgrade_md_restore_cb;
106
107 return 0;
108 }
109
110 static int
v2_upgrade(struct spdk_ftl_dev * dev,struct ftl_layout_upgrade_ctx * lctx)111 v2_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
112 {
113 struct upgrade_ctx *ctx = lctx->ctx;
114
115 if (v2_upgrade_setup_ctx(dev, lctx)) {
116 goto error;
117 }
118 /* At this point we're reading the contents of the v1 md */
119 ftl_md_restore(ctx->md);
120 return 0;
121 error:
122 v2_upgrade_cleanup(lctx);
123 return -1;
124 }
125
126 static int
v1_to_v2_upgrade_enabled(struct spdk_ftl_dev * dev,struct ftl_layout_region * region)127 v1_to_v2_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
128 {
129 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
130
131 if (ftl_region_major_upgrade_enabled(dev, region)) {
132 return -1;
133 }
134
135 /* Create the new band metadata region (v2) up front - this allocates a separate entry in the superblock and
136 * area on the cache for us. This is to reserve space for other region upgrades allocating new regions and it
137 * allows us to do an atomic upgrade of the whole region.
138 *
139 * If the upgrade is stopped by power failure/crash after the V2 region has been added, then the upgrade process
140 * will start again (since V1 still exists), but region_create will fail (since the v2 region has already been
141 * created). In such a case only verification of the region length by region_open is needed.
142 *
143 * Once the upgrade is fully done, the old v1 region entry will be removed from the SB and its area on the cache
144 * freed.
145 */
146 if (md_ops->region_create(dev, region->type, FTL_BAND_VERSION_2, dev->num_bands) &&
147 md_ops->region_open(dev, region->type, FTL_BAND_VERSION_2, sizeof(struct ftl_band_md),
148 dev->num_bands, NULL)) {
149 return -1;
150 }
151
152 return 0;
153 }
154
155 struct ftl_region_upgrade_desc band_upgrade_desc[] = {
156 [FTL_BAND_VERSION_0] = {
157 .verify = ftl_region_upgrade_disabled,
158 },
159 [FTL_BAND_VERSION_1] = {
160 .verify = v1_to_v2_upgrade_enabled,
161 .ctx_size = sizeof(struct upgrade_ctx),
162 .new_version = FTL_BAND_VERSION_2,
163 .upgrade = v2_upgrade,
164 },
165 };
166
167 SPDK_STATIC_ASSERT(SPDK_COUNTOF(band_upgrade_desc) == FTL_BAND_VERSION_CURRENT,
168 "Missing band region upgrade descriptors");
169