Line data Source code
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 0 : v2_upgrade_cleanup(struct ftl_layout_upgrade_ctx *lctx)
17 : {
18 0 : struct upgrade_ctx *ctx = lctx->ctx;
19 :
20 0 : if (ctx->md) {
21 0 : ftl_md_destroy(ctx->md, 0);
22 0 : ctx->md = NULL;
23 : }
24 0 : }
25 :
26 : static void
27 0 : v2_upgrade_finish(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, int status)
28 : {
29 0 : struct upgrade_ctx *ctx = lctx->ctx;
30 :
31 0 : v2_upgrade_cleanup(lctx);
32 0 : ftl_region_upgrade_completed(dev, lctx, ctx->reg.entry_size, ctx->reg.num_entries, status);
33 0 : }
34 :
35 : static void
36 0 : v2_upgrade_md_persist_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
37 : {
38 0 : struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx;
39 :
40 0 : v2_upgrade_finish(dev, lctx, status);
41 0 : }
42 :
43 : static void
44 0 : v2_upgrade_md_restore_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
45 : {
46 0 : struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx;
47 0 : struct upgrade_ctx *ctx = lctx->ctx;
48 0 : struct ftl_band_md *band = ftl_md_get_buffer(md);
49 0 : uint64_t move = sizeof(band->version);
50 :
51 0 : if (status) {
52 0 : v2_upgrade_finish(dev, lctx, status);
53 0 : 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 0 : for (uint64_t i = 0; i < dev->num_bands; i++, band++) {
60 0 : char *buffer = (char *)band;
61 :
62 0 : memmove(buffer + move, buffer, sizeof(*band) - move);
63 0 : band->version = FTL_BAND_VERSION_2;
64 :
65 0 : if (band->state != FTL_BAND_STATE_CLOSED && band->state != FTL_BAND_STATE_FREE) {
66 0 : v2_upgrade_finish(dev, lctx, -EINVAL);
67 0 : return;
68 : }
69 : }
70 :
71 0 : ctx->md->cb = v2_upgrade_md_persist_cb;
72 0 : ftl_md_set_region(ctx->md, &ctx->reg);
73 0 : ftl_md_persist(ctx->md);
74 : }
75 :
76 : static int
77 0 : v2_upgrade_setup_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
78 : {
79 0 : struct upgrade_ctx *ctx = lctx->ctx;
80 0 : 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 0 : if (lctx->reg->num_entries != dev->num_bands) {
85 0 : return -1;
86 : }
87 :
88 : /* Open metadata region */
89 0 : 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 0 : return -1;
92 : }
93 :
94 0 : if (lctx->reg->current.blocks != ctx->reg.current.blocks) {
95 0 : return -1;
96 : }
97 :
98 0 : ctx->md = ftl_md_create(dev, lctx->reg->current.blocks, 0, ctx->reg.name, FTL_MD_CREATE_HEAP,
99 0 : lctx->reg);
100 0 : if (!ctx->md) {
101 0 : return -1;
102 : }
103 :
104 0 : ctx->md->owner.cb_ctx = lctx;
105 0 : ctx->md->cb = v2_upgrade_md_restore_cb;
106 :
107 0 : return 0;
108 : }
109 :
110 : static int
111 0 : v2_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
112 : {
113 0 : struct upgrade_ctx *ctx = lctx->ctx;
114 :
115 0 : if (v2_upgrade_setup_ctx(dev, lctx)) {
116 0 : goto error;
117 : }
118 : /* At this point we're reading the contents of the v1 md */
119 0 : ftl_md_restore(ctx->md);
120 0 : return 0;
121 0 : error:
122 0 : v2_upgrade_cleanup(lctx);
123 0 : return -1;
124 : }
125 :
126 : static int
127 0 : v1_to_v2_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
128 : {
129 0 : const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
130 :
131 0 : if (ftl_region_major_upgrade_enabled(dev, region)) {
132 0 : 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 0 : if (md_ops->region_create(dev, region->type, FTL_BAND_VERSION_2, dev->num_bands) &&
147 0 : md_ops->region_open(dev, region->type, FTL_BAND_VERSION_2, sizeof(struct ftl_band_md),
148 : dev->num_bands, NULL)) {
149 0 : return -1;
150 : }
151 :
152 0 : 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");
|