xref: /spdk/lib/ftl/utils/ftl_layout_tracker_bdev.c (revision a5c04e6d852a9f13a1dd2d3cbce4c99e5aaac1d7)
193036282SLukasz Lasek /*   SPDX-License-Identifier: BSD-3-Clause
293036282SLukasz Lasek  *   Copyright 2023 Solidigm All Rights Reserved
393036282SLukasz Lasek  */
493036282SLukasz Lasek 
593036282SLukasz Lasek #include "ftl_layout_tracker_bdev.h"
693036282SLukasz Lasek #include "spdk/util.h"
793036282SLukasz Lasek 
893036282SLukasz Lasek #define REG_VER_ANY	UINT32_MAX
993036282SLukasz Lasek 
1093036282SLukasz Lasek struct layout_tracker_entry {
1193036282SLukasz Lasek 	TAILQ_ENTRY(layout_tracker_entry) layout_entry;
1293036282SLukasz Lasek 	struct ftl_layout_tracker_bdev_region_props reg;
1393036282SLukasz Lasek };
1493036282SLukasz Lasek 
1593036282SLukasz Lasek struct ftl_layout_tracker_bdev {
1693036282SLukasz Lasek 	TAILQ_HEAD(layout_tracker, layout_tracker_entry) layout_head;
1793036282SLukasz Lasek 	uint64_t bdev_blks;
1893036282SLukasz Lasek 	uint32_t regs_cnt;
1993036282SLukasz Lasek };
2093036282SLukasz Lasek 
2177f5fcfaSMateusz Kozlowski struct layout_tracker_blob_entry {
2277f5fcfaSMateusz Kozlowski 	/* Region type */
2377f5fcfaSMateusz Kozlowski 	uint32_t type;
2477f5fcfaSMateusz Kozlowski 
2577f5fcfaSMateusz Kozlowski 	/* Region version */
2677f5fcfaSMateusz Kozlowski 	uint32_t ver;
2777f5fcfaSMateusz Kozlowski 
2877f5fcfaSMateusz Kozlowski 	/* Region offset in blocks */
2977f5fcfaSMateusz Kozlowski 	uint64_t blk_offs;
3077f5fcfaSMateusz Kozlowski 
3177f5fcfaSMateusz Kozlowski 	/* Region size in blocks */
3277f5fcfaSMateusz Kozlowski 	uint64_t blk_sz;
3377f5fcfaSMateusz Kozlowski } __attribute__((packed));
3477f5fcfaSMateusz Kozlowski 
3577f5fcfaSMateusz Kozlowski static int
layout_tracker_init_entries(struct ftl_layout_tracker_bdev * tracker,uint64_t bdev_blks)3677f5fcfaSMateusz Kozlowski layout_tracker_init_entries(struct ftl_layout_tracker_bdev *tracker, uint64_t bdev_blks)
3793036282SLukasz Lasek {
3877f5fcfaSMateusz Kozlowski 	struct layout_tracker_entry *entry_free = calloc(1, sizeof(*entry_free));
3993036282SLukasz Lasek 
4093036282SLukasz Lasek 	if (!entry_free) {
4177f5fcfaSMateusz Kozlowski 		return -ENOMEM;
4293036282SLukasz Lasek 	}
4393036282SLukasz Lasek 
4477f5fcfaSMateusz Kozlowski 	assert(tracker);
4577f5fcfaSMateusz Kozlowski 	assert(tracker->regs_cnt == 0);
4677f5fcfaSMateusz Kozlowski 
4793036282SLukasz Lasek 	tracker->bdev_blks = bdev_blks;
4893036282SLukasz Lasek 	tracker->regs_cnt = 1;
4993036282SLukasz Lasek 	TAILQ_INIT(&tracker->layout_head);
5093036282SLukasz Lasek 
5193036282SLukasz Lasek 	entry_free->reg.blk_sz = bdev_blks;
5293036282SLukasz Lasek 	entry_free->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
5393036282SLukasz Lasek 
5493036282SLukasz Lasek 	TAILQ_INSERT_HEAD(&tracker->layout_head, entry_free, layout_entry);
5577f5fcfaSMateusz Kozlowski 	return 0;
5677f5fcfaSMateusz Kozlowski }
5777f5fcfaSMateusz Kozlowski 
5877f5fcfaSMateusz Kozlowski struct ftl_layout_tracker_bdev *
ftl_layout_tracker_bdev_init(uint64_t bdev_blks)5977f5fcfaSMateusz Kozlowski ftl_layout_tracker_bdev_init(uint64_t bdev_blks)
6077f5fcfaSMateusz Kozlowski {
6177f5fcfaSMateusz Kozlowski 	struct ftl_layout_tracker_bdev *tracker = calloc(1, sizeof(*tracker));
6277f5fcfaSMateusz Kozlowski 
6377f5fcfaSMateusz Kozlowski 	if (!tracker) {
6477f5fcfaSMateusz Kozlowski 		return NULL;
6577f5fcfaSMateusz Kozlowski 	}
6677f5fcfaSMateusz Kozlowski 
6777f5fcfaSMateusz Kozlowski 	if (layout_tracker_init_entries(tracker, bdev_blks)) {
6877f5fcfaSMateusz Kozlowski 		free(tracker);
6977f5fcfaSMateusz Kozlowski 		return NULL;
7077f5fcfaSMateusz Kozlowski 	}
7193036282SLukasz Lasek 
7293036282SLukasz Lasek 	return tracker;
7393036282SLukasz Lasek }
7493036282SLukasz Lasek 
7577f5fcfaSMateusz Kozlowski static void
layout_tracker_free_entries(struct ftl_layout_tracker_bdev * tracker)7677f5fcfaSMateusz Kozlowski layout_tracker_free_entries(struct ftl_layout_tracker_bdev *tracker)
7793036282SLukasz Lasek {
7893036282SLukasz Lasek 	struct layout_tracker_entry *entry;
7993036282SLukasz Lasek 
8093036282SLukasz Lasek 	while ((entry = TAILQ_FIRST(&tracker->layout_head))) {
8193036282SLukasz Lasek 		TAILQ_REMOVE(&tracker->layout_head, entry, layout_entry);
8293036282SLukasz Lasek 		free(entry);
8393036282SLukasz Lasek 	}
8477f5fcfaSMateusz Kozlowski 	tracker->regs_cnt = 0;
8577f5fcfaSMateusz Kozlowski }
8693036282SLukasz Lasek 
8777f5fcfaSMateusz Kozlowski void
ftl_layout_tracker_bdev_fini(struct ftl_layout_tracker_bdev * tracker)8877f5fcfaSMateusz Kozlowski ftl_layout_tracker_bdev_fini(struct ftl_layout_tracker_bdev *tracker)
8977f5fcfaSMateusz Kozlowski {
9077f5fcfaSMateusz Kozlowski 	assert(tracker);
9177f5fcfaSMateusz Kozlowski 	layout_tracker_free_entries(tracker);
9293036282SLukasz Lasek 	free(tracker);
9393036282SLukasz Lasek }
9493036282SLukasz Lasek 
9593036282SLukasz Lasek static struct layout_tracker_entry *
layout_region_find_min_free(struct ftl_layout_tracker_bdev * tracker,uint64_t blk_sz,uint64_t blk_align)9693036282SLukasz Lasek layout_region_find_min_free(struct ftl_layout_tracker_bdev *tracker, uint64_t blk_sz,
9793036282SLukasz Lasek 			    uint64_t blk_align)
9893036282SLukasz Lasek {
9993036282SLukasz Lasek 	struct layout_tracker_entry *min_free_entry = NULL;
10093036282SLukasz Lasek 	struct layout_tracker_entry *entry;
10193036282SLukasz Lasek 
10293036282SLukasz Lasek 	assert(tracker);
10393036282SLukasz Lasek 
10493036282SLukasz Lasek 	TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
10593036282SLukasz Lasek 		uint64_t align_offs, align_sz;
10693036282SLukasz Lasek 
10793036282SLukasz Lasek 		if (entry->reg.type != FTL_LAYOUT_REGION_TYPE_FREE) {
10893036282SLukasz Lasek 			continue;
10993036282SLukasz Lasek 		}
11093036282SLukasz Lasek 
11193036282SLukasz Lasek 		align_offs = entry->reg.blk_offs;
11293036282SLukasz Lasek 		align_sz = entry->reg.blk_sz;
11393036282SLukasz Lasek 		if (blk_align) {
11493036282SLukasz Lasek 			align_offs = SPDK_ALIGN_CEIL(align_offs, blk_align);
11593036282SLukasz Lasek 			align_sz -= (align_offs - entry->reg.blk_offs);
11693036282SLukasz Lasek 		}
11793036282SLukasz Lasek 
11893036282SLukasz Lasek 		if (align_sz >= blk_sz) {
11993036282SLukasz Lasek 			if (!min_free_entry || min_free_entry->reg.blk_sz > entry->reg.blk_sz) {
12093036282SLukasz Lasek 				min_free_entry = entry;
12193036282SLukasz Lasek 			}
12293036282SLukasz Lasek 		}
12393036282SLukasz Lasek 	}
12493036282SLukasz Lasek 
12593036282SLukasz Lasek 	return min_free_entry;
12693036282SLukasz Lasek }
12793036282SLukasz Lasek 
12893036282SLukasz Lasek static struct layout_tracker_entry *
layout_region_find_from(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver,struct layout_tracker_entry * entry)12993036282SLukasz Lasek layout_region_find_from(struct ftl_layout_tracker_bdev *tracker,
13093036282SLukasz Lasek 			enum ftl_layout_region_type reg_type,
13193036282SLukasz Lasek 			uint32_t reg_ver, struct layout_tracker_entry *entry)
13293036282SLukasz Lasek {
13393036282SLukasz Lasek 	assert(tracker);
13493036282SLukasz Lasek 
13593036282SLukasz Lasek 	TAILQ_FOREACH_FROM(entry, &tracker->layout_head, layout_entry) {
13693036282SLukasz Lasek 		if ((entry->reg.type == reg_type || reg_type == FTL_LAYOUT_REGION_TYPE_INVALID)
13793036282SLukasz Lasek 		    && (entry->reg.ver == reg_ver || reg_ver == REG_VER_ANY)) {
13893036282SLukasz Lasek 			return entry;
13993036282SLukasz Lasek 		}
14093036282SLukasz Lasek 	}
14193036282SLukasz Lasek 
14293036282SLukasz Lasek 	return NULL;
14393036282SLukasz Lasek }
14493036282SLukasz Lasek 
14593036282SLukasz Lasek static struct layout_tracker_entry *
layout_region_find_first(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver)14693036282SLukasz Lasek layout_region_find_first(struct ftl_layout_tracker_bdev *tracker,
14793036282SLukasz Lasek 			 enum ftl_layout_region_type reg_type,
14893036282SLukasz Lasek 			 uint32_t reg_ver)
14993036282SLukasz Lasek {
15093036282SLukasz Lasek 	return layout_region_find_from(tracker, reg_type, reg_ver, TAILQ_FIRST(&tracker->layout_head));
15193036282SLukasz Lasek }
15293036282SLukasz Lasek 
15393036282SLukasz Lasek static struct layout_tracker_entry *
layout_region_find_next(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver,struct layout_tracker_entry * entry)15493036282SLukasz Lasek layout_region_find_next(struct ftl_layout_tracker_bdev *tracker,
15593036282SLukasz Lasek 			enum ftl_layout_region_type reg_type,
15693036282SLukasz Lasek 			uint32_t reg_ver, struct layout_tracker_entry *entry)
15793036282SLukasz Lasek {
15893036282SLukasz Lasek 	if ((entry = TAILQ_NEXT(entry, layout_entry))) {
15993036282SLukasz Lasek 		return layout_region_find_from(tracker, reg_type, reg_ver, entry);
16093036282SLukasz Lasek 	}
16193036282SLukasz Lasek 	return NULL;
16293036282SLukasz Lasek }
16393036282SLukasz Lasek 
16493036282SLukasz Lasek const struct ftl_layout_tracker_bdev_region_props *
ftl_layout_tracker_bdev_add_region(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver,uint64_t blk_sz,uint64_t blk_align)16593036282SLukasz Lasek ftl_layout_tracker_bdev_add_region(struct ftl_layout_tracker_bdev *tracker,
16693036282SLukasz Lasek 				   enum ftl_layout_region_type reg_type, uint32_t reg_ver, uint64_t blk_sz, uint64_t blk_align)
16793036282SLukasz Lasek {
16893036282SLukasz Lasek 	struct layout_tracker_entry *entry_free;
16993036282SLukasz Lasek 	struct layout_tracker_entry *entry_new;
17093036282SLukasz Lasek 	uint64_t entry_free_blks_left;
17193036282SLukasz Lasek 
17293036282SLukasz Lasek 	assert(tracker);
17393036282SLukasz Lasek 	assert(reg_type < FTL_LAYOUT_REGION_TYPE_MAX);
17493036282SLukasz Lasek 
17593036282SLukasz Lasek 	entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
17693036282SLukasz Lasek 	if (entry_new) {
17793036282SLukasz Lasek 		/* Region already exists */
17893036282SLukasz Lasek 		return NULL;
17993036282SLukasz Lasek 	}
18093036282SLukasz Lasek 
18193036282SLukasz Lasek 	entry_free = layout_region_find_min_free(tracker, blk_sz, blk_align);
18293036282SLukasz Lasek 	if (!entry_free) {
18393036282SLukasz Lasek 		/* No free space */
18493036282SLukasz Lasek 		return NULL;
18593036282SLukasz Lasek 	}
18693036282SLukasz Lasek 
18793036282SLukasz Lasek 	/* Takce care of the alignment */
18893036282SLukasz Lasek 	if (blk_align) {
18993036282SLukasz Lasek 		/* Calculate the aligned region's offs and size */
19093036282SLukasz Lasek 		uint64_t align_offs = SPDK_ALIGN_CEIL(entry_free->reg.blk_offs, blk_align);
19193036282SLukasz Lasek 		assert(align_offs >= entry_free->reg.blk_offs);
19293036282SLukasz Lasek 
19393036282SLukasz Lasek 		/* Subdivide the free region in two: unaligned free region, followed by the aligned free region */
19493036282SLukasz Lasek 		if (align_offs > entry_free->reg.blk_offs) {
19593036282SLukasz Lasek 			uint64_t unaligned_sz = align_offs - entry_free->reg.blk_offs;
19693036282SLukasz Lasek 
19793036282SLukasz Lasek 			/* Setup the unaligned region */
19893036282SLukasz Lasek 			entry_new = calloc(1, sizeof(*entry_new));
19993036282SLukasz Lasek 			if (!entry_new) {
20093036282SLukasz Lasek 				return NULL;
20193036282SLukasz Lasek 			}
20293036282SLukasz Lasek 			entry_new->reg = entry_free->reg;
20393036282SLukasz Lasek 			entry_new->reg.blk_sz = unaligned_sz;
20493036282SLukasz Lasek 
20593036282SLukasz Lasek 			/* Setup the aligned region - shrink the free region found */
20693036282SLukasz Lasek 			entry_free->reg.blk_offs = align_offs;
20793036282SLukasz Lasek 			entry_free->reg.blk_sz -= unaligned_sz;
20893036282SLukasz Lasek 
20993036282SLukasz Lasek 			/* Add the unaligned region prev to the aligned one */
21093036282SLukasz Lasek 			TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
21193036282SLukasz Lasek 			tracker->regs_cnt++;
21293036282SLukasz Lasek 		}
21393036282SLukasz Lasek 	}
21493036282SLukasz Lasek 
21593036282SLukasz Lasek 	entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
21693036282SLukasz Lasek 
21793036282SLukasz Lasek 	if (entry_free_blks_left) {
21893036282SLukasz Lasek 		/* Subdivide the free region */
21993036282SLukasz Lasek 		entry_new = calloc(1, sizeof(*entry_new));
22093036282SLukasz Lasek 		if (!entry_new) {
22193036282SLukasz Lasek 			return NULL;
22293036282SLukasz Lasek 		}
22393036282SLukasz Lasek 
22493036282SLukasz Lasek 		/* Setup the new region at the beginning of the free region found */
22593036282SLukasz Lasek 		entry_new->reg.type = reg_type;
22693036282SLukasz Lasek 		entry_new->reg.ver = reg_ver;
22793036282SLukasz Lasek 		entry_new->reg.blk_offs = entry_free->reg.blk_offs;
22893036282SLukasz Lasek 		entry_new->reg.blk_sz = blk_sz;
22993036282SLukasz Lasek 
23093036282SLukasz Lasek 		/* Shrink the free region found */
23193036282SLukasz Lasek 		entry_free->reg.blk_offs += blk_sz;
23293036282SLukasz Lasek 		entry_free->reg.blk_sz = entry_free_blks_left;
23393036282SLukasz Lasek 
23493036282SLukasz Lasek 		/* Add the new region */
23593036282SLukasz Lasek 		TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
23693036282SLukasz Lasek 		tracker->regs_cnt++;
23793036282SLukasz Lasek 	} else {
23893036282SLukasz Lasek 		/* Setup the new region in place */
23993036282SLukasz Lasek 		entry_new = entry_free;
24093036282SLukasz Lasek 		entry_new->reg.type = reg_type;
24193036282SLukasz Lasek 		entry_new->reg.ver = reg_ver;
24293036282SLukasz Lasek 	}
24393036282SLukasz Lasek 
24493036282SLukasz Lasek 	return &entry_new->reg;
24593036282SLukasz Lasek }
24693036282SLukasz Lasek 
24777f5fcfaSMateusz Kozlowski const struct ftl_layout_tracker_bdev_region_props *
ftl_layout_tracker_bdev_insert_region(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver,uint64_t blk_offs,uint64_t blk_sz)24877f5fcfaSMateusz Kozlowski ftl_layout_tracker_bdev_insert_region(struct ftl_layout_tracker_bdev *tracker,
24977f5fcfaSMateusz Kozlowski 				      enum ftl_layout_region_type reg_type, uint32_t reg_ver,
25077f5fcfaSMateusz Kozlowski 				      uint64_t blk_offs, uint64_t blk_sz)
25177f5fcfaSMateusz Kozlowski {
25277f5fcfaSMateusz Kozlowski 	struct layout_tracker_entry *entry_free;
25377f5fcfaSMateusz Kozlowski 	struct layout_tracker_entry *entry_new;
25477f5fcfaSMateusz Kozlowski 	uint64_t entry_free_blks_left;
25577f5fcfaSMateusz Kozlowski 
25677f5fcfaSMateusz Kozlowski 	assert(tracker);
25777f5fcfaSMateusz Kozlowski 
25877f5fcfaSMateusz Kozlowski 	if (reg_type >= FTL_LAYOUT_REGION_TYPE_MAX && reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
25977f5fcfaSMateusz Kozlowski 		/* Invalid region type */
26077f5fcfaSMateusz Kozlowski 		return NULL;
26177f5fcfaSMateusz Kozlowski 	}
26277f5fcfaSMateusz Kozlowski 
263*a5c04e6dSMateusz Kozlowski 	if (reg_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
26477f5fcfaSMateusz Kozlowski 		entry_new = layout_region_find_first(tracker, reg_type, reg_ver);
26577f5fcfaSMateusz Kozlowski 		if (entry_new) {
26677f5fcfaSMateusz Kozlowski 			/* Region already exists */
26777f5fcfaSMateusz Kozlowski 			return NULL;
26877f5fcfaSMateusz Kozlowski 		}
269*a5c04e6dSMateusz Kozlowski 	}
27077f5fcfaSMateusz Kozlowski 
27177f5fcfaSMateusz Kozlowski 	/* Look up for the free region corresponding to the blk_offs */
27277f5fcfaSMateusz Kozlowski 	for (entry_free = layout_region_find_first(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY);
27377f5fcfaSMateusz Kozlowski 	     entry_free;
27477f5fcfaSMateusz Kozlowski 	     entry_free = layout_region_find_next(tracker, FTL_LAYOUT_REGION_TYPE_FREE, REG_VER_ANY,
27577f5fcfaSMateusz Kozlowski 			     entry_free)) {
27677f5fcfaSMateusz Kozlowski 		/* Test if the region being added fits into the free region */
27777f5fcfaSMateusz Kozlowski 		if (entry_free->reg.blk_offs <= blk_offs
27877f5fcfaSMateusz Kozlowski 		    && blk_offs + blk_sz <= entry_free->reg.blk_offs + entry_free->reg.blk_sz) {
27977f5fcfaSMateusz Kozlowski 			break;
28077f5fcfaSMateusz Kozlowski 		}
28177f5fcfaSMateusz Kozlowski 	}
28277f5fcfaSMateusz Kozlowski 
28377f5fcfaSMateusz Kozlowski 	if (!entry_free) {
28477f5fcfaSMateusz Kozlowski 		/* Did not found the corresponding free region */
28577f5fcfaSMateusz Kozlowski 		return NULL;
28677f5fcfaSMateusz Kozlowski 	}
28777f5fcfaSMateusz Kozlowski 
288*a5c04e6dSMateusz Kozlowski 	if (reg_type == FTL_LAYOUT_REGION_TYPE_INVALID) {
289*a5c04e6dSMateusz Kozlowski 		/* Dry run */
290*a5c04e6dSMateusz Kozlowski 		return &entry_free->reg;
291*a5c04e6dSMateusz Kozlowski 	}
292*a5c04e6dSMateusz Kozlowski 
29377f5fcfaSMateusz Kozlowski 	entry_free_blks_left = blk_offs - entry_free->reg.blk_offs;
29477f5fcfaSMateusz Kozlowski 	if (entry_free_blks_left) {
29577f5fcfaSMateusz Kozlowski 		/* Subdivide the free region */
29677f5fcfaSMateusz Kozlowski 		entry_new = calloc(1, sizeof(*entry_new));
29777f5fcfaSMateusz Kozlowski 		if (!entry_new) {
29877f5fcfaSMateusz Kozlowski 			return NULL;
29977f5fcfaSMateusz Kozlowski 		}
30077f5fcfaSMateusz Kozlowski 
30177f5fcfaSMateusz Kozlowski 		/* Setup another free region at the beginning of the free region found */
30277f5fcfaSMateusz Kozlowski 		entry_new->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
30377f5fcfaSMateusz Kozlowski 		entry_new->reg.blk_offs = entry_free->reg.blk_offs;
30477f5fcfaSMateusz Kozlowski 		entry_new->reg.blk_sz = entry_free_blks_left;
30577f5fcfaSMateusz Kozlowski 
30677f5fcfaSMateusz Kozlowski 		/* Shrink the free region found */
30777f5fcfaSMateusz Kozlowski 		entry_free->reg.blk_offs += entry_free_blks_left;
30877f5fcfaSMateusz Kozlowski 		assert(entry_free->reg.blk_sz > entry_free_blks_left);
30977f5fcfaSMateusz Kozlowski 		entry_free->reg.blk_sz -= entry_free_blks_left;
31077f5fcfaSMateusz Kozlowski 
31177f5fcfaSMateusz Kozlowski 		/* Add the new free region */
31277f5fcfaSMateusz Kozlowski 		TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
31377f5fcfaSMateusz Kozlowski 		tracker->regs_cnt++;
31477f5fcfaSMateusz Kozlowski 	}
31577f5fcfaSMateusz Kozlowski 
31677f5fcfaSMateusz Kozlowski 	assert(entry_free->reg.blk_offs == blk_offs);
31777f5fcfaSMateusz Kozlowski 	assert(blk_sz <= entry_free->reg.blk_sz);
31877f5fcfaSMateusz Kozlowski 
31977f5fcfaSMateusz Kozlowski 	entry_free_blks_left = entry_free->reg.blk_sz - blk_sz;
32077f5fcfaSMateusz Kozlowski 	if (entry_free_blks_left) {
32177f5fcfaSMateusz Kozlowski 		/* Subdivide the free region */
32277f5fcfaSMateusz Kozlowski 		entry_new = calloc(1, sizeof(*entry_new));
32377f5fcfaSMateusz Kozlowski 		if (!entry_new) {
32477f5fcfaSMateusz Kozlowski 			return NULL;
32577f5fcfaSMateusz Kozlowski 		}
32677f5fcfaSMateusz Kozlowski 
32777f5fcfaSMateusz Kozlowski 		/* Setup the new region at the beginning of the free region found */
32877f5fcfaSMateusz Kozlowski 		entry_new->reg.type = reg_type;
32977f5fcfaSMateusz Kozlowski 		entry_new->reg.ver = reg_ver;
33077f5fcfaSMateusz Kozlowski 		entry_new->reg.blk_offs = entry_free->reg.blk_offs;
33177f5fcfaSMateusz Kozlowski 		entry_new->reg.blk_sz = blk_sz;
33277f5fcfaSMateusz Kozlowski 
33377f5fcfaSMateusz Kozlowski 		/* Shrink the free region found */
33477f5fcfaSMateusz Kozlowski 		entry_free->reg.blk_offs += blk_sz;
33577f5fcfaSMateusz Kozlowski 		entry_free->reg.blk_sz = entry_free_blks_left;
33677f5fcfaSMateusz Kozlowski 
33777f5fcfaSMateusz Kozlowski 		/* Add the new region */
33877f5fcfaSMateusz Kozlowski 		TAILQ_INSERT_BEFORE(entry_free, entry_new, layout_entry);
33977f5fcfaSMateusz Kozlowski 		tracker->regs_cnt++;
34077f5fcfaSMateusz Kozlowski 	} else {
34177f5fcfaSMateusz Kozlowski 		/* Setup the new region in place */
34277f5fcfaSMateusz Kozlowski 		entry_new = entry_free;
34377f5fcfaSMateusz Kozlowski 		entry_new->reg.type = reg_type;
34477f5fcfaSMateusz Kozlowski 		entry_new->reg.ver = reg_ver;
34577f5fcfaSMateusz Kozlowski 	}
34677f5fcfaSMateusz Kozlowski 
34777f5fcfaSMateusz Kozlowski 	return &entry_new->reg;
34877f5fcfaSMateusz Kozlowski }
34977f5fcfaSMateusz Kozlowski 
35093036282SLukasz Lasek int
ftl_layout_tracker_bdev_rm_region(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,uint32_t reg_ver)35193036282SLukasz Lasek ftl_layout_tracker_bdev_rm_region(struct ftl_layout_tracker_bdev *tracker,
35293036282SLukasz Lasek 				  enum ftl_layout_region_type reg_type, uint32_t reg_ver)
35393036282SLukasz Lasek {
35493036282SLukasz Lasek 	struct layout_tracker_entry *entry_rm, *entry_check __attribute__((unused));
35593036282SLukasz Lasek 	struct layout_tracker_entry *entry = layout_region_find_first(tracker, reg_type, reg_ver);
35693036282SLukasz Lasek 
35793036282SLukasz Lasek 	if (!entry) {
35893036282SLukasz Lasek 		return -1;
35993036282SLukasz Lasek 	}
36093036282SLukasz Lasek 
36193036282SLukasz Lasek 	/* Free the region */
36293036282SLukasz Lasek 	entry->reg.type = FTL_LAYOUT_REGION_TYPE_FREE;
36393036282SLukasz Lasek 	entry->reg.ver = 0;
36493036282SLukasz Lasek 
36593036282SLukasz Lasek 	/* Join with the adjacent free region prev to the current region */
36693036282SLukasz Lasek 	entry_rm = TAILQ_PREV(entry, layout_tracker, layout_entry);
36793036282SLukasz Lasek 	if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
36893036282SLukasz Lasek 		TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
36993036282SLukasz Lasek 		entry->reg.blk_offs = entry_rm->reg.blk_offs;
37093036282SLukasz Lasek 		entry->reg.blk_sz += entry_rm->reg.blk_sz;
37193036282SLukasz Lasek 
37293036282SLukasz Lasek #if defined(DEBUG)
37393036282SLukasz Lasek 		entry_check = TAILQ_PREV(entry, layout_tracker, layout_entry);
37493036282SLukasz Lasek 		if (entry_check) {
37593036282SLukasz Lasek 			assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
37693036282SLukasz Lasek 		}
37793036282SLukasz Lasek #endif
37893036282SLukasz Lasek 
37993036282SLukasz Lasek 		free(entry_rm);
38093036282SLukasz Lasek 		tracker->regs_cnt--;
38193036282SLukasz Lasek 	}
38293036282SLukasz Lasek 
38393036282SLukasz Lasek 	/* Join with the adjacent free region next to the current region */
38493036282SLukasz Lasek 	entry_rm = TAILQ_NEXT(entry, layout_entry);
38593036282SLukasz Lasek 	if (entry_rm && entry_rm->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
38693036282SLukasz Lasek 		TAILQ_REMOVE(&tracker->layout_head, entry_rm, layout_entry);
38793036282SLukasz Lasek 		entry->reg.blk_sz += entry_rm->reg.blk_sz;
38893036282SLukasz Lasek 
38993036282SLukasz Lasek #if defined(DEBUG)
39093036282SLukasz Lasek 		entry_check = TAILQ_NEXT(entry, layout_entry);
39193036282SLukasz Lasek 		if (entry_check) {
39293036282SLukasz Lasek 			assert(entry_check->reg.type != FTL_LAYOUT_REGION_TYPE_FREE);
39393036282SLukasz Lasek 		}
39493036282SLukasz Lasek #endif
39593036282SLukasz Lasek 		free(entry_rm);
39693036282SLukasz Lasek 		tracker->regs_cnt--;
39793036282SLukasz Lasek 	}
39893036282SLukasz Lasek 
39993036282SLukasz Lasek 	return 0;
40093036282SLukasz Lasek }
40193036282SLukasz Lasek 
40293036282SLukasz Lasek void
ftl_layout_tracker_bdev_find_next_region(struct ftl_layout_tracker_bdev * tracker,enum ftl_layout_region_type reg_type,const struct ftl_layout_tracker_bdev_region_props ** search_ctx)40393036282SLukasz Lasek ftl_layout_tracker_bdev_find_next_region(struct ftl_layout_tracker_bdev *tracker,
40493036282SLukasz Lasek 		enum ftl_layout_region_type reg_type,
40593036282SLukasz Lasek 		const struct ftl_layout_tracker_bdev_region_props **search_ctx)
40693036282SLukasz Lasek {
40793036282SLukasz Lasek 	struct layout_tracker_entry *entry;
40893036282SLukasz Lasek 
40993036282SLukasz Lasek 	if (!search_ctx) {
41093036282SLukasz Lasek 		return;
41193036282SLukasz Lasek 	}
41293036282SLukasz Lasek 
41393036282SLukasz Lasek 	if (*search_ctx == NULL) {
41493036282SLukasz Lasek 		/* Return the first region found */
41593036282SLukasz Lasek 		entry = layout_region_find_first(tracker, reg_type, REG_VER_ANY);
41693036282SLukasz Lasek 	} else {
41793036282SLukasz Lasek 		/* Find the next region */
41893036282SLukasz Lasek 		entry = SPDK_CONTAINEROF(*search_ctx, struct layout_tracker_entry, reg);
41993036282SLukasz Lasek 		entry = layout_region_find_next(tracker, reg_type, REG_VER_ANY, entry);
42093036282SLukasz Lasek 	}
42193036282SLukasz Lasek 	*search_ctx = entry ? &entry->reg : NULL;
42293036282SLukasz Lasek }
42377f5fcfaSMateusz Kozlowski 
42477f5fcfaSMateusz Kozlowski size_t
ftl_layout_tracker_bdev_blob_store(struct ftl_layout_tracker_bdev * tracker,void * blob_buf,size_t blob_buf_sz)42577f5fcfaSMateusz Kozlowski ftl_layout_tracker_bdev_blob_store(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
42677f5fcfaSMateusz Kozlowski 				   size_t blob_buf_sz)
42777f5fcfaSMateusz Kozlowski {
42877f5fcfaSMateusz Kozlowski 	struct layout_tracker_blob_entry *blob_entry = blob_buf;
42977f5fcfaSMateusz Kozlowski 	struct layout_tracker_entry *entry;
43077f5fcfaSMateusz Kozlowski 	size_t blob_sz = 0;
43177f5fcfaSMateusz Kozlowski 
43277f5fcfaSMateusz Kozlowski 	assert(tracker);
43377f5fcfaSMateusz Kozlowski 
43477f5fcfaSMateusz Kozlowski 	TAILQ_FOREACH(entry, &tracker->layout_head, layout_entry) {
43577f5fcfaSMateusz Kozlowski 		if (blob_sz + sizeof(*blob_entry) > blob_buf_sz) {
43677f5fcfaSMateusz Kozlowski 			/* Ran out of buf space */
43777f5fcfaSMateusz Kozlowski 			assert(false);
43877f5fcfaSMateusz Kozlowski 			return 0;
43977f5fcfaSMateusz Kozlowski 		}
44077f5fcfaSMateusz Kozlowski 
44177f5fcfaSMateusz Kozlowski 		/* Skip the free space entries */
44277f5fcfaSMateusz Kozlowski 		if (entry->reg.type == FTL_LAYOUT_REGION_TYPE_FREE) {
44377f5fcfaSMateusz Kozlowski 			continue;
44477f5fcfaSMateusz Kozlowski 		}
44577f5fcfaSMateusz Kozlowski 
44677f5fcfaSMateusz Kozlowski 		/* Store the entry */
44777f5fcfaSMateusz Kozlowski 		blob_entry->type = entry->reg.type;
44877f5fcfaSMateusz Kozlowski 		blob_entry->ver = entry->reg.ver;
44977f5fcfaSMateusz Kozlowski 		blob_entry->blk_offs = entry->reg.blk_offs;
45077f5fcfaSMateusz Kozlowski 		blob_entry->blk_sz = entry->reg.blk_sz;
45177f5fcfaSMateusz Kozlowski 
45277f5fcfaSMateusz Kozlowski 		/* Move to the next entry */
45377f5fcfaSMateusz Kozlowski 		blob_entry++;
45477f5fcfaSMateusz Kozlowski 		blob_sz += sizeof(*blob_entry);
45577f5fcfaSMateusz Kozlowski 	}
45677f5fcfaSMateusz Kozlowski 
45777f5fcfaSMateusz Kozlowski 	return blob_sz;
45877f5fcfaSMateusz Kozlowski }
45977f5fcfaSMateusz Kozlowski 
46077f5fcfaSMateusz Kozlowski int
ftl_layout_tracker_bdev_blob_load(struct ftl_layout_tracker_bdev * tracker,void * blob_buf,size_t blob_sz)46177f5fcfaSMateusz Kozlowski ftl_layout_tracker_bdev_blob_load(struct ftl_layout_tracker_bdev *tracker, void *blob_buf,
46277f5fcfaSMateusz Kozlowski 				  size_t blob_sz)
46377f5fcfaSMateusz Kozlowski {
46477f5fcfaSMateusz Kozlowski 	struct layout_tracker_blob_entry *blob_entry = blob_buf;
46577f5fcfaSMateusz Kozlowski 	size_t blob_entry_num = blob_sz / sizeof(*blob_entry);
46677f5fcfaSMateusz Kozlowski 	struct layout_tracker_blob_entry *blob_entry_end = blob_entry + blob_entry_num;
46777f5fcfaSMateusz Kozlowski 
46877f5fcfaSMateusz Kozlowski 	if (blob_sz % sizeof(*blob_entry) != 0) {
46977f5fcfaSMateusz Kozlowski 		/* Invalid blob size */
47077f5fcfaSMateusz Kozlowski 		return -1;
47177f5fcfaSMateusz Kozlowski 	}
47277f5fcfaSMateusz Kozlowski 
47377f5fcfaSMateusz Kozlowski 	/* Free the current MD layout tracking info */
47477f5fcfaSMateusz Kozlowski 	layout_tracker_free_entries(tracker);
47577f5fcfaSMateusz Kozlowski 
47677f5fcfaSMateusz Kozlowski 	/* Reinit MD layout tracking info */
47777f5fcfaSMateusz Kozlowski 	if (layout_tracker_init_entries(tracker, tracker->bdev_blks)) {
47877f5fcfaSMateusz Kozlowski 		return -1;
47977f5fcfaSMateusz Kozlowski 	}
48077f5fcfaSMateusz Kozlowski 
48177f5fcfaSMateusz Kozlowski 	for (; blob_entry < blob_entry_end; blob_entry++) {
48277f5fcfaSMateusz Kozlowski 		/* Verify the type */
48377f5fcfaSMateusz Kozlowski 		if (blob_entry->type == FTL_LAYOUT_REGION_TYPE_FREE) {
48477f5fcfaSMateusz Kozlowski 			return -1;
48577f5fcfaSMateusz Kozlowski 		}
48677f5fcfaSMateusz Kozlowski 
48777f5fcfaSMateusz Kozlowski 		/* Load the entry */
48877f5fcfaSMateusz Kozlowski 		if (!ftl_layout_tracker_bdev_insert_region(tracker, blob_entry->type, blob_entry->ver,
48977f5fcfaSMateusz Kozlowski 				blob_entry->blk_offs, blob_entry->blk_sz)) {
49077f5fcfaSMateusz Kozlowski 			return -1;
49177f5fcfaSMateusz Kozlowski 		}
49277f5fcfaSMateusz Kozlowski 	}
49377f5fcfaSMateusz Kozlowski 
49477f5fcfaSMateusz Kozlowski 	return 0;
49577f5fcfaSMateusz Kozlowski }
496