Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright 2023 Solidigm All Rights Reserved
3 : */
4 :
5 : #include "ftl_layout_tracker_bdev.h"
6 : #include "spdk/util.h"
7 :
8 : #define REG_VER_ANY UINT32_MAX
9 :
10 : struct layout_tracker_entry {
11 : TAILQ_ENTRY(layout_tracker_entry) layout_entry;
12 : struct ftl_layout_tracker_bdev_region_props reg;
13 : };
14 :
15 : struct ftl_layout_tracker_bdev {
16 : TAILQ_HEAD(layout_tracker, layout_tracker_entry) layout_head;
17 : uint64_t bdev_blks;
18 : uint32_t regs_cnt;
19 : };
20 :
21 : struct layout_tracker_blob_entry {
22 : /* Region type */
23 : uint32_t type;
24 :
25 : /* Region version */
26 : uint32_t ver;
27 :
28 : /* Region offset in blocks */
29 : uint64_t blk_offs;
30 :
31 : /* Region size in blocks */
32 : uint64_t blk_sz;
33 : } __attribute__((packed));
34 :
35 : static int
36 24 : layout_tracker_init_entries(struct ftl_layout_tracker_bdev *tracker, uint64_t bdev_blks)
37 : {
38 24 : struct layout_tracker_entry *entry_free = calloc(1, sizeof(*entry_free));
39 :
40 24 : if (!entry_free) {
41 0 : return -ENOMEM;
42 : }
43 :
44 24 : assert(tracker);
45 24 : assert(tracker->regs_cnt == 0);
46 :
47 24 : tracker->bdev_blks = bdev_blks;
48 24 : tracker->regs_cnt = 1;
49 24 : TAILQ_INIT(&tracker->layout_head);
50 :
51 24 : entry_free->reg.blk_sz = bdev_blks;
52 24 : entry_free->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
53 :
54 24 : TAILQ_INSERT_HEAD(&tracker->layout_head, entry_free, layout_entry);
55 24 : return 0;
56 : }
57 :
58 : struct ftl_layout_tracker_bdev *
59 4 : ftl_layout_tracker_bdev_init(uint64_t bdev_blks)
60 : {
61 4 : struct ftl_layout_tracker_bdev *tracker = calloc(1, sizeof(*tracker));
62 :
63 4 : if (!tracker) {
64 0 : return NULL;
65 : }
66 :
67 4 : if (layout_tracker_init_entries(tracker, bdev_blks)) {
68 0 : free(tracker);
69 0 : return NULL;
70 : }
71 :
72 4 : return tracker;
73 : }
74 :
75 : static void
76 24 : layout_tracker_free_entries(struct ftl_layout_tracker_bdev *tracker)
77 : {
78 : struct layout_tracker_entry *entry;
79 :
80 266 : while ((entry = TAILQ_FIRST(&tracker->layout_head))) {
81 242 : TAILQ_REMOVE(&tracker->layout_head, entry, layout_entry);
82 242 : free(entry);
83 : }
84 24 : tracker->regs_cnt = 0;
85 24 : }
86 :
87 : void
88 4 : ftl_layout_tracker_bdev_fini(struct ftl_layout_tracker_bdev *tracker)
89 : {
90 4 : assert(tracker);
91 4 : layout_tracker_free_entries(tracker);
92 4 : free(tracker);
93 4 : }
94 :
95 : static struct layout_tracker_entry *
96 36 : layout_region_find_min_free(struct ftl_layout_tracker_bdev *tracker, uint64_t blk_sz,
97 : uint64_t blk_align)
98 : {
99 36 : struct layout_tracker_entry *min_free_entry = NULL;
100 : struct layout_tracker_entry *entry;
101 :
102 36 : assert(tracker);
103 :
104 288 : TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
105 : uint64_t align_offs, align_sz;
106 :
107 252 : if (entry->reg.type != FTL_LAYOUT_REGION_TYPE_FREE) {
108 216 : continue;
109 : }
110 :
111 36 : align_offs = entry->reg.blk_offs;
112 36 : align_sz = entry->reg.blk_sz;
113 36 : if (blk_align) {
114 36 : align_offs = SPDK_ALIGN_CEIL(align_offs, blk_align);
115 36 : align_sz -= (align_offs - entry->reg.blk_offs);
116 : }
117 :
118 36 : if (align_sz >= blk_sz) {
119 36 : if (!min_free_entry || min_free_entry->reg.blk_sz > entry->reg.blk_sz) {
120 36 : min_free_entry = entry;
121 : }
122 : }
123 : }
124 :
125 36 : return min_free_entry;
126 : }
127 :
128 : static struct layout_tracker_entry *
129 458 : layout_region_find_from(struct ftl_layout_tracker_bdev *tracker,
130 : enum ftl_layout_region_type reg_type,
131 : uint32_t reg_ver, struct layout_tracker_entry *entry)
132 : {
133 458 : assert(tracker);
134 :
135 3618 : TAILQ_FOREACH_FROM(entry, &tracker->layout_head, layout_entry) {
136 3371 : if ((entry->reg.type == reg_type || reg_type == FTL_LAYOUT_REGION_TYPE_INVALID)
137 218 : && (entry->reg.ver == reg_ver || reg_ver == REG_VER_ANY)) {
138 211 : return entry;
139 : }
140 : }
141 :
142 247 : return NULL;
143 : }
144 :
145 : static struct layout_tracker_entry *
146 436 : layout_region_find_first(struct ftl_layout_tracker_bdev *tracker,
147 : enum ftl_layout_region_type reg_type,
148 : uint32_t reg_ver)
149 : {
150 436 : return layout_region_find_from(tracker, reg_type, reg_ver, TAILQ_FIRST(&tracker->layout_head));
151 : }
152 :
153 : static struct layout_tracker_entry *
154 24 : layout_region_find_next(struct ftl_layout_tracker_bdev *tracker,
155 : enum ftl_layout_region_type reg_type,
156 : uint32_t reg_ver, struct layout_tracker_entry *entry)
157 : {
158 24 : if ((entry = TAILQ_NEXT(entry, layout_entry))) {
159 22 : return layout_region_find_from(tracker, reg_type, reg_ver, entry);
160 : }
161 2 : return NULL;
162 : }
163 :
164 : const struct ftl_layout_tracker_bdev_region_props *
165 36 : ftl_layout_tracker_bdev_add_region(struct ftl_layout_tracker_bdev *tracker,
166 : enum ftl_layout_region_type reg_type, uint32_t reg_ver, uint64_t blk_sz, uint64_t blk_align)
167 : {
168 : struct layout_tracker_entry *entry_free;
169 : struct layout_tracker_entry *entry_new;
170 : uint64_t entry_free_blks_left;
171 :
172 36 : assert(tracker);
173 36 : assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
174 :
175 36 : entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
176 36 : if (entry_new) {
177 : /* Region already exists */
178 0 : return NULL;
179 : }
180 :
181 36 : entry_free = layout_region_find_min_free(tracker, blk_sz, blk_align);
182 36 : if (!entry_free) {
183 : /* No free space */
184 0 : return NULL;
185 : }
186 :
187 : /* Takce care of the alignment */
188 36 : if (blk_align) {
189 : /* Calculate the aligned region's offs and size */
190 36 : uint64_t align_offs = SPDK_ALIGN_CEIL(entry_free->reg.blk_offs, blk_align);
191 36 : assert(align_offs >= entry_free->reg.blk_offs);
192 :
193 : /* Subdivide the free region in two: unaligned free region, followed by the aligned free region */
194 36 : if (align_offs > entry_free->reg.blk_offs) {
195 0 : uint64_t unaligned_sz = align_offs - entry_free->reg.blk_offs;
196 :
197 : /* Setup the unaligned region */
198 0 : entry_new = calloc(1, sizeof(*entry_new));
199 0 : if (!entry_new) {
200 0 : return NULL;
201 : }
202 0 : entry_new->reg = entry_free->reg;
203 0 : entry_new->reg.blk_sz = unaligned_sz;
204 :
205 : /* Setup the aligned region - shrink the free region found */
206 0 : entry_free->reg.blk_offs = align_offs;
207 0 : entry_free->reg.blk_sz -= unaligned_sz;
208 :
209 : /* Add the unaligned region prev to the aligned one */
210 0 : TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
211 0 : tracker->regs_cnt++;
212 : }
213 : }
214 :
215 36 : entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
216 :
217 36 : if (entry_free_blks_left) {
218 : /* Subdivide the free region */
219 36 : entry_new = calloc(1, sizeof(*entry_new));
220 36 : if (!entry_new) {
221 0 : return NULL;
222 : }
223 :
224 : /* Setup the new region at the beginning of the free region found */
225 36 : entry_new->reg.type = reg_type;
226 36 : entry_new->reg.ver = reg_ver;
227 36 : entry_new->reg.blk_offs = entry_free->reg.blk_offs;
228 36 : entry_new->reg.blk_sz = blk_sz;
229 :
230 : /* Shrink the free region found */
231 36 : entry_free->reg.blk_offs += blk_sz;
232 36 : entry_free->reg.blk_sz = entry_free_blks_left;
233 :
234 : /* Add the new region */
235 36 : TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
236 36 : tracker->regs_cnt++;
237 : } else {
238 : /* Setup the new region in place */
239 0 : entry_new = entry_free;
240 0 : entry_new->reg.type = reg_type;
241 0 : entry_new->reg.ver = reg_ver;
242 : }
243 :
244 36 : return &entry_new->reg;
245 : }
246 :
247 : const struct ftl_layout_tracker_bdev_region_props *
248 184 : ftl_layout_tracker_bdev_insert_region(struct ftl_layout_tracker_bdev *tracker,
249 : enum ftl_layout_region_type reg_type, uint32_t reg_ver,
250 : uint64_t blk_offs, uint64_t blk_sz)
251 : {
252 : struct layout_tracker_entry *entry_free;
253 : struct layout_tracker_entry *entry_new;
254 : uint64_t entry_free_blks_left;
255 :
256 184 : assert(tracker);
257 :
258 184 : if (reg_type >= FTL_LAYOUT_REGION_TYPE_MAX && reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
259 : /* Invalid region type */
260 2 : return NULL;
261 : }
262 :
263 182 : if (reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
264 182 : entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
265 182 : if (entry_new) {
266 : /* Region already exists */
267 1 : return NULL;
268 : }
269 : }
270 :
271 : /* Look up for the free region corresponding to the blk_offs */
272 184 : for (entry_free = layout_region_find_first(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY);
273 : entry_free;
274 3 : entry_free = layout_region_find_next(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY,
275 : entry_free)) {
276 : /* Test if the region being added fits into the free region */
277 182 : if (entry_free->reg.blk_offs <= blk_offs
278 180 : && blk_offs + blk_sz <= entry_free->reg.blk_offs + entry_free->reg.blk_sz) {
279 179 : break;
280 : }
281 : }
282 :
283 181 : if (!entry_free) {
284 : /* Did not found the corresponding free region */
285 2 : return NULL;
286 : }
287 :
288 179 : if (reg_type == FTL_LAYOUT_REGION_TYPE_INVALID) {
289 : /* Dry run */
290 0 : return &entry_free->reg;
291 : }
292 :
293 179 : entry_free_blks_left = blk_offs - entry_free->reg.blk_offs;
294 179 : if (entry_free_blks_left) {
295 : /* Subdivide the free region */
296 6 : entry_new = calloc(1, sizeof(*entry_new));
297 6 : if (!entry_new) {
298 0 : return NULL;
299 : }
300 :
301 : /* Setup another free region at the beginning of the free region found */
302 6 : entry_new->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
303 6 : entry_new->reg.blk_offs = entry_free->reg.blk_offs;
304 6 : entry_new->reg.blk_sz = entry_free_blks_left;
305 :
306 : /* Shrink the free region found */
307 6 : entry_free->reg.blk_offs += entry_free_blks_left;
308 6 : assert(entry_free->reg.blk_sz > entry_free_blks_left);
309 6 : entry_free->reg.blk_sz -= entry_free_blks_left;
310 :
311 : /* Add the new free region */
312 6 : TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
313 6 : tracker->regs_cnt++;
314 : }
315 :
316 179 : assert(entry_free->reg.blk_offs == blk_offs);
317 179 : assert(blk_sz <= entry_free->reg.blk_sz);
318 :
319 179 : entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
320 179 : if (entry_free_blks_left) {
321 : /* Subdivide the free region */
322 178 : entry_new = calloc(1, sizeof(*entry_new));
323 178 : if (!entry_new) {
324 0 : return NULL;
325 : }
326 :
327 : /* Setup the new region at the beginning of the free region found */
328 178 : entry_new->reg.type = reg_type;
329 178 : entry_new->reg.ver = reg_ver;
330 178 : entry_new->reg.blk_offs = entry_free->reg.blk_offs;
331 178 : entry_new->reg.blk_sz = blk_sz;
332 :
333 : /* Shrink the free region found */
334 178 : entry_free->reg.blk_offs += blk_sz;
335 178 : entry_free->reg.blk_sz = entry_free_blks_left;
336 :
337 : /* Add the new region */
338 178 : TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
339 178 : tracker->regs_cnt++;
340 : } else {
341 : /* Setup the new region in place */
342 1 : entry_new = entry_free;
343 1 : entry_new->reg.type = reg_type;
344 1 : entry_new->reg.ver = reg_ver;
345 : }
346 :
347 179 : return &entry_new->reg;
348 : }
349 :
350 : int
351 3 : ftl_layout_tracker_bdev_rm_region(struct ftl_layout_tracker_bdev *tracker,
352 : enum ftl_layout_region_type reg_type, uint32_t reg_ver)
353 : {
354 : struct layout_tracker_entry *entry_rm, *entry_check __attribute__((unused));
355 3 : struct layout_tracker_entry *entry = layout_region_find_first(tracker, reg_type, reg_ver);
356 :
357 3 : if (!entry) {
358 0 : return -1;
359 : }
360 :
361 : /* Free the region */
362 3 : entry->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
363 3 : entry->reg.ver = 0;
364 :
365 : /* Join with the adjacent free region prev to the current region */
366 3 : entry_rm = TAILQ_PREV(entry, layout_tracker, layout_entry);
367 3 : if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
368 1 : TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
369 1 : entry->reg.blk_offs = entry_rm->reg.blk_offs;
370 1 : entry->reg.blk_sz += entry_rm->reg.blk_sz;
371 :
372 : #if defined(DEBUG)
373 1 : entry_check = TAILQ_PREV(entry, layout_tracker, layout_entry);
374 1 : if (entry_check) {
375 1 : assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
376 : }
377 : #endif
378 :
379 1 : free(entry_rm);
380 1 : tracker->regs_cnt--;
381 : }
382 :
383 : /* Join with the adjacent free region next to the current region */
384 3 : entry_rm = TAILQ_NEXT(entry, layout_entry);
385 3 : if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
386 1 : TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
387 1 : entry->reg.blk_sz += entry_rm->reg.blk_sz;
388 :
389 : #if defined(DEBUG)
390 1 : entry_check = TAILQ_NEXT(entry, layout_entry);
391 1 : if (entry_check) {
392 0 : assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
393 : }
394 : #endif
395 1 : free(entry_rm);
396 1 : tracker->regs_cnt--;
397 : }
398 :
399 3 : return 0;
400 : }
401 :
402 : void
403 55 : ftl_layout_tracker_bdev_find_next_region(struct ftl_layout_tracker_bdev *tracker,
404 : enum ftl_layout_region_type reg_type,
405 : const struct ftl_layout_tracker_bdev_region_props **search_ctx)
406 : {
407 : struct layout_tracker_entry *entry;
408 :
409 55 : if (!search_ctx) {
410 0 : return;
411 : }
412 :
413 55 : if (*search_ctx == NULL) {
414 : /* Return the first region found */
415 34 : entry = layout_region_find_first(tracker, reg_type, REG_VER_ANY);
416 : } else {
417 : /* Find the next region */
418 21 : entry = SPDK_CONTAINEROF(*search_ctx, struct layout_tracker_entry, reg);
419 21 : entry = layout_region_find_next(tracker, reg_type, REG_VER_ANY, entry);
420 : }
421 55 : *search_ctx = entry ? &entry->reg : NULL;
422 : }
423 :
424 : size_t
425 4 : ftl_layout_tracker_bdev_blob_store(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
426 : size_t blob_buf_sz)
427 : {
428 4 : struct layout_tracker_blob_entry *blob_entry = blob_buf;
429 : struct layout_tracker_entry *entry;
430 4 : size_t blob_sz = 0;
431 :
432 4 : assert(tracker);
433 :
434 44 : TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
435 40 : if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
436 : /* Ran out of buf space */
437 0 : assert(false);
438 : return 0;
439 : }
440 :
441 : /* Skip the free space entries */
442 40 : if (entry->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
443 4 : continue;
444 : }
445 :
446 : /* Store the entry */
447 36 : blob_entry->type = entry->reg.type;
448 36 : blob_entry->ver = entry->reg.ver;
449 36 : blob_entry->blk_offs = entry->reg.blk_offs;
450 36 : blob_entry->blk_sz = entry->reg.blk_sz;
451 :
452 : /* Move to the next entry */
453 36 : blob_entry++;
454 36 : blob_sz += sizeof(*blob_entry);
455 : }
456 :
457 4 : return blob_sz;
458 : }
459 :
460 : int
461 20 : ftl_layout_tracker_bdev_blob_load(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
462 : size_t blob_sz)
463 : {
464 20 : struct layout_tracker_blob_entry *blob_entry = blob_buf;
465 20 : size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
466 20 : struct layout_tracker_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
467 :
468 20 : if (blob_sz % sizeof(*blob_entry) != 0) {
469 : /* Invalid blob size */
470 0 : return -1;
471 : }
472 :
473 : /* Free the current MD layout tracking info */
474 20 : layout_tracker_free_entries(tracker);
475 :
476 : /* Reinit MD layout tracking info */
477 20 : if (layout_tracker_init_entries(tracker, tracker->bdev_blks)) {
478 0 : return -1;
479 : }
480 :
481 197 : for (; blob_entry < blob_entry_end; blob_entry++) {
482 : /* Verify the type */
483 182 : if (blob_entry->type == FTL_LAYOUT_REGION_TYPE_FREE) {
484 0 : return -1;
485 : }
486 :
487 : /* Load the entry */
488 182 : if (!ftl_layout_tracker_bdev_insert_region(tracker, blob_entry->type, blob_entry->ver,
489 : blob_entry->blk_offs, blob_entry->blk_sz)) {
490 5 : return -1;
491 : }
492 : }
493 :
494 15 : return 0;
495 : }
|