xref: /spdk/lib/ftl/utils/ftl_md.c (revision 186b109dd3a723612e3df79bb3d97699173d39e3)
17a7ac2afSArtur Paszkiewicz /*   SPDX-License-Identifier: BSD-3-Clause
217cf101bSMateusz Kozlowski  *   Copyright 2023 Solidigm All Rights Reserved
3a6dbe372Spaul luse  *   Copyright (C) 2022 Intel Corporation.
47a7ac2afSArtur Paszkiewicz  *   All rights reserved.
57a7ac2afSArtur Paszkiewicz  */
67a7ac2afSArtur Paszkiewicz 
77a7ac2afSArtur Paszkiewicz #include "spdk/env.h"
87a7ac2afSArtur Paszkiewicz #include "spdk/bdev_module.h"
97a7ac2afSArtur Paszkiewicz 
107a7ac2afSArtur Paszkiewicz #include "ftl_core.h"
117a7ac2afSArtur Paszkiewicz #include "ftl_md.h"
12d6795254SArtur Paszkiewicz #include "ftl_nv_cache_io.h"
137a7ac2afSArtur Paszkiewicz 
147a7ac2afSArtur Paszkiewicz struct ftl_md;
157a7ac2afSArtur Paszkiewicz static void io_submit(struct ftl_md *md);
167a7ac2afSArtur Paszkiewicz static void io_done(struct ftl_md *md);
177a7ac2afSArtur Paszkiewicz 
187a7ac2afSArtur Paszkiewicz static bool
197a7ac2afSArtur Paszkiewicz has_mirror(struct ftl_md *md)
207a7ac2afSArtur Paszkiewicz {
217a7ac2afSArtur Paszkiewicz 	if (md->region) {
227a7ac2afSArtur Paszkiewicz 		if (md->region->mirror_type != FTL_LAYOUT_REGION_TYPE_INVALID) {
237a7ac2afSArtur Paszkiewicz 			return md->mirror_enabled;
247a7ac2afSArtur Paszkiewicz 		}
257a7ac2afSArtur Paszkiewicz 	}
267a7ac2afSArtur Paszkiewicz 
277a7ac2afSArtur Paszkiewicz 	return false;
287a7ac2afSArtur Paszkiewicz }
297a7ac2afSArtur Paszkiewicz 
30522a0c82SLukasz Lasek static struct ftl_md *
31522a0c82SLukasz Lasek ftl_md_get_mirror(struct ftl_md *md)
327a7ac2afSArtur Paszkiewicz {
33522a0c82SLukasz Lasek 	if (has_mirror(md)) {
34522a0c82SLukasz Lasek 		return md->dev->layout.md[md->region->mirror_type];
357a7ac2afSArtur Paszkiewicz 	}
367a7ac2afSArtur Paszkiewicz 
37522a0c82SLukasz Lasek 	return NULL;
387a7ac2afSArtur Paszkiewicz }
397a7ac2afSArtur Paszkiewicz 
407a7ac2afSArtur Paszkiewicz uint64_t
417a7ac2afSArtur Paszkiewicz ftl_md_xfer_blocks(struct spdk_ftl_dev *dev)
427a7ac2afSArtur Paszkiewicz {
437a7ac2afSArtur Paszkiewicz 	return 4ULL * dev->xfer_size;
447a7ac2afSArtur Paszkiewicz }
457a7ac2afSArtur Paszkiewicz 
467a7ac2afSArtur Paszkiewicz static uint64_t
477a7ac2afSArtur Paszkiewicz xfer_size(struct ftl_md *md)
487a7ac2afSArtur Paszkiewicz {
497a7ac2afSArtur Paszkiewicz 	return ftl_md_xfer_blocks(md->dev) * FTL_BLOCK_SIZE;
507a7ac2afSArtur Paszkiewicz }
517a7ac2afSArtur Paszkiewicz 
52818b9c05SArtur Paszkiewicz static void
53a0fb555bSMateusz Kozlowski ftl_md_create_spdk_buf(struct ftl_md *md, uint64_t vss_blksz)
54a0fb555bSMateusz Kozlowski {
55a0fb555bSMateusz Kozlowski 	md->shm_fd = -1;
56a0fb555bSMateusz Kozlowski 	md->vss_data = NULL;
57a0fb555bSMateusz Kozlowski 	md->data = spdk_zmalloc(md->data_blocks * (FTL_BLOCK_SIZE + vss_blksz), FTL_BLOCK_SIZE, NULL,
58*186b109dSJim Harris 				SPDK_ENV_NUMA_ID_ANY, SPDK_MALLOC_DMA);
59a0fb555bSMateusz Kozlowski 
60a0fb555bSMateusz Kozlowski 	if (md->data && vss_blksz) {
61a0fb555bSMateusz Kozlowski 		md->vss_data = ((char *)md->data) + md->data_blocks * FTL_BLOCK_SIZE;
62a0fb555bSMateusz Kozlowski 	}
63a0fb555bSMateusz Kozlowski }
64a0fb555bSMateusz Kozlowski 
65a0fb555bSMateusz Kozlowski static void
66818b9c05SArtur Paszkiewicz ftl_md_create_heap(struct ftl_md *md, uint64_t vss_blksz)
67818b9c05SArtur Paszkiewicz {
68818b9c05SArtur Paszkiewicz 	md->shm_fd = -1;
69818b9c05SArtur Paszkiewicz 	md->vss_data = NULL;
70818b9c05SArtur Paszkiewicz 	md->data = calloc(md->data_blocks, FTL_BLOCK_SIZE + vss_blksz);
71818b9c05SArtur Paszkiewicz 
72818b9c05SArtur Paszkiewicz 	if (md->data && vss_blksz) {
73818b9c05SArtur Paszkiewicz 		md->vss_data = ((char *)md->data) + md->data_blocks * FTL_BLOCK_SIZE;
74818b9c05SArtur Paszkiewicz 	}
75818b9c05SArtur Paszkiewicz }
76818b9c05SArtur Paszkiewicz 
77818b9c05SArtur Paszkiewicz static void
78a0fb555bSMateusz Kozlowski ftl_md_destroy_spdk_buf(struct ftl_md *md)
79a0fb555bSMateusz Kozlowski {
80a0fb555bSMateusz Kozlowski 	if (md->data) {
81a0fb555bSMateusz Kozlowski 		spdk_free(md->data);
82a0fb555bSMateusz Kozlowski 		md->data = NULL;
83a0fb555bSMateusz Kozlowski 		md->vss_data = NULL;
84a0fb555bSMateusz Kozlowski 	}
85a0fb555bSMateusz Kozlowski }
86a0fb555bSMateusz Kozlowski 
87a0fb555bSMateusz Kozlowski static void
88818b9c05SArtur Paszkiewicz ftl_md_destroy_heap(struct ftl_md *md)
89818b9c05SArtur Paszkiewicz {
90818b9c05SArtur Paszkiewicz 	if (md->data) {
91818b9c05SArtur Paszkiewicz 		free(md->data);
92818b9c05SArtur Paszkiewicz 		md->data = NULL;
93818b9c05SArtur Paszkiewicz 		md->vss_data = NULL;
94818b9c05SArtur Paszkiewicz 	}
95818b9c05SArtur Paszkiewicz }
96818b9c05SArtur Paszkiewicz 
97818b9c05SArtur Paszkiewicz static int
98818b9c05SArtur Paszkiewicz ftl_wrapper_open(const char *name, int of, mode_t m)
99818b9c05SArtur Paszkiewicz {
100818b9c05SArtur Paszkiewicz 	return open(name, of, m);
101818b9c05SArtur Paszkiewicz }
102818b9c05SArtur Paszkiewicz 
103818b9c05SArtur Paszkiewicz static void
104818b9c05SArtur Paszkiewicz ftl_md_setup_obj(struct ftl_md *md, int flags,
105818b9c05SArtur Paszkiewicz 		 const char *name)
106818b9c05SArtur Paszkiewicz {
107818b9c05SArtur Paszkiewicz 	char uuid_str[SPDK_UUID_STRING_LEN];
108818b9c05SArtur Paszkiewicz 	const char *fmt;
109818b9c05SArtur Paszkiewicz 
110818b9c05SArtur Paszkiewicz 	if (!(flags & FTL_MD_CREATE_SHM)) {
111818b9c05SArtur Paszkiewicz 		assert(false);
112818b9c05SArtur Paszkiewicz 		return;
113818b9c05SArtur Paszkiewicz 	}
114818b9c05SArtur Paszkiewicz 
115818b9c05SArtur Paszkiewicz 	/* TODO: temporary, define a proper hugetlbfs mountpoint */
116818b9c05SArtur Paszkiewicz 	fmt = "/dev/hugepages/ftl_%s_%s";
117818b9c05SArtur Paszkiewicz 	md->shm_mmap_flags = MAP_SHARED;
118818b9c05SArtur Paszkiewicz 	md->shm_open = ftl_wrapper_open;
119818b9c05SArtur Paszkiewicz 	md->shm_unlink = unlink;
120818b9c05SArtur Paszkiewicz 
121818b9c05SArtur Paszkiewicz 	if (name == NULL ||
122818b9c05SArtur Paszkiewicz 	    spdk_uuid_fmt_lower(uuid_str, SPDK_UUID_STRING_LEN, &md->dev->conf.uuid) ||
123818b9c05SArtur Paszkiewicz 	    snprintf(md->name, sizeof(md->name) / sizeof(md->name[0]),
124818b9c05SArtur Paszkiewicz 		     fmt, uuid_str, name) <= 0) {
125818b9c05SArtur Paszkiewicz 		md->name[0] = 0;
126818b9c05SArtur Paszkiewicz 	}
127818b9c05SArtur Paszkiewicz }
128818b9c05SArtur Paszkiewicz 
129818b9c05SArtur Paszkiewicz static void
130f45c0075SArtur Paszkiewicz ftl_md_invalidate_shm(struct ftl_md *md)
131f45c0075SArtur Paszkiewicz {
132f45c0075SArtur Paszkiewicz 	if (md->dev->sb_shm && md->dev->sb_shm->shm_ready) {
133f45c0075SArtur Paszkiewicz 		md->dev->init_retry = true;
134f45c0075SArtur Paszkiewicz 		md->dev->sb_shm->shm_ready = false;
135f45c0075SArtur Paszkiewicz 	}
136f45c0075SArtur Paszkiewicz }
137f45c0075SArtur Paszkiewicz 
138f45c0075SArtur Paszkiewicz static void
139818b9c05SArtur Paszkiewicz ftl_md_create_shm(struct ftl_md *md, uint64_t vss_blksz, int flags)
140818b9c05SArtur Paszkiewicz {
141818b9c05SArtur Paszkiewicz 	struct stat shm_stat;
142818b9c05SArtur Paszkiewicz 	size_t vss_blk_offs;
143818b9c05SArtur Paszkiewicz 	void *shm_ptr;
144818b9c05SArtur Paszkiewicz 	int open_flags = O_RDWR;
145818b9c05SArtur Paszkiewicz 	mode_t open_mode = S_IRUSR | S_IWUSR;
146818b9c05SArtur Paszkiewicz 
147818b9c05SArtur Paszkiewicz 	assert(md->shm_open && md->shm_unlink);
148818b9c05SArtur Paszkiewicz 	md->data = NULL;
149818b9c05SArtur Paszkiewicz 	md->vss_data = NULL;
150818b9c05SArtur Paszkiewicz 	md->shm_sz = 0;
151818b9c05SArtur Paszkiewicz 
152818b9c05SArtur Paszkiewicz 	/* Must have an object name */
153818b9c05SArtur Paszkiewicz 	if (md->name[0] == 0) {
154818b9c05SArtur Paszkiewicz 		assert(false);
155818b9c05SArtur Paszkiewicz 		return;
156818b9c05SArtur Paszkiewicz 	}
157818b9c05SArtur Paszkiewicz 
158818b9c05SArtur Paszkiewicz 	/* If specified, unlink before create a new SHM object */
159818b9c05SArtur Paszkiewicz 	if (flags & FTL_MD_CREATE_SHM_NEW) {
160818b9c05SArtur Paszkiewicz 		if (md->shm_unlink(md->name) < 0 && errno != ENOENT) {
161f45c0075SArtur Paszkiewicz 			ftl_md_invalidate_shm(md);
162818b9c05SArtur Paszkiewicz 			return;
163818b9c05SArtur Paszkiewicz 		}
164818b9c05SArtur Paszkiewicz 		open_flags += O_CREAT | O_TRUNC;
165818b9c05SArtur Paszkiewicz 	}
166818b9c05SArtur Paszkiewicz 
167818b9c05SArtur Paszkiewicz 	/* Open existing or create a new SHM object, then query its props */
168818b9c05SArtur Paszkiewicz 	md->shm_fd = md->shm_open(md->name, open_flags, open_mode);
169818b9c05SArtur Paszkiewicz 	if (md->shm_fd < 0 || fstat(md->shm_fd, &shm_stat) < 0) {
170818b9c05SArtur Paszkiewicz 		goto err_shm;
171818b9c05SArtur Paszkiewicz 	}
172818b9c05SArtur Paszkiewicz 
173818b9c05SArtur Paszkiewicz 	/* Verify open mode hasn't changed */
174818b9c05SArtur Paszkiewicz 	if ((shm_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != open_mode) {
175818b9c05SArtur Paszkiewicz 		goto err_shm;
176818b9c05SArtur Paszkiewicz 	}
177818b9c05SArtur Paszkiewicz 
178818b9c05SArtur Paszkiewicz 	/* Round up the SHM obj size to the nearest blk size (i.e. page size) */
179818b9c05SArtur Paszkiewicz 	md->shm_sz = spdk_divide_round_up(md->data_blocks * FTL_BLOCK_SIZE, shm_stat.st_blksize);
180818b9c05SArtur Paszkiewicz 
181818b9c05SArtur Paszkiewicz 	/* Add some blks for VSS metadata */
182818b9c05SArtur Paszkiewicz 	vss_blk_offs = md->shm_sz;
183818b9c05SArtur Paszkiewicz 
184818b9c05SArtur Paszkiewicz 	if (vss_blksz) {
185818b9c05SArtur Paszkiewicz 		md->shm_sz += spdk_divide_round_up(md->data_blocks * vss_blksz,
186818b9c05SArtur Paszkiewicz 						   shm_stat.st_blksize);
187818b9c05SArtur Paszkiewicz 	}
188818b9c05SArtur Paszkiewicz 
189818b9c05SArtur Paszkiewicz 	/* Total SHM obj size */
190818b9c05SArtur Paszkiewicz 	md->shm_sz *= shm_stat.st_blksize;
191818b9c05SArtur Paszkiewicz 
192818b9c05SArtur Paszkiewicz 	/* Set or check the object size - zero init`d in case of set (FTL_MD_CREATE_SHM_NEW) */
193818b9c05SArtur Paszkiewicz 	if ((shm_stat.st_size == 0 && (ftruncate(md->shm_fd, md->shm_sz) < 0 ||
194818b9c05SArtur Paszkiewicz 				       (flags & FTL_MD_CREATE_SHM_NEW) == 0))
195818b9c05SArtur Paszkiewicz 	    || (shm_stat.st_size > 0 && (size_t)shm_stat.st_size != md->shm_sz)) {
196818b9c05SArtur Paszkiewicz 		goto err_shm;
197818b9c05SArtur Paszkiewicz 	}
198818b9c05SArtur Paszkiewicz 
199818b9c05SArtur Paszkiewicz 	/* Create a virtual memory mapping for the object */
200818b9c05SArtur Paszkiewicz 	shm_ptr = mmap(NULL, md->shm_sz, PROT_READ | PROT_WRITE, md->shm_mmap_flags,
201818b9c05SArtur Paszkiewicz 		       md->shm_fd, 0);
202818b9c05SArtur Paszkiewicz 	if (shm_ptr == MAP_FAILED) {
203818b9c05SArtur Paszkiewicz 		goto err_shm;
204818b9c05SArtur Paszkiewicz 	}
205818b9c05SArtur Paszkiewicz 
206818b9c05SArtur Paszkiewicz 	md->data = shm_ptr;
207818b9c05SArtur Paszkiewicz 	if (vss_blksz) {
208818b9c05SArtur Paszkiewicz 		md->vss_data = ((char *)shm_ptr) + vss_blk_offs * shm_stat.st_blksize;
209818b9c05SArtur Paszkiewicz 	}
210818b9c05SArtur Paszkiewicz 
211818b9c05SArtur Paszkiewicz 	/* Lock the pages in memory (i.e. prevent the pages to be paged out) */
212818b9c05SArtur Paszkiewicz 	if (mlock(md->data, md->shm_sz) < 0) {
213818b9c05SArtur Paszkiewicz 		goto err_map;
214818b9c05SArtur Paszkiewicz 	}
215818b9c05SArtur Paszkiewicz 
216818b9c05SArtur Paszkiewicz 	if (spdk_mem_register(md->data, md->shm_sz)) {
217818b9c05SArtur Paszkiewicz 		goto err_mlock;
218818b9c05SArtur Paszkiewicz 	}
219818b9c05SArtur Paszkiewicz 	md->mem_reg = true;
220818b9c05SArtur Paszkiewicz 
221818b9c05SArtur Paszkiewicz 	return;
222818b9c05SArtur Paszkiewicz 
223818b9c05SArtur Paszkiewicz 	/* Cleanup upon fault */
224818b9c05SArtur Paszkiewicz err_mlock:
225818b9c05SArtur Paszkiewicz 	munlock(md->data, md->shm_sz);
226818b9c05SArtur Paszkiewicz 
227818b9c05SArtur Paszkiewicz err_map:
228818b9c05SArtur Paszkiewicz 	munmap(md->data, md->shm_sz);
229818b9c05SArtur Paszkiewicz 	md->data = NULL;
230818b9c05SArtur Paszkiewicz 	md->vss_data = NULL;
231818b9c05SArtur Paszkiewicz 	md->shm_sz = 0;
232818b9c05SArtur Paszkiewicz 
233818b9c05SArtur Paszkiewicz err_shm:
234818b9c05SArtur Paszkiewicz 	if (md->shm_fd >= 0) {
235818b9c05SArtur Paszkiewicz 		close(md->shm_fd);
236818b9c05SArtur Paszkiewicz 		md->shm_unlink(md->name);
237818b9c05SArtur Paszkiewicz 		md->shm_fd = -1;
238818b9c05SArtur Paszkiewicz 	}
239f45c0075SArtur Paszkiewicz 	ftl_md_invalidate_shm(md);
240818b9c05SArtur Paszkiewicz }
241818b9c05SArtur Paszkiewicz 
242818b9c05SArtur Paszkiewicz static void
2430e33da49SKozlowski Mateusz ftl_md_destroy_shm(struct ftl_md *md, int flags)
244818b9c05SArtur Paszkiewicz {
245818b9c05SArtur Paszkiewicz 	if (!md->data) {
246818b9c05SArtur Paszkiewicz 		return;
247818b9c05SArtur Paszkiewicz 	}
248818b9c05SArtur Paszkiewicz 
249818b9c05SArtur Paszkiewicz 	assert(md->shm_sz > 0);
250818b9c05SArtur Paszkiewicz 	if (md->mem_reg) {
251818b9c05SArtur Paszkiewicz 		spdk_mem_unregister(md->data, md->shm_sz);
252818b9c05SArtur Paszkiewicz 		md->mem_reg = false;
253818b9c05SArtur Paszkiewicz 	}
254818b9c05SArtur Paszkiewicz 
255818b9c05SArtur Paszkiewicz 	/* Unlock the pages in memory */
256818b9c05SArtur Paszkiewicz 	munlock(md->data, md->shm_sz);
257818b9c05SArtur Paszkiewicz 
258818b9c05SArtur Paszkiewicz 	/* Remove the virtual memory mapping for the object */
259818b9c05SArtur Paszkiewicz 	munmap(md->data, md->shm_sz);
260818b9c05SArtur Paszkiewicz 
261818b9c05SArtur Paszkiewicz 	/* Close SHM object fd */
262818b9c05SArtur Paszkiewicz 	close(md->shm_fd);
263818b9c05SArtur Paszkiewicz 
264818b9c05SArtur Paszkiewicz 	md->data = NULL;
265818b9c05SArtur Paszkiewicz 	md->vss_data = NULL;
266818b9c05SArtur Paszkiewicz 
2670e33da49SKozlowski Mateusz 	/* If specified, keep the object in SHM */
2680e33da49SKozlowski Mateusz 	if (flags & FTL_MD_DESTROY_SHM_KEEP) {
2690e33da49SKozlowski Mateusz 		return;
2700e33da49SKozlowski Mateusz 	}
2710e33da49SKozlowski Mateusz 
272818b9c05SArtur Paszkiewicz 	/* Otherwise destroy/unlink the object */
273818b9c05SArtur Paszkiewicz 	assert(md->name[0] != 0 && md->shm_unlink != NULL);
274818b9c05SArtur Paszkiewicz 	md->shm_unlink(md->name);
275818b9c05SArtur Paszkiewicz }
276818b9c05SArtur Paszkiewicz 
2777a7ac2afSArtur Paszkiewicz struct ftl_md *ftl_md_create(struct spdk_ftl_dev *dev, uint64_t blocks,
278818b9c05SArtur Paszkiewicz 			     uint64_t vss_blksz, const char *name, int flags,
2797a7ac2afSArtur Paszkiewicz 			     const struct ftl_layout_region *region)
2807a7ac2afSArtur Paszkiewicz {
2817a7ac2afSArtur Paszkiewicz 	struct ftl_md *md;
2827a7ac2afSArtur Paszkiewicz 
2837a7ac2afSArtur Paszkiewicz 	md = calloc(1, sizeof(*md));
2847a7ac2afSArtur Paszkiewicz 	if (!md) {
2857a7ac2afSArtur Paszkiewicz 		return NULL;
2867a7ac2afSArtur Paszkiewicz 	}
2877a7ac2afSArtur Paszkiewicz 	md->dev = dev;
2887a7ac2afSArtur Paszkiewicz 	md->data_blocks = blocks;
2897a7ac2afSArtur Paszkiewicz 	md->mirror_enabled = true;
2907a7ac2afSArtur Paszkiewicz 
291818b9c05SArtur Paszkiewicz 	if (flags != FTL_MD_CREATE_NO_MEM) {
292818b9c05SArtur Paszkiewicz 		if (flags & FTL_MD_CREATE_SHM) {
293818b9c05SArtur Paszkiewicz 			ftl_md_setup_obj(md, flags, name);
294818b9c05SArtur Paszkiewicz 			ftl_md_create_shm(md, vss_blksz, flags);
295a0fb555bSMateusz Kozlowski 		} else if (flags & FTL_MD_CREATE_SPDK_BUF) {
296a0fb555bSMateusz Kozlowski 			ftl_md_create_spdk_buf(md, vss_blksz);
297818b9c05SArtur Paszkiewicz 		} else {
298818b9c05SArtur Paszkiewicz 			assert((flags & FTL_MD_CREATE_HEAP) == FTL_MD_CREATE_HEAP);
299818b9c05SArtur Paszkiewicz 			ftl_md_create_heap(md, vss_blksz);
300818b9c05SArtur Paszkiewicz 		}
3017a7ac2afSArtur Paszkiewicz 
302818b9c05SArtur Paszkiewicz 		if (!md->data) {
3037a7ac2afSArtur Paszkiewicz 			free(md);
3047a7ac2afSArtur Paszkiewicz 			return NULL;
3057a7ac2afSArtur Paszkiewicz 		}
3067a7ac2afSArtur Paszkiewicz 	}
3077a7ac2afSArtur Paszkiewicz 
3087a7ac2afSArtur Paszkiewicz 	if (region) {
3097a7ac2afSArtur Paszkiewicz 		size_t entry_vss_buf_size = vss_blksz * region->entry_size;
3107a7ac2afSArtur Paszkiewicz 
3117a7ac2afSArtur Paszkiewicz 		if (entry_vss_buf_size) {
3127a7ac2afSArtur Paszkiewicz 			md->entry_vss_dma_buf = spdk_malloc(entry_vss_buf_size, FTL_BLOCK_SIZE,
3137a7ac2afSArtur Paszkiewicz 							    NULL, SPDK_ENV_LCORE_ID_ANY,
3147a7ac2afSArtur Paszkiewicz 							    SPDK_MALLOC_DMA);
3157a7ac2afSArtur Paszkiewicz 			if (!md->entry_vss_dma_buf) {
3167a7ac2afSArtur Paszkiewicz 				goto err;
3177a7ac2afSArtur Paszkiewicz 			}
3187a7ac2afSArtur Paszkiewicz 		}
3197a7ac2afSArtur Paszkiewicz 
320522a0c82SLukasz Lasek 		ftl_md_set_region(md, region);
3217a7ac2afSArtur Paszkiewicz 	}
3227a7ac2afSArtur Paszkiewicz 
3237a7ac2afSArtur Paszkiewicz 	return md;
3247a7ac2afSArtur Paszkiewicz err:
3250e33da49SKozlowski Mateusz 	ftl_md_destroy(md, ftl_md_destroy_region_flags(dev, region->type));
3267a7ac2afSArtur Paszkiewicz 	return NULL;
3277a7ac2afSArtur Paszkiewicz }
3287a7ac2afSArtur Paszkiewicz 
329818b9c05SArtur Paszkiewicz int
330818b9c05SArtur Paszkiewicz ftl_md_unlink(struct spdk_ftl_dev *dev, const char *name, int flags)
331818b9c05SArtur Paszkiewicz {
332818b9c05SArtur Paszkiewicz 	struct ftl_md md = { 0 };
333818b9c05SArtur Paszkiewicz 
334818b9c05SArtur Paszkiewicz 	if (0 == (flags & FTL_MD_CREATE_SHM)) {
335818b9c05SArtur Paszkiewicz 		/* Unlink can be called for shared memory only */
336818b9c05SArtur Paszkiewicz 		return -EINVAL;
337818b9c05SArtur Paszkiewicz 	}
338818b9c05SArtur Paszkiewicz 
339818b9c05SArtur Paszkiewicz 	md.dev = dev;
340818b9c05SArtur Paszkiewicz 	ftl_md_setup_obj(&md, flags, name);
341818b9c05SArtur Paszkiewicz 
342818b9c05SArtur Paszkiewicz 	return md.shm_unlink(md.name);
343818b9c05SArtur Paszkiewicz }
344818b9c05SArtur Paszkiewicz 
3457a7ac2afSArtur Paszkiewicz void
3460e33da49SKozlowski Mateusz ftl_md_destroy(struct ftl_md *md, int flags)
3477a7ac2afSArtur Paszkiewicz {
3487a7ac2afSArtur Paszkiewicz 	if (!md) {
3497a7ac2afSArtur Paszkiewicz 		return;
3507a7ac2afSArtur Paszkiewicz 	}
3517a7ac2afSArtur Paszkiewicz 
352522a0c82SLukasz Lasek 	if (!md->is_mirror) {
3530e33da49SKozlowski Mateusz 		ftl_md_free_buf(md, flags);
3547a7ac2afSArtur Paszkiewicz 		spdk_free(md->entry_vss_dma_buf);
355522a0c82SLukasz Lasek 	}
3567a7ac2afSArtur Paszkiewicz 	free(md);
3577a7ac2afSArtur Paszkiewicz }
3587a7ac2afSArtur Paszkiewicz 
3597a7ac2afSArtur Paszkiewicz void
3600e33da49SKozlowski Mateusz ftl_md_free_buf(struct ftl_md *md, int flags)
3617a7ac2afSArtur Paszkiewicz {
3627a7ac2afSArtur Paszkiewicz 	if (!md) {
3637a7ac2afSArtur Paszkiewicz 		return;
3647a7ac2afSArtur Paszkiewicz 	}
3657a7ac2afSArtur Paszkiewicz 
366818b9c05SArtur Paszkiewicz 	if (md->shm_fd < 0) {
367a0fb555bSMateusz Kozlowski 		if (flags & FTL_MD_DESTROY_SPDK_BUF) {
368a0fb555bSMateusz Kozlowski 			ftl_md_destroy_spdk_buf(md);
369a0fb555bSMateusz Kozlowski 		} else {
3700e33da49SKozlowski Mateusz 			assert(flags == 0);
371818b9c05SArtur Paszkiewicz 			ftl_md_destroy_heap(md);
372a0fb555bSMateusz Kozlowski 		}
373818b9c05SArtur Paszkiewicz 	} else {
3740e33da49SKozlowski Mateusz 		ftl_md_destroy_shm(md, flags);
3757a7ac2afSArtur Paszkiewicz 	}
3767a7ac2afSArtur Paszkiewicz }
3777a7ac2afSArtur Paszkiewicz 
3787a7ac2afSArtur Paszkiewicz void *
3797a7ac2afSArtur Paszkiewicz ftl_md_get_buffer(struct ftl_md *md)
3807a7ac2afSArtur Paszkiewicz {
3817a7ac2afSArtur Paszkiewicz 	return md->data;
3827a7ac2afSArtur Paszkiewicz }
3837a7ac2afSArtur Paszkiewicz 
3847a7ac2afSArtur Paszkiewicz uint64_t
3857a7ac2afSArtur Paszkiewicz ftl_md_get_buffer_size(struct ftl_md *md)
3867a7ac2afSArtur Paszkiewicz {
3877a7ac2afSArtur Paszkiewicz 	return md->data_blocks * FTL_BLOCK_SIZE;
3887a7ac2afSArtur Paszkiewicz }
3897a7ac2afSArtur Paszkiewicz 
3907a7ac2afSArtur Paszkiewicz static void
3917a7ac2afSArtur Paszkiewicz ftl_md_vss_buf_init(union ftl_md_vss *buf, uint32_t count,
3927a7ac2afSArtur Paszkiewicz 		    const union ftl_md_vss *vss_pattern)
3937a7ac2afSArtur Paszkiewicz {
3947a7ac2afSArtur Paszkiewicz 	while (count) {
3957a7ac2afSArtur Paszkiewicz 		count--;
3967a7ac2afSArtur Paszkiewicz 		buf[count] = *vss_pattern;
3977a7ac2afSArtur Paszkiewicz 	}
3987a7ac2afSArtur Paszkiewicz }
3997a7ac2afSArtur Paszkiewicz 
4007a7ac2afSArtur Paszkiewicz union ftl_md_vss *ftl_md_vss_buf_alloc(struct ftl_layout_region *region, uint32_t count)
4017a7ac2afSArtur Paszkiewicz {
4027a7ac2afSArtur Paszkiewicz 	union ftl_md_vss *buf = spdk_zmalloc(count * FTL_MD_VSS_SZ, FTL_BLOCK_SIZE, NULL,
4037a7ac2afSArtur Paszkiewicz 						     SPDK_ENV_LCORE_ID_ANY,
4047a7ac2afSArtur Paszkiewicz 						     SPDK_MALLOC_DMA);
4057a7ac2afSArtur Paszkiewicz 
4067a7ac2afSArtur Paszkiewicz 	if (!buf) {
4077a7ac2afSArtur Paszkiewicz 		return NULL;
4087a7ac2afSArtur Paszkiewicz 	}
4097a7ac2afSArtur Paszkiewicz 
4107a7ac2afSArtur Paszkiewicz 	union ftl_md_vss vss_buf = {0};
4117a7ac2afSArtur Paszkiewicz 	vss_buf.version.md_version = region->current.version;
4127a7ac2afSArtur Paszkiewicz 	ftl_md_vss_buf_init(buf, count, &vss_buf);
4137a7ac2afSArtur Paszkiewicz 	return buf;
4147a7ac2afSArtur Paszkiewicz }
4157a7ac2afSArtur Paszkiewicz 
4167a7ac2afSArtur Paszkiewicz union ftl_md_vss *ftl_md_get_vss_buffer(struct ftl_md *md)
4177a7ac2afSArtur Paszkiewicz {
4187a7ac2afSArtur Paszkiewicz 	return md->vss_data;
4197a7ac2afSArtur Paszkiewicz }
4207a7ac2afSArtur Paszkiewicz 
4217a7ac2afSArtur Paszkiewicz static void
4227a7ac2afSArtur Paszkiewicz io_cleanup(struct ftl_md *md)
4237a7ac2afSArtur Paszkiewicz {
4247a7ac2afSArtur Paszkiewicz 	spdk_dma_free(md->io.data);
4257a7ac2afSArtur Paszkiewicz 	md->io.data = NULL;
4267a7ac2afSArtur Paszkiewicz 
4277a7ac2afSArtur Paszkiewicz 	spdk_dma_free(md->io.md);
4287a7ac2afSArtur Paszkiewicz 	md->io.md = NULL;
4297a7ac2afSArtur Paszkiewicz }
4307a7ac2afSArtur Paszkiewicz 
4317a7ac2afSArtur Paszkiewicz static void
4327a7ac2afSArtur Paszkiewicz exception(void *arg)
4337a7ac2afSArtur Paszkiewicz {
4347a7ac2afSArtur Paszkiewicz 	struct ftl_md *md = arg;
4357a7ac2afSArtur Paszkiewicz 
4367a7ac2afSArtur Paszkiewicz 	md->cb(md->dev, md, -EINVAL);
4377a7ac2afSArtur Paszkiewicz 	io_cleanup(md);
4387a7ac2afSArtur Paszkiewicz }
4397a7ac2afSArtur Paszkiewicz 
4401790ee8aSArtur Paszkiewicz static inline enum ftl_stats_type
4411790ee8aSArtur Paszkiewicz get_bdev_io_ftl_stats_type(struct spdk_ftl_dev *dev, struct spdk_bdev_io *bdev_io) {
4421790ee8aSArtur Paszkiewicz 	struct spdk_bdev *nvc = spdk_bdev_desc_get_bdev(dev->nv_cache.bdev_desc);
4431790ee8aSArtur Paszkiewicz 
4441790ee8aSArtur Paszkiewicz 	if (bdev_io->bdev == nvc)
4451790ee8aSArtur Paszkiewicz 	{
4461790ee8aSArtur Paszkiewicz 		return FTL_STATS_TYPE_MD_NV_CACHE;
4471790ee8aSArtur Paszkiewicz 	} else
4481790ee8aSArtur Paszkiewicz 	{
4491790ee8aSArtur Paszkiewicz 		return FTL_STATS_TYPE_MD_BASE;
4501790ee8aSArtur Paszkiewicz 	}
4511790ee8aSArtur Paszkiewicz }
4521790ee8aSArtur Paszkiewicz 
4537a7ac2afSArtur Paszkiewicz static void
4547a7ac2afSArtur Paszkiewicz read_write_blocks_cb(struct spdk_bdev_io *bdev_io, bool success, void *arg)
4557a7ac2afSArtur Paszkiewicz {
4567a7ac2afSArtur Paszkiewicz 	struct ftl_md *md = arg;
4577a7ac2afSArtur Paszkiewicz 
4581790ee8aSArtur Paszkiewicz 	ftl_stats_bdev_io_completed(md->dev, get_bdev_io_ftl_stats_type(md->dev, bdev_io), bdev_io);
4591790ee8aSArtur Paszkiewicz 
4607a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(!success)) {
4617a7ac2afSArtur Paszkiewicz 		if (md->io.op == FTL_MD_OP_RESTORE && has_mirror(md)) {
4627a7ac2afSArtur Paszkiewicz 			md->io.status = -EAGAIN;
4637a7ac2afSArtur Paszkiewicz 		} else {
4647a7ac2afSArtur Paszkiewicz 			md->io.status = -EIO;
4657a7ac2afSArtur Paszkiewicz 		}
4667a7ac2afSArtur Paszkiewicz 	} else {
4677a7ac2afSArtur Paszkiewicz 		uint64_t blocks = bdev_io->u.bdev.num_blocks;
4687a7ac2afSArtur Paszkiewicz 		uint64_t size = blocks * FTL_BLOCK_SIZE;
4697a7ac2afSArtur Paszkiewicz 
4707a7ac2afSArtur Paszkiewicz 		if (md->io.op == FTL_MD_OP_RESTORE) {
4717a7ac2afSArtur Paszkiewicz 			memcpy(md->data + md->io.data_offset, md->io.data, size);
4727a7ac2afSArtur Paszkiewicz 
4737a7ac2afSArtur Paszkiewicz 			if (md->vss_data) {
4747a7ac2afSArtur Paszkiewicz 				uint64_t vss_offset = md->io.data_offset / FTL_BLOCK_SIZE;
4757a7ac2afSArtur Paszkiewicz 				vss_offset *= FTL_MD_VSS_SZ;
4767a7ac2afSArtur Paszkiewicz 				memcpy(md->vss_data + vss_offset, md->io.md, blocks * FTL_MD_VSS_SZ);
4777a7ac2afSArtur Paszkiewicz 			}
4787a7ac2afSArtur Paszkiewicz 		}
4797a7ac2afSArtur Paszkiewicz 
4807a7ac2afSArtur Paszkiewicz 		md->io.address += blocks;
4817a7ac2afSArtur Paszkiewicz 		md->io.remaining -= blocks;
4827a7ac2afSArtur Paszkiewicz 		md->io.data_offset += size;
4837a7ac2afSArtur Paszkiewicz 	}
4847a7ac2afSArtur Paszkiewicz 
4857a7ac2afSArtur Paszkiewicz 	spdk_bdev_free_io(bdev_io);
4867a7ac2afSArtur Paszkiewicz 
4877a7ac2afSArtur Paszkiewicz 	io_submit(md);
4887a7ac2afSArtur Paszkiewicz }
4897a7ac2afSArtur Paszkiewicz 
4907a7ac2afSArtur Paszkiewicz static inline int
4917a7ac2afSArtur Paszkiewicz read_blocks(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *desc,
4927a7ac2afSArtur Paszkiewicz 	    struct spdk_io_channel *ch,
4937a7ac2afSArtur Paszkiewicz 	    void *buf, void *md_buf,
4947a7ac2afSArtur Paszkiewicz 	    uint64_t offset_blocks, uint64_t num_blocks,
4957a7ac2afSArtur Paszkiewicz 	    spdk_bdev_io_completion_cb cb, void *cb_arg)
4967a7ac2afSArtur Paszkiewicz {
497a68a12a4SKozlowski Mateusz 	if (desc == dev->nv_cache.bdev_desc) {
498d6795254SArtur Paszkiewicz 		return ftl_nv_cache_bdev_read_blocks_with_md(dev, desc, ch, buf, md_buf,
499d6795254SArtur Paszkiewicz 				offset_blocks, num_blocks,
500d6795254SArtur Paszkiewicz 				cb, cb_arg);
501d6795254SArtur Paszkiewicz 	} else if (md_buf) {
5027a7ac2afSArtur Paszkiewicz 		return spdk_bdev_read_blocks_with_md(desc, ch, buf, md_buf,
5037a7ac2afSArtur Paszkiewicz 						     offset_blocks, num_blocks,
5047a7ac2afSArtur Paszkiewicz 						     cb, cb_arg);
5057a7ac2afSArtur Paszkiewicz 	} else {
5067a7ac2afSArtur Paszkiewicz 		return spdk_bdev_read_blocks(desc, ch, buf,
5077a7ac2afSArtur Paszkiewicz 					     offset_blocks, num_blocks,
5087a7ac2afSArtur Paszkiewicz 					     cb, cb_arg);
5097a7ac2afSArtur Paszkiewicz 	}
5107a7ac2afSArtur Paszkiewicz }
5117a7ac2afSArtur Paszkiewicz 
5127a7ac2afSArtur Paszkiewicz static inline int
5137a7ac2afSArtur Paszkiewicz write_blocks(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *desc,
5147a7ac2afSArtur Paszkiewicz 	     struct spdk_io_channel *ch,
5157a7ac2afSArtur Paszkiewicz 	     void *buf, void *md_buf,
5167a7ac2afSArtur Paszkiewicz 	     uint64_t offset_blocks, uint64_t num_blocks,
5177a7ac2afSArtur Paszkiewicz 	     spdk_bdev_io_completion_cb cb, void *cb_arg)
5187a7ac2afSArtur Paszkiewicz {
519a68a12a4SKozlowski Mateusz 	if (desc == dev->nv_cache.bdev_desc) {
520d6795254SArtur Paszkiewicz 		return ftl_nv_cache_bdev_write_blocks_with_md(dev, desc, ch, buf, md_buf,
521d6795254SArtur Paszkiewicz 				offset_blocks, num_blocks,
522d6795254SArtur Paszkiewicz 				cb, cb_arg);
523d6795254SArtur Paszkiewicz 	} else if (md_buf) {
5247a7ac2afSArtur Paszkiewicz 		return spdk_bdev_write_blocks_with_md(desc, ch, buf, md_buf, offset_blocks,
5257a7ac2afSArtur Paszkiewicz 						      num_blocks, cb, cb_arg);
5267a7ac2afSArtur Paszkiewicz 	} else {
5277a7ac2afSArtur Paszkiewicz 		return spdk_bdev_write_blocks(desc, ch, buf, offset_blocks, num_blocks, cb, cb_arg);
5287a7ac2afSArtur Paszkiewicz 	}
5297a7ac2afSArtur Paszkiewicz }
5307a7ac2afSArtur Paszkiewicz 
5317a7ac2afSArtur Paszkiewicz static void
5327a7ac2afSArtur Paszkiewicz read_write_blocks(void *_md)
5337a7ac2afSArtur Paszkiewicz {
5347a7ac2afSArtur Paszkiewicz 	struct ftl_md *md = _md;
5357a7ac2afSArtur Paszkiewicz 	const struct ftl_layout_region *region = md->region;
5367a7ac2afSArtur Paszkiewicz 	uint64_t blocks;
5377a7ac2afSArtur Paszkiewicz 	int rc = 0;
5387a7ac2afSArtur Paszkiewicz 
5397a7ac2afSArtur Paszkiewicz 	blocks = spdk_min(md->io.remaining, ftl_md_xfer_blocks(md->dev));
5407a7ac2afSArtur Paszkiewicz 
5417a7ac2afSArtur Paszkiewicz 	switch (md->io.op) {
5427a7ac2afSArtur Paszkiewicz 	case FTL_MD_OP_RESTORE:
5437a7ac2afSArtur Paszkiewicz 		rc = read_blocks(md->dev, region->bdev_desc, region->ioch,
5447a7ac2afSArtur Paszkiewicz 				 md->io.data, md->io.md,
5457a7ac2afSArtur Paszkiewicz 				 md->io.address, blocks,
5467a7ac2afSArtur Paszkiewicz 				 read_write_blocks_cb, md);
5477a7ac2afSArtur Paszkiewicz 		break;
5487a7ac2afSArtur Paszkiewicz 	case FTL_MD_OP_PERSIST:
5497a7ac2afSArtur Paszkiewicz 	case FTL_MD_OP_CLEAR:
5507a7ac2afSArtur Paszkiewicz 		rc = write_blocks(md->dev, region->bdev_desc, region->ioch,
5517a7ac2afSArtur Paszkiewicz 				  md->io.data, md->io.md,
5527a7ac2afSArtur Paszkiewicz 				  md->io.address, blocks,
5537a7ac2afSArtur Paszkiewicz 				  read_write_blocks_cb, md);
5547a7ac2afSArtur Paszkiewicz 		break;
5557a7ac2afSArtur Paszkiewicz 	default:
5567a7ac2afSArtur Paszkiewicz 		ftl_abort();
5577a7ac2afSArtur Paszkiewicz 	}
5587a7ac2afSArtur Paszkiewicz 
5597a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(rc)) {
5607a7ac2afSArtur Paszkiewicz 		if (rc == -ENOMEM) {
5617a7ac2afSArtur Paszkiewicz 			struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(region->bdev_desc);
5627a7ac2afSArtur Paszkiewicz 			md->io.bdev_io_wait.bdev = bdev;
5637a7ac2afSArtur Paszkiewicz 			md->io.bdev_io_wait.cb_fn = read_write_blocks;
5647a7ac2afSArtur Paszkiewicz 			md->io.bdev_io_wait.cb_arg = md;
5657a7ac2afSArtur Paszkiewicz 			spdk_bdev_queue_io_wait(bdev, region->ioch, &md->io.bdev_io_wait);
5667a7ac2afSArtur Paszkiewicz 		} else {
5677a7ac2afSArtur Paszkiewicz 			ftl_abort();
5687a7ac2afSArtur Paszkiewicz 		}
5697a7ac2afSArtur Paszkiewicz 	}
5707a7ac2afSArtur Paszkiewicz }
5717a7ac2afSArtur Paszkiewicz 
5727a7ac2afSArtur Paszkiewicz static void
5737a7ac2afSArtur Paszkiewicz io_submit(struct ftl_md *md)
5747a7ac2afSArtur Paszkiewicz {
5757a7ac2afSArtur Paszkiewicz 	if (!md->io.remaining || md->io.status) {
5767a7ac2afSArtur Paszkiewicz 		io_done(md);
5777a7ac2afSArtur Paszkiewicz 		return;
5787a7ac2afSArtur Paszkiewicz 	}
5797a7ac2afSArtur Paszkiewicz 
5807a7ac2afSArtur Paszkiewicz 	if (md->io.op == FTL_MD_OP_PERSIST) {
5817a7ac2afSArtur Paszkiewicz 		uint64_t blocks = spdk_min(md->io.remaining, ftl_md_xfer_blocks(md->dev));
5827a7ac2afSArtur Paszkiewicz 
5837a7ac2afSArtur Paszkiewicz 		memcpy(md->io.data, md->data + md->io.data_offset, FTL_BLOCK_SIZE * blocks);
5847a7ac2afSArtur Paszkiewicz 
5857a7ac2afSArtur Paszkiewicz 		if (md->vss_data) {
5867a7ac2afSArtur Paszkiewicz 			uint64_t vss_offset = md->io.data_offset / FTL_BLOCK_SIZE;
5877a7ac2afSArtur Paszkiewicz 			vss_offset *= FTL_MD_VSS_SZ;
5887a7ac2afSArtur Paszkiewicz 			assert(md->io.md);
5897a7ac2afSArtur Paszkiewicz 			memcpy(md->io.md, md->vss_data + vss_offset, FTL_MD_VSS_SZ * blocks);
5907a7ac2afSArtur Paszkiewicz 		}
5917a7ac2afSArtur Paszkiewicz 	}
5927a7ac2afSArtur Paszkiewicz 
5937a7ac2afSArtur Paszkiewicz 	read_write_blocks(md);
5947a7ac2afSArtur Paszkiewicz }
5957a7ac2afSArtur Paszkiewicz 
5967a7ac2afSArtur Paszkiewicz static int
5977a7ac2afSArtur Paszkiewicz io_can_start(struct ftl_md *md)
5987a7ac2afSArtur Paszkiewicz {
5997a7ac2afSArtur Paszkiewicz 	assert(NULL == md->io.data);
6007a7ac2afSArtur Paszkiewicz 	if (NULL != md->io.data) {
6017a7ac2afSArtur Paszkiewicz 		/* Outgoing IO on metadata */
6027a7ac2afSArtur Paszkiewicz 		return -EINVAL;
6037a7ac2afSArtur Paszkiewicz 	}
6047a7ac2afSArtur Paszkiewicz 
6057a7ac2afSArtur Paszkiewicz 	if (!md->region) {
6067a7ac2afSArtur Paszkiewicz 		/* No device region to process data */
6077a7ac2afSArtur Paszkiewicz 		return -EINVAL;
6087a7ac2afSArtur Paszkiewicz 	}
6097a7ac2afSArtur Paszkiewicz 
6107a7ac2afSArtur Paszkiewicz 	if (md->region->current.blocks > md->data_blocks) {
6117a7ac2afSArtur Paszkiewicz 		/* No device region to process data */
6127a7ac2afSArtur Paszkiewicz 		FTL_ERRLOG(md->dev, "Blocks number mismatch between metadata object and"
6137a7ac2afSArtur Paszkiewicz 			   "device region\n");
6147a7ac2afSArtur Paszkiewicz 		return -EINVAL;
6157a7ac2afSArtur Paszkiewicz 	}
6167a7ac2afSArtur Paszkiewicz 
6177a7ac2afSArtur Paszkiewicz 	return 0;
6187a7ac2afSArtur Paszkiewicz }
6197a7ac2afSArtur Paszkiewicz 
6207a7ac2afSArtur Paszkiewicz static int
6217a7ac2afSArtur Paszkiewicz io_prepare(struct ftl_md *md, enum ftl_md_ops op)
6227a7ac2afSArtur Paszkiewicz {
6237a7ac2afSArtur Paszkiewicz 	const struct ftl_layout_region *region = md->region;
6247a7ac2afSArtur Paszkiewicz 	uint64_t data_size, meta_size = 0;
6257a7ac2afSArtur Paszkiewicz 
6267a7ac2afSArtur Paszkiewicz 	/* Allocates buffer for IO */
6277a7ac2afSArtur Paszkiewicz 	data_size = xfer_size(md);
6287a7ac2afSArtur Paszkiewicz 	md->io.data = spdk_zmalloc(data_size, FTL_BLOCK_SIZE, NULL,
6297a7ac2afSArtur Paszkiewicz 				   SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
6307a7ac2afSArtur Paszkiewicz 	if (!md->io.data) {
6317a7ac2afSArtur Paszkiewicz 		return -ENOMEM;
6327a7ac2afSArtur Paszkiewicz 	}
6337a7ac2afSArtur Paszkiewicz 
6347a7ac2afSArtur Paszkiewicz 	if (md->vss_data || md->region->vss_blksz) {
6357a7ac2afSArtur Paszkiewicz 		meta_size = ftl_md_xfer_blocks(md->dev) * FTL_MD_VSS_SZ;
6367a7ac2afSArtur Paszkiewicz 		md->io.md = spdk_zmalloc(meta_size, FTL_BLOCK_SIZE, NULL,
6377a7ac2afSArtur Paszkiewicz 					 SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
6387a7ac2afSArtur Paszkiewicz 		if (!md->io.md) {
6397a7ac2afSArtur Paszkiewicz 			spdk_dma_free(md->io.data);
6407a7ac2afSArtur Paszkiewicz 			md->io.data = NULL;
6417a7ac2afSArtur Paszkiewicz 			return -ENOMEM;
6427a7ac2afSArtur Paszkiewicz 		}
6437a7ac2afSArtur Paszkiewicz 	}
6447a7ac2afSArtur Paszkiewicz 
6457a7ac2afSArtur Paszkiewicz 	md->io.address = region->current.offset;
6467a7ac2afSArtur Paszkiewicz 	md->io.remaining = region->current.blocks;
6477a7ac2afSArtur Paszkiewicz 	md->io.data_offset = 0;
6487a7ac2afSArtur Paszkiewicz 	md->io.status = 0;
6497a7ac2afSArtur Paszkiewicz 	md->io.op = op;
6507a7ac2afSArtur Paszkiewicz 
6517a7ac2afSArtur Paszkiewicz 	return 0;
6527a7ac2afSArtur Paszkiewicz }
6537a7ac2afSArtur Paszkiewicz 
6547a7ac2afSArtur Paszkiewicz static int
6557a7ac2afSArtur Paszkiewicz io_init(struct ftl_md *md, enum ftl_md_ops op)
6567a7ac2afSArtur Paszkiewicz {
6577a7ac2afSArtur Paszkiewicz 	if (io_can_start(md)) {
6587a7ac2afSArtur Paszkiewicz 		return -EINVAL;
6597a7ac2afSArtur Paszkiewicz 	}
6607a7ac2afSArtur Paszkiewicz 
6617a7ac2afSArtur Paszkiewicz 	if (io_prepare(md, op)) {
6627a7ac2afSArtur Paszkiewicz 		return -ENOMEM;
6637a7ac2afSArtur Paszkiewicz 	}
6647a7ac2afSArtur Paszkiewicz 
6657a7ac2afSArtur Paszkiewicz 	return 0;
6667a7ac2afSArtur Paszkiewicz }
6677a7ac2afSArtur Paszkiewicz 
6687a7ac2afSArtur Paszkiewicz static uint64_t
6697a7ac2afSArtur Paszkiewicz persist_entry_lba(struct ftl_md *md, uint64_t start_entry)
6707a7ac2afSArtur Paszkiewicz {
6717a7ac2afSArtur Paszkiewicz 	return md->region->current.offset + start_entry * md->region->entry_size;
6727a7ac2afSArtur Paszkiewicz }
6737a7ac2afSArtur Paszkiewicz 
6747a7ac2afSArtur Paszkiewicz static void
6757a7ac2afSArtur Paszkiewicz persist_entry_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
6767a7ac2afSArtur Paszkiewicz {
6777a7ac2afSArtur Paszkiewicz 	struct ftl_md_io_entry_ctx *ctx = cb_arg;
6781790ee8aSArtur Paszkiewicz 	struct ftl_md *md = ctx->md;
6791790ee8aSArtur Paszkiewicz 
6801790ee8aSArtur Paszkiewicz 	ftl_stats_bdev_io_completed(md->dev, get_bdev_io_ftl_stats_type(md->dev, bdev_io), bdev_io);
6817a7ac2afSArtur Paszkiewicz 
6827a7ac2afSArtur Paszkiewicz 	spdk_bdev_free_io(bdev_io);
6837a7ac2afSArtur Paszkiewicz 
6847a7ac2afSArtur Paszkiewicz 	assert(ctx->remaining > 0);
6857a7ac2afSArtur Paszkiewicz 	ctx->remaining--;
6867a7ac2afSArtur Paszkiewicz 
6877a7ac2afSArtur Paszkiewicz 	if (!success) {
6887a7ac2afSArtur Paszkiewicz 		ctx->status = -EIO;
6897a7ac2afSArtur Paszkiewicz 	}
6907a7ac2afSArtur Paszkiewicz 
6917a7ac2afSArtur Paszkiewicz 	if (!ctx->remaining) {
6927a7ac2afSArtur Paszkiewicz 		ctx->cb(ctx->status, ctx->cb_arg);
6937a7ac2afSArtur Paszkiewicz 	}
6947a7ac2afSArtur Paszkiewicz }
6957a7ac2afSArtur Paszkiewicz 
6967a7ac2afSArtur Paszkiewicz static int
6977a7ac2afSArtur Paszkiewicz ftl_md_persist_entry_write_blocks(struct ftl_md_io_entry_ctx *ctx, struct ftl_md *md,
6987a7ac2afSArtur Paszkiewicz 				  spdk_bdev_io_wait_cb retry_fn)
6997a7ac2afSArtur Paszkiewicz {
7007a7ac2afSArtur Paszkiewicz 	int rc;
7017a7ac2afSArtur Paszkiewicz 
7027a7ac2afSArtur Paszkiewicz 	rc = write_blocks(md->dev, md->region->bdev_desc, md->region->ioch,
7037a7ac2afSArtur Paszkiewicz 			  ctx->buffer, ctx->vss_buffer,
7041f11e145SMateusz Kozlowski 			  persist_entry_lba(md, ctx->start_entry), md->region->entry_size * ctx->num_entries,
7057a7ac2afSArtur Paszkiewicz 			  persist_entry_cb, ctx);
7067a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(rc)) {
7077a7ac2afSArtur Paszkiewicz 		if (rc == -ENOMEM) {
7087a7ac2afSArtur Paszkiewicz 			struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(md->region->bdev_desc);
7097a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.bdev = bdev;
7107a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.cb_fn = retry_fn;
7117a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.cb_arg = ctx;
7127a7ac2afSArtur Paszkiewicz 			spdk_bdev_queue_io_wait(bdev, md->region->ioch, &ctx->bdev_io_wait);
7137a7ac2afSArtur Paszkiewicz 		} else {
7147a7ac2afSArtur Paszkiewicz 			ftl_abort();
7157a7ac2afSArtur Paszkiewicz 		}
7167a7ac2afSArtur Paszkiewicz 	}
7177a7ac2afSArtur Paszkiewicz 
7187a7ac2afSArtur Paszkiewicz 	return rc;
7197a7ac2afSArtur Paszkiewicz }
7207a7ac2afSArtur Paszkiewicz 
7217a7ac2afSArtur Paszkiewicz static void
7227a7ac2afSArtur Paszkiewicz ftl_md_persist_entry_mirror(void *_ctx)
7237a7ac2afSArtur Paszkiewicz {
7247a7ac2afSArtur Paszkiewicz 	struct ftl_md_io_entry_ctx *ctx = _ctx;
725522a0c82SLukasz Lasek 	struct ftl_md *md_mirror = ftl_md_get_mirror(ctx->md);
7267a7ac2afSArtur Paszkiewicz 
727522a0c82SLukasz Lasek 	ftl_md_persist_entry_write_blocks(ctx, md_mirror, ftl_md_persist_entry_mirror);
7287a7ac2afSArtur Paszkiewicz }
7297a7ac2afSArtur Paszkiewicz 
7307a7ac2afSArtur Paszkiewicz static void
7317a7ac2afSArtur Paszkiewicz ftl_md_persist_entry_primary(void *_ctx)
7327a7ac2afSArtur Paszkiewicz {
7337a7ac2afSArtur Paszkiewicz 	struct ftl_md_io_entry_ctx *ctx = _ctx;
7347a7ac2afSArtur Paszkiewicz 	struct ftl_md *md = ctx->md;
7357a7ac2afSArtur Paszkiewicz 	int rc;
7367a7ac2afSArtur Paszkiewicz 
7377a7ac2afSArtur Paszkiewicz 	rc = ftl_md_persist_entry_write_blocks(ctx, md, ftl_md_persist_entry_primary);
7387a7ac2afSArtur Paszkiewicz 
7397a7ac2afSArtur Paszkiewicz 	if (!rc && has_mirror(md)) {
740522a0c82SLukasz Lasek 		assert(md->region->entry_size == (ftl_md_get_mirror(md))->region->entry_size);
7417a7ac2afSArtur Paszkiewicz 
7427a7ac2afSArtur Paszkiewicz 		/* The MD object has mirror so execute persist on it too */
7437a7ac2afSArtur Paszkiewicz 		ftl_md_persist_entry_mirror(ctx);
7447a7ac2afSArtur Paszkiewicz 		ctx->remaining++;
7457a7ac2afSArtur Paszkiewicz 	}
7467a7ac2afSArtur Paszkiewicz }
7477a7ac2afSArtur Paszkiewicz 
7487a7ac2afSArtur Paszkiewicz static void
7497a7ac2afSArtur Paszkiewicz _ftl_md_persist_entry(struct ftl_md_io_entry_ctx *ctx)
7507a7ac2afSArtur Paszkiewicz {
7517a7ac2afSArtur Paszkiewicz 	ctx->status = 0;
7527a7ac2afSArtur Paszkiewicz 	ctx->remaining = 1;
7537a7ac2afSArtur Paszkiewicz 
7547a7ac2afSArtur Paszkiewicz 	/* First execute an IO to the primary region */
7557a7ac2afSArtur Paszkiewicz 	ftl_md_persist_entry_primary(ctx);
7567a7ac2afSArtur Paszkiewicz }
7577a7ac2afSArtur Paszkiewicz 
7587a7ac2afSArtur Paszkiewicz void
7591f11e145SMateusz Kozlowski ftl_md_persist_entries(struct ftl_md *md, uint64_t start_entry, uint64_t num_entries, void *buffer,
7601f11e145SMateusz Kozlowski 		       void *vss_buffer, ftl_md_io_entry_cb cb, void *cb_arg,
7617a7ac2afSArtur Paszkiewicz 		       struct ftl_md_io_entry_ctx *ctx)
7627a7ac2afSArtur Paszkiewicz {
7637a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(0 == md->region->entry_size)) {
7647a7ac2afSArtur Paszkiewicz 		/* This MD has not been configured to support persist entry call */
7657a7ac2afSArtur Paszkiewicz 		ftl_abort();
7667a7ac2afSArtur Paszkiewicz 	}
7671f11e145SMateusz Kozlowski 	if (spdk_unlikely(start_entry + num_entries > md->region->num_entries)) {
76834edd9f1SKamil Godzwon 		/* Exceeding number of available entries */
7691f11e145SMateusz Kozlowski 		ftl_abort();
7701f11e145SMateusz Kozlowski 	}
7717a7ac2afSArtur Paszkiewicz 
7727a7ac2afSArtur Paszkiewicz 	/* Initialize persist entry context */
7737a7ac2afSArtur Paszkiewicz 	ctx->cb = cb;
7747a7ac2afSArtur Paszkiewicz 	ctx->cb_arg = cb_arg;
7757a7ac2afSArtur Paszkiewicz 	ctx->md = md;
7767a7ac2afSArtur Paszkiewicz 	ctx->start_entry = start_entry;
7777a7ac2afSArtur Paszkiewicz 	ctx->buffer = buffer;
7781f11e145SMateusz Kozlowski 	ctx->num_entries = num_entries;
7797a7ac2afSArtur Paszkiewicz 	ctx->vss_buffer = vss_buffer ? : md->entry_vss_dma_buf;
7807a7ac2afSArtur Paszkiewicz 
7817a7ac2afSArtur Paszkiewicz 	_ftl_md_persist_entry(ctx);
7827a7ac2afSArtur Paszkiewicz }
7837a7ac2afSArtur Paszkiewicz 
7847a7ac2afSArtur Paszkiewicz static void
7857a7ac2afSArtur Paszkiewicz read_entry_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
7867a7ac2afSArtur Paszkiewicz {
7877a7ac2afSArtur Paszkiewicz 	struct ftl_md_io_entry_ctx *ctx = cb_arg;
7887a7ac2afSArtur Paszkiewicz 	struct ftl_md *md = ctx->md;
7897a7ac2afSArtur Paszkiewicz 
7901790ee8aSArtur Paszkiewicz 	ftl_stats_bdev_io_completed(md->dev, get_bdev_io_ftl_stats_type(md->dev, bdev_io), bdev_io);
7911790ee8aSArtur Paszkiewicz 
7927a7ac2afSArtur Paszkiewicz 	spdk_bdev_free_io(bdev_io);
7937a7ac2afSArtur Paszkiewicz 
7947a7ac2afSArtur Paszkiewicz 	if (!success) {
7957a7ac2afSArtur Paszkiewicz 		if (has_mirror(md)) {
796522a0c82SLukasz Lasek 			md->mirror_enabled = true;
7977a7ac2afSArtur Paszkiewicz 
7987a7ac2afSArtur Paszkiewicz 			/* First read from the mirror */
799522a0c82SLukasz Lasek 			ftl_md_read_entry(ftl_md_get_mirror(md), ctx->start_entry, ctx->buffer,
800522a0c82SLukasz Lasek 					  ctx->vss_buffer,
8017a7ac2afSArtur Paszkiewicz 					  ctx->cb, ctx->cb_arg,
8027a7ac2afSArtur Paszkiewicz 					  ctx);
8037a7ac2afSArtur Paszkiewicz 			return;
8047a7ac2afSArtur Paszkiewicz 		} else {
8057a7ac2afSArtur Paszkiewicz 			ctx->status = -EIO;
8067a7ac2afSArtur Paszkiewicz 			goto finish_io;
8077a7ac2afSArtur Paszkiewicz 		}
8087a7ac2afSArtur Paszkiewicz 	}
8097a7ac2afSArtur Paszkiewicz 
8107a7ac2afSArtur Paszkiewicz finish_io:
8117a7ac2afSArtur Paszkiewicz 	ctx->cb(ctx->status, ctx->cb_arg);
8127a7ac2afSArtur Paszkiewicz }
8137a7ac2afSArtur Paszkiewicz 
8147a7ac2afSArtur Paszkiewicz static void
8157a7ac2afSArtur Paszkiewicz ftl_md_read_entry_read_blocks(struct ftl_md_io_entry_ctx *ctx, struct ftl_md *md,
8167a7ac2afSArtur Paszkiewicz 			      spdk_bdev_io_wait_cb retry_fn)
8177a7ac2afSArtur Paszkiewicz {
8187a7ac2afSArtur Paszkiewicz 	int rc;
8197a7ac2afSArtur Paszkiewicz 
8207a7ac2afSArtur Paszkiewicz 	rc = read_blocks(md->dev, md->region->bdev_desc, md->region->ioch,
8217a7ac2afSArtur Paszkiewicz 			 ctx->buffer, ctx->vss_buffer,
8227a7ac2afSArtur Paszkiewicz 			 persist_entry_lba(md, ctx->start_entry), md->region->entry_size,
8237a7ac2afSArtur Paszkiewicz 			 read_entry_cb, ctx);
8247a7ac2afSArtur Paszkiewicz 
8257a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(rc)) {
8267a7ac2afSArtur Paszkiewicz 		if (rc == -ENOMEM) {
8277a7ac2afSArtur Paszkiewicz 			struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(md->region->bdev_desc);
8287a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.bdev = bdev;
8297a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.cb_fn = retry_fn;
8307a7ac2afSArtur Paszkiewicz 			ctx->bdev_io_wait.cb_arg = ctx;
8317a7ac2afSArtur Paszkiewicz 			spdk_bdev_queue_io_wait(bdev, md->region->ioch, &ctx->bdev_io_wait);
8327a7ac2afSArtur Paszkiewicz 		} else {
8337a7ac2afSArtur Paszkiewicz 			ftl_abort();
8347a7ac2afSArtur Paszkiewicz 		}
8357a7ac2afSArtur Paszkiewicz 	}
8367a7ac2afSArtur Paszkiewicz }
8377a7ac2afSArtur Paszkiewicz 
8387a7ac2afSArtur Paszkiewicz static void
8397a7ac2afSArtur Paszkiewicz _ftl_md_read_entry(void *_ctx)
8407a7ac2afSArtur Paszkiewicz {
8417a7ac2afSArtur Paszkiewicz 	struct ftl_md_io_entry_ctx *ctx = _ctx;
8427a7ac2afSArtur Paszkiewicz 
8437a7ac2afSArtur Paszkiewicz 	ftl_md_read_entry_read_blocks(ctx, ctx->md, _ftl_md_read_entry);
8447a7ac2afSArtur Paszkiewicz }
8457a7ac2afSArtur Paszkiewicz 
8467a7ac2afSArtur Paszkiewicz void
8477a7ac2afSArtur Paszkiewicz ftl_md_read_entry(struct ftl_md *md, uint64_t start_entry, void *buffer, void *vss_buffer,
8487a7ac2afSArtur Paszkiewicz 		  ftl_md_io_entry_cb cb, void *cb_arg,
8497a7ac2afSArtur Paszkiewicz 		  struct ftl_md_io_entry_ctx *ctx)
8507a7ac2afSArtur Paszkiewicz {
8517a7ac2afSArtur Paszkiewicz 	if (spdk_unlikely(0 == md->region->entry_size)) {
8527a7ac2afSArtur Paszkiewicz 		/* This MD has not been configured to support read entry call */
8537a7ac2afSArtur Paszkiewicz 		ftl_abort();
8547a7ac2afSArtur Paszkiewicz 	}
8557a7ac2afSArtur Paszkiewicz 
8567a7ac2afSArtur Paszkiewicz 	ctx->cb = cb;
8577a7ac2afSArtur Paszkiewicz 	ctx->cb_arg = cb_arg;
8587a7ac2afSArtur Paszkiewicz 	ctx->md = md;
8597a7ac2afSArtur Paszkiewicz 	ctx->start_entry = start_entry;
8607a7ac2afSArtur Paszkiewicz 	ctx->buffer = buffer;
8617a7ac2afSArtur Paszkiewicz 	ctx->vss_buffer = vss_buffer;
8627a7ac2afSArtur Paszkiewicz 
8637a7ac2afSArtur Paszkiewicz 	_ftl_md_read_entry(ctx);
8647a7ac2afSArtur Paszkiewicz }
8657a7ac2afSArtur Paszkiewicz 
8667a7ac2afSArtur Paszkiewicz void
8677a7ac2afSArtur Paszkiewicz ftl_md_persist_entry_retry(struct ftl_md_io_entry_ctx *ctx)
8687a7ac2afSArtur Paszkiewicz {
8697a7ac2afSArtur Paszkiewicz 	_ftl_md_persist_entry(ctx);
8707a7ac2afSArtur Paszkiewicz }
8717a7ac2afSArtur Paszkiewicz 
8727a7ac2afSArtur Paszkiewicz static void
8737a7ac2afSArtur Paszkiewicz persist_mirror_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
8747a7ac2afSArtur Paszkiewicz {
8757a7ac2afSArtur Paszkiewicz 	struct ftl_md *primary = md->owner.private;
8767a7ac2afSArtur Paszkiewicz 
8777a7ac2afSArtur Paszkiewicz 	if (status) {
8787a7ac2afSArtur Paszkiewicz 		/* We got an error, stop persist procedure immediately */
8797a7ac2afSArtur Paszkiewicz 		primary->io.status = status;
8807a7ac2afSArtur Paszkiewicz 		io_done(primary);
8817a7ac2afSArtur Paszkiewicz 	} else {
8827a7ac2afSArtur Paszkiewicz 		/* Now continue the persist procedure on the primary MD object */
8837a7ac2afSArtur Paszkiewicz 		if (0 == io_init(primary, FTL_MD_OP_PERSIST)) {
8847a7ac2afSArtur Paszkiewicz 			io_submit(primary);
8857a7ac2afSArtur Paszkiewicz 		} else {
8867a7ac2afSArtur Paszkiewicz 			spdk_thread_send_msg(spdk_get_thread(), exception, primary);
8877a7ac2afSArtur Paszkiewicz 		}
8887a7ac2afSArtur Paszkiewicz 	}
8897a7ac2afSArtur Paszkiewicz }
8907a7ac2afSArtur Paszkiewicz 
8917a7ac2afSArtur Paszkiewicz void
8927a7ac2afSArtur Paszkiewicz ftl_md_persist(struct ftl_md *md)
8937a7ac2afSArtur Paszkiewicz {
8947a7ac2afSArtur Paszkiewicz 	if (has_mirror(md)) {
895522a0c82SLukasz Lasek 		struct ftl_md *md_mirror = ftl_md_get_mirror(md);
896522a0c82SLukasz Lasek 
897522a0c82SLukasz Lasek 		md->mirror_enabled = true;
8987a7ac2afSArtur Paszkiewicz 
8997a7ac2afSArtur Paszkiewicz 		/* Set callback and context in mirror */
900522a0c82SLukasz Lasek 		md_mirror->cb = persist_mirror_cb;
901522a0c82SLukasz Lasek 		md_mirror->owner.private = md;
9027a7ac2afSArtur Paszkiewicz 
9037a7ac2afSArtur Paszkiewicz 		/* First persist the mirror */
904522a0c82SLukasz Lasek 		ftl_md_persist(md_mirror);
9057a7ac2afSArtur Paszkiewicz 		return;
9067a7ac2afSArtur Paszkiewicz 	}
9077a7ac2afSArtur Paszkiewicz 
9087a7ac2afSArtur Paszkiewicz 	if (0 == io_init(md, FTL_MD_OP_PERSIST)) {
9097a7ac2afSArtur Paszkiewicz 		io_submit(md);
9107a7ac2afSArtur Paszkiewicz 	} else {
9117a7ac2afSArtur Paszkiewicz 		spdk_thread_send_msg(spdk_get_thread(), exception, md);
9127a7ac2afSArtur Paszkiewicz 	}
9137a7ac2afSArtur Paszkiewicz }
9147a7ac2afSArtur Paszkiewicz 
9157a7ac2afSArtur Paszkiewicz static void
9167a7ac2afSArtur Paszkiewicz restore_mirror_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
9177a7ac2afSArtur Paszkiewicz {
9187a7ac2afSArtur Paszkiewicz 	struct ftl_md *primary = md->owner.private;
9197a7ac2afSArtur Paszkiewicz 
9207a7ac2afSArtur Paszkiewicz 	if (status) {
9217a7ac2afSArtur Paszkiewicz 		/* Cannot restore the object from the mirror too, mark error and fail */
9227a7ac2afSArtur Paszkiewicz 		primary->io.status = -EIO;
9237a7ac2afSArtur Paszkiewicz 		io_done(primary);
9247a7ac2afSArtur Paszkiewicz 	} else {
9257a7ac2afSArtur Paszkiewicz 		/*
9267a7ac2afSArtur Paszkiewicz 		 * Restoring from the mirror successful. Synchronize mirror to the primary.
9277a7ac2afSArtur Paszkiewicz 		 * Because we read MD content from the mirror, we can disable it, only the primary
9287a7ac2afSArtur Paszkiewicz 		 * requires persisting.
9297a7ac2afSArtur Paszkiewicz 		 */
9307a7ac2afSArtur Paszkiewicz 		primary->io.status = 0;
9317a7ac2afSArtur Paszkiewicz 		primary->mirror_enabled = false;
9327a7ac2afSArtur Paszkiewicz 		io_cleanup(primary);
9337a7ac2afSArtur Paszkiewicz 		ftl_md_persist(primary);
9347a7ac2afSArtur Paszkiewicz 		primary->mirror_enabled = true;
9357a7ac2afSArtur Paszkiewicz 	}
9367a7ac2afSArtur Paszkiewicz }
9377a7ac2afSArtur Paszkiewicz 
938c6880a39SArtur Paszkiewicz static void
939c6880a39SArtur Paszkiewicz restore_sync_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
940c6880a39SArtur Paszkiewicz {
941c6880a39SArtur Paszkiewicz 	struct ftl_md *primary = md->owner.private;
942c6880a39SArtur Paszkiewicz 
943c6880a39SArtur Paszkiewicz 	if (status) {
944c6880a39SArtur Paszkiewicz 		/* Cannot sync the object from the primary to the mirror, mark error and fail */
945c6880a39SArtur Paszkiewicz 		primary->io.status = -EIO;
946c6880a39SArtur Paszkiewicz 		io_done(primary);
947c6880a39SArtur Paszkiewicz 	} else {
948c6880a39SArtur Paszkiewicz 		primary->cb(dev, primary, primary->io.status);
949c6880a39SArtur Paszkiewicz 		io_cleanup(primary);
950c6880a39SArtur Paszkiewicz 	}
951c6880a39SArtur Paszkiewicz }
952c6880a39SArtur Paszkiewicz 
9537a7ac2afSArtur Paszkiewicz static int
9547a7ac2afSArtur Paszkiewicz restore_done(struct ftl_md *md)
9557a7ac2afSArtur Paszkiewicz {
9567a7ac2afSArtur Paszkiewicz 	if (-EAGAIN == md->io.status) {
9577a7ac2afSArtur Paszkiewicz 		/* Failed to read MD from primary region, try it from mirror.
9587a7ac2afSArtur Paszkiewicz 		 * At the moment read the mirror entirely, (TODO) in the
9597a7ac2afSArtur Paszkiewicz 		 * feature we can restore from primary and mirror region
9607a7ac2afSArtur Paszkiewicz 		 * with finer granularity.
9617a7ac2afSArtur Paszkiewicz 		 */
9627a7ac2afSArtur Paszkiewicz 
9637a7ac2afSArtur Paszkiewicz 		if (has_mirror(md)) {
964522a0c82SLukasz Lasek 			struct ftl_md *md_mirror = ftl_md_get_mirror(md);
965522a0c82SLukasz Lasek 
966522a0c82SLukasz Lasek 			md->mirror_enabled = true;
9677a7ac2afSArtur Paszkiewicz 
9687a7ac2afSArtur Paszkiewicz 			/* Set callback and context in mirror */
969522a0c82SLukasz Lasek 			md_mirror->cb = restore_mirror_cb;
970522a0c82SLukasz Lasek 			md_mirror->owner.private = md;
9717a7ac2afSArtur Paszkiewicz 
9727a7ac2afSArtur Paszkiewicz 			/* First persist the mirror */
973522a0c82SLukasz Lasek 			ftl_md_restore(md_mirror);
9747a7ac2afSArtur Paszkiewicz 			return -EAGAIN;
9757a7ac2afSArtur Paszkiewicz 		} else {
9767a7ac2afSArtur Paszkiewicz 			return -EIO;
9777a7ac2afSArtur Paszkiewicz 		}
978c6880a39SArtur Paszkiewicz 	} else if (0 == md->io.status && false == md->dev->sb->clean) {
979c6880a39SArtur Paszkiewicz 		if (has_mirror(md)) {
980522a0c82SLukasz Lasek 			struct ftl_md *md_mirror = ftl_md_get_mirror(md);
981c6880a39SArtur Paszkiewicz 			/* There was a dirty shutdown, synchronize primary to mirror */
982c6880a39SArtur Paszkiewicz 
983c6880a39SArtur Paszkiewicz 			/* Set callback and context in the mirror */
984522a0c82SLukasz Lasek 			md_mirror->cb = restore_sync_cb;
985522a0c82SLukasz Lasek 			md_mirror->owner.private = md;
986c6880a39SArtur Paszkiewicz 
987c6880a39SArtur Paszkiewicz 			/* First persist the mirror */
988522a0c82SLukasz Lasek 			ftl_md_persist(md_mirror);
989c6880a39SArtur Paszkiewicz 			return -EAGAIN;
990c6880a39SArtur Paszkiewicz 		}
9917a7ac2afSArtur Paszkiewicz 	}
9927a7ac2afSArtur Paszkiewicz 
9937a7ac2afSArtur Paszkiewicz 	return md->io.status;
9947a7ac2afSArtur Paszkiewicz }
9957a7ac2afSArtur Paszkiewicz 
9967a7ac2afSArtur Paszkiewicz static void
9977a7ac2afSArtur Paszkiewicz io_done(struct ftl_md *md)
9987a7ac2afSArtur Paszkiewicz {
9997a7ac2afSArtur Paszkiewicz 	int status;
10007a7ac2afSArtur Paszkiewicz 
10017a7ac2afSArtur Paszkiewicz 	if (md->io.op == FTL_MD_OP_RESTORE) {
10027a7ac2afSArtur Paszkiewicz 		status = restore_done(md);
10037a7ac2afSArtur Paszkiewicz 	} else {
10047a7ac2afSArtur Paszkiewicz 		status = md->io.status;
10057a7ac2afSArtur Paszkiewicz 	}
10067a7ac2afSArtur Paszkiewicz 
10077a7ac2afSArtur Paszkiewicz 	if (status != -EAGAIN) {
10089f42898aSLukasz Lasek 		/* The MD instance may be destroyed in ctx of md->cb(), e.g. upon region upgrade. */
10099f42898aSLukasz Lasek 		/* Need to cleanup DMA bufs first. */
10107a7ac2afSArtur Paszkiewicz 		io_cleanup(md);
10119f42898aSLukasz Lasek 		md->cb(md->dev, md, status);
10127a7ac2afSArtur Paszkiewicz 	}
10137a7ac2afSArtur Paszkiewicz }
10147a7ac2afSArtur Paszkiewicz 
10157a7ac2afSArtur Paszkiewicz void
10167a7ac2afSArtur Paszkiewicz ftl_md_restore(struct ftl_md *md)
10177a7ac2afSArtur Paszkiewicz {
10187a7ac2afSArtur Paszkiewicz 	if (0 == io_init(md, FTL_MD_OP_RESTORE)) {
10197a7ac2afSArtur Paszkiewicz 		io_submit(md);
10207a7ac2afSArtur Paszkiewicz 	} else {
10217a7ac2afSArtur Paszkiewicz 		spdk_thread_send_msg(spdk_get_thread(), exception, md);
10227a7ac2afSArtur Paszkiewicz 	}
10237a7ac2afSArtur Paszkiewicz }
10247a7ac2afSArtur Paszkiewicz 
10257a7ac2afSArtur Paszkiewicz static int
10267a7ac2afSArtur Paszkiewicz pattern_prepare(struct ftl_md *md,
10277a7ac2afSArtur Paszkiewicz 		int data_pattern, union ftl_md_vss *vss_pattern)
10287a7ac2afSArtur Paszkiewicz {
10297a7ac2afSArtur Paszkiewicz 	void *data = md->io.data;
10307a7ac2afSArtur Paszkiewicz 	uint64_t data_size = xfer_size(md);
10317a7ac2afSArtur Paszkiewicz 
10327a7ac2afSArtur Paszkiewicz 	memset(data, data_pattern, data_size);
10337a7ac2afSArtur Paszkiewicz 
10347a7ac2afSArtur Paszkiewicz 	if (md->io.md) {
10357a7ac2afSArtur Paszkiewicz 		if (vss_pattern) {
10367a7ac2afSArtur Paszkiewicz 			/* store the VSS pattern... */
10377a7ac2afSArtur Paszkiewicz 			ftl_md_vss_buf_init(md->io.md, ftl_md_xfer_blocks(md->dev), vss_pattern);
10387a7ac2afSArtur Paszkiewicz 		} else {
10397a7ac2afSArtur Paszkiewicz 			/* ...or default init VSS to 0 */
10407a7ac2afSArtur Paszkiewicz 			union ftl_md_vss vss = {0};
10417a7ac2afSArtur Paszkiewicz 
10427a7ac2afSArtur Paszkiewicz 			vss.version.md_version = md->region->current.version;
10437a7ac2afSArtur Paszkiewicz 			ftl_md_vss_buf_init(md->io.md, ftl_md_xfer_blocks(md->dev), &vss);
10447a7ac2afSArtur Paszkiewicz 		}
10457a7ac2afSArtur Paszkiewicz 	}
10467a7ac2afSArtur Paszkiewicz 
10477a7ac2afSArtur Paszkiewicz 	return 0;
10487a7ac2afSArtur Paszkiewicz }
10497a7ac2afSArtur Paszkiewicz 
10507a7ac2afSArtur Paszkiewicz static void
10517a7ac2afSArtur Paszkiewicz clear_mirror_cb(struct spdk_ftl_dev *dev, struct ftl_md *secondary, int status)
10527a7ac2afSArtur Paszkiewicz {
10537a7ac2afSArtur Paszkiewicz 	struct ftl_md *primary = secondary->owner.private;
10547a7ac2afSArtur Paszkiewicz 
10557a7ac2afSArtur Paszkiewicz 	if (status) {
10567a7ac2afSArtur Paszkiewicz 		/* We got an error, stop persist procedure immediately */
10577a7ac2afSArtur Paszkiewicz 		primary->io.status = status;
10587a7ac2afSArtur Paszkiewicz 		io_done(primary);
10597a7ac2afSArtur Paszkiewicz 	} else {
10607a7ac2afSArtur Paszkiewicz 		/* Now continue the persist procedure on the primary MD object */
10617a7ac2afSArtur Paszkiewicz 		io_submit(primary);
10627a7ac2afSArtur Paszkiewicz 	}
10637a7ac2afSArtur Paszkiewicz }
10647a7ac2afSArtur Paszkiewicz 
10657a7ac2afSArtur Paszkiewicz void
10667a7ac2afSArtur Paszkiewicz ftl_md_clear(struct ftl_md *md, int data_pattern, union ftl_md_vss *vss_pattern)
10677a7ac2afSArtur Paszkiewicz {
10687a7ac2afSArtur Paszkiewicz 	if (has_mirror(md)) {
1069522a0c82SLukasz Lasek 		struct ftl_md *md_mirror = ftl_md_get_mirror(md);
1070522a0c82SLukasz Lasek 
1071522a0c82SLukasz Lasek 		md->mirror_enabled = true;
10727a7ac2afSArtur Paszkiewicz 
10737a7ac2afSArtur Paszkiewicz 		/* Set callback and context in mirror */
1074522a0c82SLukasz Lasek 		md_mirror->cb = clear_mirror_cb;
1075522a0c82SLukasz Lasek 		md_mirror->owner.private = md;
10767a7ac2afSArtur Paszkiewicz 
1077db085261SMateusz Kozlowski 		/* The pattern bufs will not be available outside of this fn context */
1078db085261SMateusz Kozlowski 		/* Configure the IO for the primary region now */
1079db085261SMateusz Kozlowski 		if (0 == io_init(md, FTL_MD_OP_CLEAR) && 0 == pattern_prepare(md, data_pattern, vss_pattern)) {
10807a7ac2afSArtur Paszkiewicz 			/* First persist the mirror */
1081522a0c82SLukasz Lasek 			ftl_md_clear(md_mirror, data_pattern, vss_pattern);
1082db085261SMateusz Kozlowski 		} else {
1083db085261SMateusz Kozlowski 			spdk_thread_send_msg(spdk_get_thread(), exception, md);
1084db085261SMateusz Kozlowski 		}
10857a7ac2afSArtur Paszkiewicz 		return;
10867a7ac2afSArtur Paszkiewicz 	}
10877a7ac2afSArtur Paszkiewicz 
10887a7ac2afSArtur Paszkiewicz 	if (0 == io_init(md, FTL_MD_OP_CLEAR) && 0 == pattern_prepare(md, data_pattern, vss_pattern)) {
10897a7ac2afSArtur Paszkiewicz 		io_submit(md);
10907a7ac2afSArtur Paszkiewicz 	} else {
10917a7ac2afSArtur Paszkiewicz 		spdk_thread_send_msg(spdk_get_thread(), exception, md);
10927a7ac2afSArtur Paszkiewicz 	}
10937a7ac2afSArtur Paszkiewicz }
10947a7ac2afSArtur Paszkiewicz 
10957a7ac2afSArtur Paszkiewicz const struct ftl_layout_region *
10967a7ac2afSArtur Paszkiewicz ftl_md_get_region(struct ftl_md *md)
10977a7ac2afSArtur Paszkiewicz {
10987a7ac2afSArtur Paszkiewicz 	return md->region;
10997a7ac2afSArtur Paszkiewicz }
11007a7ac2afSArtur Paszkiewicz 
1101522a0c82SLukasz Lasek void
11027a7ac2afSArtur Paszkiewicz ftl_md_set_region(struct ftl_md *md,
11037a7ac2afSArtur Paszkiewicz 		  const struct ftl_layout_region *region)
11047a7ac2afSArtur Paszkiewicz {
11057a7ac2afSArtur Paszkiewicz 	assert(region->current.blocks <= md->data_blocks);
11067a7ac2afSArtur Paszkiewicz 	md->region = region;
11077a7ac2afSArtur Paszkiewicz 
11087a7ac2afSArtur Paszkiewicz 	if (md->vss_data) {
11097a7ac2afSArtur Paszkiewicz 		union ftl_md_vss vss = {0};
11107a7ac2afSArtur Paszkiewicz 		vss.version.md_version = region->current.version;
11117a7ac2afSArtur Paszkiewicz 		ftl_md_vss_buf_init(md->vss_data, md->data_blocks, &vss);
11127a7ac2afSArtur Paszkiewicz 		if (region->entry_size) {
11137a7ac2afSArtur Paszkiewicz 			assert(md->entry_vss_dma_buf);
11147a7ac2afSArtur Paszkiewicz 			ftl_md_vss_buf_init(md->entry_vss_dma_buf, region->entry_size, &vss);
11157a7ac2afSArtur Paszkiewicz 		}
11167a7ac2afSArtur Paszkiewicz 	}
11177a7ac2afSArtur Paszkiewicz 
11187a7ac2afSArtur Paszkiewicz 	if (has_mirror(md)) {
1119522a0c82SLukasz Lasek 		md->mirror_enabled = true;
11207a7ac2afSArtur Paszkiewicz 	}
11217a7ac2afSArtur Paszkiewicz }
1122811a027eSKozlowski Mateusz 
1123811a027eSKozlowski Mateusz int
1124811a027eSKozlowski Mateusz ftl_md_create_region_flags(struct spdk_ftl_dev *dev, int region_type)
1125811a027eSKozlowski Mateusz {
1126811a027eSKozlowski Mateusz 	int flags = FTL_MD_CREATE_SHM;
1127811a027eSKozlowski Mateusz 
1128811a027eSKozlowski Mateusz 	switch (region_type) {
1129811a027eSKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_SB:
1130811a027eSKozlowski Mateusz 		if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
1131811a027eSKozlowski Mateusz 			flags |= FTL_MD_CREATE_SHM_NEW;
1132811a027eSKozlowski Mateusz 		}
1133811a027eSKozlowski Mateusz 		break;
1134811a027eSKozlowski Mateusz 
1135811a027eSKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_BAND_MD:
1136811a027eSKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_NVC_MD:
11371e904e2bSArtur Paszkiewicz 		if (!ftl_fast_startup(dev)) {
1138811a027eSKozlowski Mateusz 			flags |= FTL_MD_CREATE_SHM_NEW;
11391e904e2bSArtur Paszkiewicz 		}
1140811a027eSKozlowski Mateusz 		break;
1141cea8dadeSArtur Paszkiewicz 	case FTL_LAYOUT_REGION_TYPE_VALID_MAP:
114278c3cbf4SArtur Paszkiewicz 	case FTL_LAYOUT_REGION_TYPE_TRIM_MD:
11432d613454SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_TRIM_LOG:
1144cea8dadeSArtur Paszkiewicz 		if (!ftl_fast_startup(dev) && !ftl_fast_recovery(dev)) {
1145cea8dadeSArtur Paszkiewicz 			flags |= FTL_MD_CREATE_SHM_NEW;
1146cea8dadeSArtur Paszkiewicz 		}
1147cea8dadeSArtur Paszkiewicz 		break;
11484061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC:
11494061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT:
11504061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP:
11514061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT:
11524061ed11SMateusz Kozlowski 		return FTL_MD_CREATE_SPDK_BUF;
1153811a027eSKozlowski Mateusz 	default:
1154811a027eSKozlowski Mateusz 		return FTL_MD_CREATE_HEAP;
1155811a027eSKozlowski Mateusz 	}
1156811a027eSKozlowski Mateusz 
1157811a027eSKozlowski Mateusz 	return flags;
1158811a027eSKozlowski Mateusz }
1159811a027eSKozlowski Mateusz 
1160811a027eSKozlowski Mateusz int
11610e33da49SKozlowski Mateusz ftl_md_destroy_region_flags(struct spdk_ftl_dev *dev, int region_type)
11620e33da49SKozlowski Mateusz {
11630e33da49SKozlowski Mateusz 	switch (region_type) {
11640e33da49SKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_SB:
11650e33da49SKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_BAND_MD:
1166cea8dadeSArtur Paszkiewicz 	case FTL_LAYOUT_REGION_TYPE_VALID_MAP:
11670e33da49SKozlowski Mateusz 	case FTL_LAYOUT_REGION_TYPE_NVC_MD:
116878c3cbf4SArtur Paszkiewicz 	case FTL_LAYOUT_REGION_TYPE_TRIM_MD:
11692d613454SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_TRIM_LOG:
11700e33da49SKozlowski Mateusz 		if (dev->conf.fast_shutdown) {
11710e33da49SKozlowski Mateusz 			return FTL_MD_DESTROY_SHM_KEEP;
11720e33da49SKozlowski Mateusz 		}
11730e33da49SKozlowski Mateusz 		break;
11744061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC:
11754061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_GC_NEXT:
11764061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP:
11774061ed11SMateusz Kozlowski 	case FTL_LAYOUT_REGION_TYPE_P2L_CKPT_COMP_NEXT:
11784061ed11SMateusz Kozlowski 		return FTL_MD_DESTROY_SPDK_BUF;
11790e33da49SKozlowski Mateusz 	default:
11800e33da49SKozlowski Mateusz 		break;
11810e33da49SKozlowski Mateusz 	}
11820e33da49SKozlowski Mateusz 	return 0;
11830e33da49SKozlowski Mateusz }
11840e33da49SKozlowski Mateusz 
11850e33da49SKozlowski Mateusz int
1186811a027eSKozlowski Mateusz ftl_md_create_shm_flags(struct spdk_ftl_dev *dev)
1187811a027eSKozlowski Mateusz {
11881e904e2bSArtur Paszkiewicz 	int flags = FTL_MD_CREATE_SHM;
1189811a027eSKozlowski Mateusz 
11901e904e2bSArtur Paszkiewicz 	if (!ftl_fast_startup(dev) && !ftl_fast_recovery(dev)) {
11911e904e2bSArtur Paszkiewicz 		flags |= FTL_MD_CREATE_SHM_NEW;
11921e904e2bSArtur Paszkiewicz 	}
1193811a027eSKozlowski Mateusz 	return flags;
1194811a027eSKozlowski Mateusz }
11950e33da49SKozlowski Mateusz 
11960e33da49SKozlowski Mateusz int
11970e33da49SKozlowski Mateusz ftl_md_destroy_shm_flags(struct spdk_ftl_dev *dev)
11980e33da49SKozlowski Mateusz {
11990e33da49SKozlowski Mateusz 	return (dev->conf.fast_shutdown) ? FTL_MD_DESTROY_SHM_KEEP : 0;
12000e33da49SKozlowski Mateusz }
1201