1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2022 Intel Corporation.
3 * All rights reserved.
4 */
5
6 #include "mngt/ftl_mngt.h"
7 #include "mngt/ftl_mngt_steps.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_cb(struct spdk_ftl_dev * dev,struct ftl_md * md,int status)36 v2_upgrade_md_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 int
v2_upgrade_setup_ctx(struct spdk_ftl_dev * dev,struct ftl_layout_upgrade_ctx * lctx,uint32_t type)44 v2_upgrade_setup_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, uint32_t type)
45 {
46 struct upgrade_ctx *ctx = lctx->ctx;
47 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
48
49 /* TODO Add validation if no open bands */
50
51 /* Open metadata region */
52 if (md_ops->region_open(dev, lctx->reg->type, FTL_P2L_VERSION_2,
53 sizeof(struct ftl_p2l_ckpt_page_no_vss),
54 dev->layout.p2l.ckpt_pages, &ctx->reg)) {
55 return -1;
56 }
57
58 ctx->md = ftl_md_create(dev, ctx->reg.current.blocks, 0, ctx->reg.name, FTL_MD_CREATE_HEAP,
59 &ctx->reg);
60 if (!ctx->md) {
61 return -1;
62 }
63
64 ctx->md->owner.cb_ctx = lctx;
65 ctx->md->cb = v2_upgrade_md_cb;
66
67 return 0;
68 }
69
70 static int
v2_upgrade(struct spdk_ftl_dev * dev,struct ftl_layout_upgrade_ctx * lctx)71 v2_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
72 {
73 struct upgrade_ctx *ctx = lctx->ctx;
74
75 if (v2_upgrade_setup_ctx(dev, lctx, lctx->reg->type)) {
76 goto error;
77 }
78 ftl_md_clear(ctx->md, 0, NULL);
79 return 0;
80 error:
81 v2_upgrade_cleanup(lctx);
82 return -1;
83 }
84
85 static int
v1_to_v2_upgrade_enabled(struct spdk_ftl_dev * dev,struct ftl_layout_region * region)86 v1_to_v2_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
87 {
88 const struct ftl_md_layout_ops *md_ops = &dev->nv_cache.nvc_type->ops.md_layout_ops;
89
90 if (ftl_region_major_upgrade_enabled(dev, region)) {
91 return -1;
92 }
93
94 /* Create the new P2L metadata region (v2) up front - this allocates a separate entry in the superblock and
95 * area on the cache for us. This is to reserve space for other region upgrades allocating new regions and it
96 * allows us to do an atomic upgrade of the whole region.
97 *
98 * If the upgrade is stopped by power failure/crash after the V2 region has been added, then the upgrade process
99 * will start again (since V1 still exists), but region_create will fail (since the v2 region has already been
100 * created). In such a case only verification of the region length by region_open is needed.
101 *
102 * Once the upgrade is fully done, the old v1 region entry will be removed from the SB and its area on the cache
103 * freed.
104 */
105 if (md_ops->region_create(dev, region->type, FTL_P2L_VERSION_2, dev->layout.p2l.ckpt_pages) &&
106 md_ops->region_open(dev, region->type, FTL_P2L_VERSION_2, sizeof(struct ftl_p2l_ckpt_page_no_vss),
107 dev->layout.p2l.ckpt_pages, NULL)) {
108 return -1;
109 }
110
111 return 0;
112 }
113
114 struct ftl_region_upgrade_desc p2l_upgrade_desc[] = {
115 [FTL_P2L_VERSION_0] = {
116 .verify = ftl_region_upgrade_disabled
117 },
118 [FTL_P2L_VERSION_1] = {
119 .verify = v1_to_v2_upgrade_enabled,
120 .ctx_size = sizeof(struct upgrade_ctx),
121 .new_version = FTL_P2L_VERSION_2,
122 .upgrade = v2_upgrade,
123 },
124 };
125
126 SPDK_STATIC_ASSERT(SPDK_COUNTOF(p2l_upgrade_desc) == FTL_P2L_VERSION_CURRENT,
127 "Missing P2L region upgrade descriptors");
128