xref: /spdk/lib/ftl/upgrade/ftl_band_upgrade.c (revision 7c0177b049f1f9eb05ea28cbca2d4353e38909c7)
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
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
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
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
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
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
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
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