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