xref: /spdk/lib/ftl/ftl_sb.c (revision 12fbe739a31b09aff0d05f354d4f3bbef99afc55)
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.h"
8 #include "ftl_core.h"
9 #include "ftl_layout.h"
10 #include "upgrade/ftl_sb_prev.h"
11 
12 bool
13 ftl_superblock_check_magic(struct ftl_superblock *sb)
14 {
15 	if (sb->header.version >= FTL_SB_VERSION_3) {
16 		return sb->header.magic == FTL_SUPERBLOCK_MAGIC;
17 	} else {
18 		return sb->header.magic == FTL_SUPERBLOCK_MAGIC_V2;
19 	}
20 }
21 bool
22 ftl_superblock_md_layout_is_empty(struct ftl_superblock *sb)
23 {
24 	return sb->md_layout_head.type == FTL_LAYOUT_REGION_TYPE_INVALID;
25 }
26 
27 static bool
28 md_region_is_fixed(int reg_type)
29 {
30 	switch (reg_type) {
31 	case FTL_LAYOUT_REGION_TYPE_SB:
32 	case FTL_LAYOUT_REGION_TYPE_SB_BASE:
33 	case FTL_LAYOUT_REGION_TYPE_DATA_BASE:
34 		return true;
35 
36 	default:
37 		return false;
38 	}
39 }
40 
41 static uint32_t
42 md_regions_count(void)
43 {
44 	uint32_t reg_cnt = 0, reg_type;
45 
46 	for (reg_type = 0; reg_type < FTL_LAYOUT_REGION_TYPE_MAX; reg_type++) {
47 		if (md_region_is_fixed(reg_type)) {
48 			continue;
49 		}
50 
51 		reg_cnt++;
52 	}
53 	return reg_cnt;
54 }
55 
56 static bool
57 superblock_md_region_overflow(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg)
58 {
59 	/* sb_reg is part of the sb structure - the pointer should be at a positive offset */
60 	if ((uintptr_t)sb_reg < (uintptr_t)dev->sb) {
61 		return true;
62 	}
63 
64 	/* Make sure the entry doesn't overflow the pointer value (probably overkill to check) */
65 	if (UINT64_MAX - (uintptr_t)sb_reg <= sizeof(*sb_reg)) {
66 		return true;
67 	}
68 
69 	/* There's only a finite (FTL_SUPERBLOCK_SIZE) amount of space in the superblock. Make sure the region wholly fits in that space. */
70 	if ((uintptr_t)(sb_reg + 1) > ((uintptr_t)(dev->sb) + FTL_SUPERBLOCK_SIZE)) {
71 		return true;
72 	}
73 
74 	return false;
75 }
76 
77 static int
78 superblock_md_layout_add(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region *sb_reg,
79 			 uint32_t reg_type, uint32_t reg_version, uint64_t blk_offs, uint64_t blk_sz)
80 {
81 	if (superblock_md_region_overflow(dev, sb_reg)) {
82 		FTL_ERRLOG(dev, "Buffer overflow\n");
83 		return -EOVERFLOW;
84 	}
85 
86 	sb_reg->type = reg_type;
87 	sb_reg->version = reg_version;
88 	sb_reg->blk_offs = blk_offs;
89 	sb_reg->blk_sz = blk_sz;
90 	return 0;
91 }
92 
93 static int
94 superblock_md_layout_add_free(struct spdk_ftl_dev *dev, struct ftl_superblock_md_region **sb_reg,
95 			      uint32_t reg_type, uint32_t free_type, uint64_t total_blocks)
96 {
97 	struct ftl_layout *layout = &dev->layout;
98 	struct ftl_layout_region *reg = &layout->region[reg_type];
99 	uint64_t blks_left = total_blocks - reg->current.offset - reg->current.blocks;
100 
101 	if (blks_left == 0) {
102 		return 0;
103 	}
104 
105 	(*sb_reg)->df_next = ftl_df_get_obj_id(dev->sb, (*sb_reg) + 1);
106 	(*sb_reg) = (*sb_reg) + 1;
107 
108 	if (superblock_md_layout_add(dev, *sb_reg, free_type, 0,
109 				     reg->current.offset + reg->current.blocks, blks_left)) {
110 		return -1;
111 	}
112 
113 	(*sb_reg)->df_next = FTL_DF_OBJ_ID_INVALID;
114 
115 	return 0;
116 }
117 
118 int
119 ftl_superblock_md_layout_build(struct spdk_ftl_dev *dev)
120 {
121 	struct ftl_superblock *sb = dev->sb;
122 	struct ftl_layout *layout = &dev->layout;
123 	struct ftl_layout_region *reg;
124 	int n = 0;
125 	bool is_empty = ftl_superblock_md_layout_is_empty(dev->sb);
126 	struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
127 
128 	/* TODO: major upgrades: add all free regions being tracked
129 	 * For now SB MD layout must be empty - otherwise md free regions may be lost */
130 	assert(is_empty);
131 
132 	for (; n < FTL_LAYOUT_REGION_TYPE_MAX;) {
133 		reg = &layout->region[n];
134 		if (md_region_is_fixed(reg->type)) {
135 			reg->current.sb_md_reg = NULL;
136 			n++;
137 
138 			if (n >= FTL_LAYOUT_REGION_TYPE_MAX) {
139 				/* For VSS emulation the last layout type is a fixed region, we need to move back the list and end the list on previous entry */
140 				sb_reg--;
141 				break;
142 			}
143 			continue;
144 		}
145 
146 		if (superblock_md_layout_add(dev, sb_reg, reg->type, reg->current.version,
147 					     reg->current.offset, reg->current.blocks)) {
148 			return -1;
149 		}
150 		reg->current.sb_md_reg = sb_reg;
151 
152 		n++;
153 		if (n < FTL_LAYOUT_REGION_TYPE_MAX) {
154 			/* next region */
155 			sb_reg->df_next = ftl_df_get_obj_id(sb, sb_reg + 1);
156 			sb_reg++;
157 		}
158 	}
159 
160 	/* terminate the list */
161 	sb_reg->df_next = FTL_DF_OBJ_ID_INVALID;
162 
163 	/* create free_nvc/free_base regions on the first run */
164 	if (is_empty) {
165 		superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_NVC,
166 					      FTL_LAYOUT_REGION_TYPE_FREE_NVC, layout->nvc.total_blocks);
167 
168 		superblock_md_layout_add_free(dev, &sb_reg, FTL_LAYOUT_REGION_LAST_BASE,
169 					      FTL_LAYOUT_REGION_TYPE_FREE_BASE, layout->base.total_blocks);
170 	}
171 
172 	return 0;
173 }
174 
175 int
176 ftl_superblock_md_layout_load_all(struct spdk_ftl_dev *dev)
177 {
178 	struct ftl_superblock *sb = dev->sb;
179 	struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
180 	struct ftl_layout *layout = &dev->layout;
181 	uint32_t regs_found = 0;
182 
183 	for (int n = 0; n < FTL_LAYOUT_REGION_TYPE_MAX; n++) {
184 		if (md_region_is_fixed(sb_reg->type)) {
185 			continue;
186 		}
187 
188 		layout->region[n].current.sb_md_reg = NULL;
189 		layout->region[n].prev = layout->region[n].current;
190 	}
191 
192 	while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
193 		struct ftl_layout_region *reg;
194 
195 		/* TODO: major upgrades: add free regions tracking */
196 		if (sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_NVC ||
197 		    sb_reg->type == FTL_LAYOUT_REGION_TYPE_FREE_BASE) {
198 			goto next_sb_reg;
199 		}
200 
201 		if (sb_reg->type >= FTL_LAYOUT_REGION_TYPE_MAX) {
202 			FTL_ERRLOG(dev, "Invalid MD region type found\n");
203 			return -1;
204 		}
205 
206 		if (md_region_is_fixed(sb_reg->type)) {
207 			FTL_ERRLOG(dev, "Unsupported MD region type found\n");
208 			return -1;
209 		}
210 
211 		reg = &layout->region[sb_reg->type];
212 		if (sb_reg->version == reg->current.version) {
213 			if (reg->current.sb_md_reg) {
214 				FTL_ERRLOG(dev, "Multiple/looping current regions found\n");
215 				return -1;
216 			}
217 
218 			reg->current.offset = sb_reg->blk_offs;
219 			reg->current.blocks = sb_reg->blk_sz;
220 			reg->current.sb_md_reg = sb_reg;
221 
222 			if (!reg->prev.sb_md_reg) {
223 				regs_found++;
224 			}
225 		} else {
226 			if (sb_reg->version > reg->current.version) {
227 				FTL_ERRLOG(dev, "Unknown region version found\n");
228 				return -1;
229 			}
230 			/*
231 			 * The metadata regions are kept as a linked list, it's therefore possible to have it corrupted and contain loops.
232 			 * If the prev region has already been updated to the oldest found version (see following comment/block) and we see
233 			 * the same version again, it's a loop and error.
234 			 */
235 			if (sb_reg->version == reg->prev.version) {
236 				FTL_ERRLOG(dev, "Multiple/looping prev regions found\n");
237 				return -1;
238 			}
239 
240 			/*
241 			 * The following check is in preparation for major metadata upgrade.
242 			 * It's possible that a region will need to be increased in size. It will allocate an additional region in this case
243 			 * and upgrade each entries one by one. If in the middle of this upgrade process a dirty shutdown occurs, there will be
244 			 * multiple entries of the same metadata region in the superblock (old original and partially updated one). This will then
245 			 * effectively rollback the upgrade to the oldest found metadata section and start from the beginning.
246 			 */
247 			if (sb_reg->version < reg->prev.version) {
248 				if ((!reg->current.sb_md_reg) && (!reg->prev.sb_md_reg)) {
249 					regs_found++;
250 				}
251 
252 				reg->prev.offset = sb_reg->blk_offs;
253 				reg->prev.blocks = sb_reg->blk_sz;
254 				reg->prev.version = sb_reg->version;
255 				reg->prev.sb_md_reg = sb_reg;
256 			}
257 		}
258 
259 next_sb_reg:
260 		if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
261 			break;
262 		}
263 
264 		if (UINT64_MAX - (uintptr_t)sb <= sb_reg->df_next) {
265 			FTL_ERRLOG(dev, "Buffer overflow\n");
266 			return -EOVERFLOW;
267 		}
268 
269 		sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
270 		if (superblock_md_region_overflow(dev, sb_reg)) {
271 			FTL_ERRLOG(dev, "Buffer overflow\n");
272 			return -EOVERFLOW;
273 		}
274 	}
275 
276 	if (regs_found != md_regions_count()) {
277 		FTL_ERRLOG(dev, "Missing regions\n");
278 		return -1;
279 	}
280 
281 	return 0;
282 }
283 
284 static void
285 ftl_superblock_md_layout_free_region(struct spdk_ftl_dev *dev,
286 				     struct ftl_superblock_md_region *sb_reg)
287 {
288 	/* TODO: major upgrades: implement */
289 	sb_reg->type = FTL_LAYOUT_REGION_TYPE_FREE_NVC;
290 }
291 
292 int
293 ftl_superblock_md_layout_upgrade_region(struct spdk_ftl_dev *dev,
294 					struct ftl_superblock_md_region *sb_reg, uint32_t new_version)
295 {
296 	struct ftl_superblock *sb = dev->sb;
297 	struct ftl_superblock_md_region *sb_reg_iter = &sb->md_layout_head;
298 	struct ftl_layout *layout = &dev->layout;
299 	struct ftl_layout_region *reg = &layout->region[sb_reg->type];
300 	uint32_t old_version = sb_reg->version;
301 
302 	assert(sb_reg);
303 	assert(reg->prev.sb_md_reg == sb_reg);
304 	assert(new_version > old_version);
305 
306 	while (sb_reg_iter->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
307 		if (sb_reg_iter->type != sb_reg->type) {
308 			goto next_sb_reg_iter;
309 		}
310 
311 		/* Verify all region versions up to new_version are updated: */
312 		if (sb_reg_iter->version != old_version && sb_reg_iter->version < new_version) {
313 			FTL_ERRLOG(dev, "Region upgrade skipped\n");
314 			return -1;
315 		}
316 
317 		if (sb_reg_iter->version == new_version) {
318 			/* Major upgrades: update region prev version to the new version region found */
319 			assert(sb_reg != sb_reg_iter);
320 			reg->prev.offset = sb_reg_iter->blk_offs;
321 			reg->prev.blocks = sb_reg_iter->blk_sz;
322 			reg->prev.version = sb_reg_iter->version;
323 			reg->prev.sb_md_reg = sb_reg_iter;
324 
325 			ftl_superblock_md_layout_free_region(dev, sb_reg);
326 			goto exit;
327 		}
328 
329 next_sb_reg_iter:
330 		if (sb_reg_iter->df_next == FTL_DF_OBJ_ID_INVALID) {
331 			break;
332 		}
333 
334 		sb_reg_iter = ftl_df_get_obj_ptr(sb, sb_reg_iter->df_next);
335 		if (superblock_md_region_overflow(dev, sb_reg_iter)) {
336 			FTL_ERRLOG(dev, "Buffer overflow\n");
337 			return -EOVERFLOW;
338 		}
339 	}
340 
341 	/* Minor upgrades: update the region in place (only the new version) */
342 	assert(sb_reg == reg->prev.sb_md_reg);
343 	sb_reg->version = new_version;
344 	reg->prev.version = new_version;
345 
346 exit:
347 	/* Update the region current version */
348 	if (new_version == reg->current.version) {
349 		reg->current.offset = sb_reg->blk_offs;
350 		reg->current.blocks = sb_reg->blk_sz;
351 		reg->current.version = sb_reg->version;
352 		reg->current.sb_md_reg = sb_reg;
353 	}
354 
355 	return 0;
356 }
357 
358 void
359 ftl_superblock_md_layout_dump(struct spdk_ftl_dev *dev)
360 {
361 	struct ftl_superblock *sb = dev->sb;
362 	struct ftl_superblock_md_region *sb_reg = &sb->md_layout_head;
363 
364 	FTL_NOTICELOG(dev, "SB metadata layout:\n");
365 	while (sb_reg->type != FTL_LAYOUT_REGION_TYPE_INVALID) {
366 		FTL_NOTICELOG(dev,
367 			      "Region df:0x%"PRIx64" type:0x%"PRIx32" ver:%"PRIu32" blk_offs:0x%"PRIx64" blk_sz:0x%"PRIx64"\n",
368 			      ftl_df_get_obj_id(sb, sb_reg), sb_reg->type, sb_reg->version, sb_reg->blk_offs, sb_reg->blk_sz);
369 
370 		if (sb_reg->df_next == FTL_DF_OBJ_ID_INVALID) {
371 			break;
372 		}
373 
374 		sb_reg = ftl_df_get_obj_ptr(sb, sb_reg->df_next);
375 		if (superblock_md_region_overflow(dev, sb_reg)) {
376 			FTL_ERRLOG(dev, "Buffer overflow\n");
377 			return;
378 		}
379 	}
380 }
381