xref: /spdk/lib/ftl/upgrade/ftl_sb_v3.c (revision 8afdeef3becfe9409cc9e7372bd0bc10e8b7d46d)
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_sb_v3.h"
8 #include "ftl_core.h"
9 #include "ftl_layout.h"
10 #include "upgrade/ftl_sb_upgrade.h"
11 
12 bool
13 ftl_superblock_v3_check_magic(union ftl_superblock_ver *sb_ver)
14 {
15 	return sb_ver->header.magic == FTL_SUPERBLOCK_MAGIC;
16 }
17 
18 bool
19 ftl_superblock_v3_md_layout_is_empty(union ftl_superblock_ver *sb_ver)
20 {
21 	return sb_ver->v3.md_layout_head.type == FTL_LAYOUT_REGION_TYPE_INVALID;
22 }
23 
24 static bool
25 md_region_is_fixed(int reg_type)
26 {
27 	switch (reg_type) {
28 	case FTL_LAYOUT_REGION_TYPE_SB:
29 	case FTL_LAYOUT_REGION_TYPE_SB_BASE:
30 	case FTL_LAYOUT_REGION_TYPE_DATA_BASE:
31 		return true;
32 
33 	default:
34 		return false;
35 	}
36 }
37 
38 bool
39 ftl_superblock_v3_md_region_overflow(struct spdk_ftl_dev *dev,
40 				     struct ftl_superblock_v3_md_region *sb_reg)
41 {
42 	/* sb_reg is part of the sb structure - the pointer should be at a positive offset */
43 	if ((uintptr_t)sb_reg < (uintptr_t)dev->sb) {
44 		return true;
45 	}
46 
47 	/* Make sure the entry doesn't overflow the pointer value (probably overkill to check) */
48 	if (UINT64_MAX - (uintptr_t)sb_reg <= sizeof(*sb_reg)) {
49 		return true;
50 	}
51 
52 	/* There's only a finite (FTL_SUPERBLOCK_SIZE) amount of space in the superblock. Make sure the region wholly fits in that space. */
53 	if ((uintptr_t)(sb_reg + 1) > ((uintptr_t)(dev->sb) + FTL_SUPERBLOCK_SIZE)) {
54 		return true;
55 	}
56 
57 	return false;
58 }
59 
60 int
61 ftl_superblock_v3_md_layout_load_all(struct spdk_ftl_dev *dev)
62 {
63 	struct ftl_superblock_v3 *sb = (struct ftl_superblock_v3 *)dev->sb;
64 	struct ftl_superblock_v3_md_region *sb_reg = &sb->md_layout_head;
65 	struct ftl_layout *layout = &dev->layout;
66 	uint32_t regs_found;
67 	uint32_t n;
68 	ftl_df_obj_id df_sentinel = FTL_DF_OBJ_ID_INVALID;
69 	ftl_df_obj_id df_prev = ftl_df_get_obj_id(sb, sb_reg);
70 
71 	for (n = 0; n < FTL_LAYOUT_REGION_TYPE_MAX_V3; n++) {
72 		if (md_region_is_fixed(n)) {
73 			continue;
74 		}
75 		layout->region[n].type = FTL_LAYOUT_REGION_TYPE_INVALID;
76 	}
77 
78 	while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
79 		struct ftl_layout_region *reg;
80 
81 		/* TODO: major upgrades: add free regions tracking */
82 		if (sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_NVC ||
83 		    sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_BASE) {
84 			goto next_sb_reg;
85 		}
86 
87 		if (sb_reg->type >= FTL_LAYOUT_REGION_TYPE_MAX_V3) {
88 			FTL_ERRLOG(dev, "Invalid MD region type found\n");
89 			return -1;
90 		}
91 
92 		if (md_region_is_fixed(sb_reg->type)) {
93 			FTL_ERRLOG(dev, "Unsupported MD region type found\n");
94 			return -1;
95 		}
96 
97 		reg = &layout->region[sb_reg->type];
98 		/* Find the oldest region version */
99 		if (reg->type == FTL_LAYOUT_REGION_TYPE_INVALID || sb_reg->version < reg->current.version) {
100 			reg->type = sb_reg->type;
101 			reg->current.offset = sb_reg->blk_offs;
102 			reg->current.blocks = sb_reg->blk_sz;
103 			reg->current.version = sb_reg->version;
104 		} else if (sb_reg->version == reg->current.version) {
105 			FTL_ERRLOG(dev, "Multiple/looping regions found\n");
106 			return -EAGAIN;
107 		}
108 
109 next_sb_reg:
110 		if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
111 			break;
112 		}
113 
114 		if (UINT64_MAX - (uintptr_t)sb <= sb_reg->df_next) {
115 			FTL_ERRLOG(dev, "Buffer overflow\n");
116 			return -EOVERFLOW;
117 		}
118 
119 		if (sb_reg->df_next <= df_prev) {
120 			df_sentinel = df_prev;
121 		}
122 		df_prev = sb_reg->df_next;
123 
124 		if (df_sentinel != FTL_DF_OBJ_ID_INVALID && sb_reg->df_next == df_sentinel) {
125 			FTL_ERRLOG(dev, "Looping regions found\n");
126 			return -ELOOP;
127 		}
128 
129 		sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
130 		if (ftl_superblock_v3_md_region_overflow(dev, sb_reg)) {
131 			FTL_ERRLOG(dev, "Buffer overflow\n");
132 			return -EOVERFLOW;
133 		}
134 	}
135 
136 	for (regs_found = 0, n = 0; n < FTL_LAYOUT_REGION_TYPE_MAX_V3; n++) {
137 		if (layout->region[n].type == n) {
138 			regs_found++;
139 		}
140 	}
141 
142 	if (regs_found != FTL_LAYOUT_REGION_TYPE_MAX_V3) {
143 		FTL_ERRLOG(dev, "Missing regions\n");
144 		return -1;
145 	}
146 
147 	return 0;
148 }
149 
150 void
151 ftl_superblock_v3_md_layout_dump(struct spdk_ftl_dev *dev)
152 {
153 	struct ftl_superblock_v3 *sb = (struct ftl_superblock_v3 *)dev->sb;
154 	struct ftl_superblock_v3_md_region *sb_reg = &sb->md_layout_head;
155 
156 	FTL_NOTICELOG(dev, "SB metadata layout:\n");
157 	while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
158 		FTL_NOTICELOG(dev,
159 			      "Region df:0x%"PRIx64" type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n",
160 			      ftl_df_get_obj_id(sb, sb_reg), sb_reg->type, sb_reg->version, sb_reg->blk_offs, sb_reg->blk_sz);
161 
162 		if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
163 			break;
164 		}
165 
166 		sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
167 		if (ftl_superblock_v3_md_region_overflow(dev, sb_reg)) {
168 			FTL_ERRLOG(dev, "Buffer overflow\n");
169 			return;
170 		}
171 	}
172 }
173