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