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_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 0 : v1_to_v2_upgrade_cleanup(struct ftl_layout_upgrade_ctx *lctx)
18 : {
19 0 : struct upgrade_ctx *ctx = lctx->ctx;
20 :
21 0 : if (ctx->md_v2) {
22 0 : ftl_md_destroy(ctx->md_v2, 0);
23 0 : ctx->md_v2 = NULL;
24 : }
25 0 : }
26 :
27 : static void
28 0 : v1_to_v2_upgrade_finish(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx, int status)
29 : {
30 0 : struct upgrade_ctx *ctx = lctx->ctx;
31 :
32 0 : v1_to_v2_upgrade_cleanup(lctx);
33 0 : ftl_region_upgrade_completed(dev, lctx, ctx->reg_v2.entry_size, ctx->reg_v2.num_entries, status);
34 0 : }
35 :
36 : static void
37 0 : v1_to_v2_upgrade_set(struct ftl_layout_upgrade_ctx *lctx)
38 : {
39 0 : struct upgrade_ctx *ctx = lctx->ctx;
40 0 : 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 0 : for (uint64_t i = 0; i < ctx->reg_v2.current.blocks; i++, md++) {
44 0 : ftl_nv_cache_chunk_md_initialize(md);
45 : }
46 0 : }
47 :
48 : static void
49 0 : v1_to_v2_upgrade_md_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
50 : {
51 0 : struct ftl_layout_upgrade_ctx *lctx = md->owner.cb_ctx;
52 :
53 0 : v1_to_v2_upgrade_finish(dev, lctx, status);
54 0 : }
55 :
56 : static int
57 0 : v1_to_v2_upgrade_setup_ctx(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx,
58 : uint32_t type)
59 : {
60 0 : struct upgrade_ctx *ctx = lctx->ctx;
61 0 : 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 0 : 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 0 : return -1;
69 : }
70 0 : ctx->md_v2 = ftl_md_create(dev, ctx->reg_v2.current.blocks, 0, ctx->reg_v2.name, FTL_MD_CREATE_HEAP,
71 0 : &ctx->reg_v2);
72 0 : if (!ctx->md_v2) {
73 0 : return -1;
74 : }
75 :
76 0 : ctx->md_v2->owner.cb_ctx = lctx;
77 0 : ctx->md_v2->cb = v1_to_v2_upgrade_md_cb;
78 0 : v1_to_v2_upgrade_set(lctx);
79 :
80 0 : return 0;
81 : }
82 :
83 : static int
84 0 : v1_to_v2_upgrade(struct spdk_ftl_dev *dev, struct ftl_layout_upgrade_ctx *lctx)
85 : {
86 0 : 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 0 : if (v1_to_v2_upgrade_setup_ctx(dev, lctx, lctx->reg->type)) {
95 0 : goto error;
96 : }
97 0 : ftl_md_persist(ctx->md_v2);
98 0 : return 0;
99 :
100 0 : error:
101 0 : v1_to_v2_upgrade_cleanup(lctx);
102 0 : return -1;
103 : }
104 :
105 : static int
106 0 : v1_to_v2_upgrade_enabled(struct spdk_ftl_dev *dev, struct ftl_layout_region *region)
107 : {
108 0 : 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 0 : if (ftl_region_major_upgrade_enabled(dev, region)) {
113 0 : 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 0 : if (md_ops->region_create(dev, region->type, FTL_NVC_VERSION_2, dev->layout.nvc.chunk_count) &&
128 0 : 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 0 : return -1;
131 : }
132 :
133 0 : 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");
|