Commit fc2dbeaa authored by Wojciech Malikowski's avatar Wojciech Malikowski Committed by Tomasz Zawadzki
Browse files

lib/ftl: Init/fini context moved from dev structure to init module



init_ctx and fini_ctx from dev structyre was redundant to initialization
module context. This patch merge those structures and keep initialization
and deinitialization context private for init module.

Change-Id: Ic94bc813112b265642933bc8183acb1961a0c01a
Signed-off-by: default avatarWojciech Malikowski <wojciech.malikowski@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/549


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarDarek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
parent 5780ad96
Loading
Loading
Loading
Loading
+6 −22
Original line number Diff line number Diff line
@@ -124,17 +124,6 @@ struct ftl_nv_cache {
	pthread_spinlock_t			lock;
};

struct ftl_init_context {
	/* User's callback */
	spdk_ftl_init_fn			cb_fn;
	/* Callback's argument */
	void					*cb_arg;
	/* Thread to call the callback on */
	struct spdk_thread			*thread;
	/* Poller to check if the device has been destroyed/initialized */
	struct spdk_poller			*poller;
};

struct spdk_ftl_dev {
	/* Device instance */
	struct spdk_uuid			uuid;
@@ -147,13 +136,8 @@ struct spdk_ftl_dev {
	int					initialized;
	/* Indicates the device is about to be stopped */
	int					halt;

	/* Status to return for halt completion callback */
	int					halt_complete_status;
	/* Initializaton context */
	struct ftl_init_context			init_ctx;
	/* Destruction context */
	struct ftl_init_context			fini_ctx;
	/* Indicates the device is about to start stopping - use to handle multiple stop request */
	bool					halt_started;

	/* Underlying device */
	struct spdk_bdev_desc			*base_bdev_desc;
@@ -256,7 +240,7 @@ struct ftl_media_event {
	struct spdk_bdev_media_event		event;
};

typedef void (*ftl_restore_fn)(struct spdk_ftl_dev *, struct ftl_restore *, int);
typedef void (*ftl_restore_fn)(struct ftl_restore *, int, void *cb_arg);

void	ftl_apply_limits(struct spdk_ftl_dev *dev);
void	ftl_io_read(struct ftl_io *io);
@@ -272,9 +256,9 @@ size_t ftl_tail_md_hdr_num_blocks(void);
size_t	ftl_vld_map_num_blocks(const struct spdk_ftl_dev *dev);
size_t	ftl_lba_map_num_blocks(const struct spdk_ftl_dev *dev);
size_t	ftl_head_md_num_blocks(const struct spdk_ftl_dev *dev);
int	ftl_restore_md(struct spdk_ftl_dev *dev, ftl_restore_fn cb);
int	ftl_restore_device(struct ftl_restore *restore, ftl_restore_fn cb);
void	ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb);
int	ftl_restore_md(struct spdk_ftl_dev *dev, ftl_restore_fn cb, void *cb_arg);
int	ftl_restore_device(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg);
void	ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg);
int	ftl_band_set_direct_access(struct ftl_band *band, bool access);
bool	ftl_addr_is_written(struct ftl_band *band, struct ftl_addr addr);
int	ftl_flush_active_bands(struct spdk_ftl_dev *dev, spdk_ftl_fn cb_fn, void *cb_arg);
+139 −113
Original line number Diff line number Diff line
@@ -60,12 +60,29 @@ static struct spdk_bdev_module g_ftl_bdev_module = {
	.name	= "ftl_lib",
};

typedef void (*spdk_ftl_init_fn)(struct spdk_ftl_dev *, void *, int);

struct ftl_dev_init_ctx {
	/* Owner */
	struct spdk_ftl_dev		*dev;
	/* Initial arguments */
	struct spdk_ftl_dev_init_opts	opts;
	/* IO channel for zone info retrieving */
	struct spdk_io_channel		*ioch;
	/* Buffer for reading zone info  */
	struct spdk_bdev_zone_info	info[FTL_ZONE_INFO_COUNT];
	/* Currently read zone */
	size_t				zone_id;
	/* User's callback */
	spdk_ftl_init_fn		cb_fn;
	/* Callback's argument */
	void				*cb_arg;
	/* Thread to call the callback on */
	struct spdk_thread		*thread;
	/* Poller to check if the device has been destroyed/initialized */
	struct spdk_poller		*poller;
	/* Status to return for halt completion callback */
	int				halt_complete_status;
};

static STAILQ_HEAD(, spdk_ftl_dev)	g_ftl_queue = STAILQ_HEAD_INITIALIZER(g_ftl_queue);
@@ -565,56 +582,65 @@ ftl_dev_l2p_alloc(struct spdk_ftl_dev *dev)
}

static void
ftl_call_init_complete_cb(void *_ctx)
ftl_dev_free_init_ctx(struct ftl_dev_init_ctx *init_ctx)
{
	struct ftl_init_context *ctx = _ctx;
	struct spdk_ftl_dev *dev = SPDK_CONTAINEROF(ctx, struct spdk_ftl_dev, init_ctx);
	if (!init_ctx) {
		return;
	}

	if (ctx->cb_fn != NULL) {
		ctx->cb_fn(dev, ctx->cb_arg, 0);
	if (init_ctx->ioch) {
		spdk_put_io_channel(init_ctx->ioch);
	}

	free(init_ctx);
}

static void
ftl_init_complete(struct spdk_ftl_dev *dev)
ftl_call_init_complete_cb(void *ctx)
{
	struct ftl_dev_init_ctx *init_ctx = ctx;
	struct spdk_ftl_dev *dev = init_ctx->dev;

	if (init_ctx->cb_fn != NULL) {
		init_ctx->cb_fn(dev, init_ctx->cb_arg, 0);
	}

	ftl_dev_free_init_ctx(init_ctx);
}

static void
ftl_init_complete(struct ftl_dev_init_ctx *init_ctx)
{
	struct spdk_ftl_dev *dev = init_ctx->dev;

	pthread_mutex_lock(&g_ftl_queue_lock);
	STAILQ_INSERT_HEAD(&g_ftl_queue, dev, stailq);
	pthread_mutex_unlock(&g_ftl_queue_lock);

	dev->initialized = 1;

	spdk_thread_send_msg(dev->init_ctx.thread, ftl_call_init_complete_cb, &dev->init_ctx);
	spdk_thread_send_msg(init_ctx->thread, ftl_call_init_complete_cb, init_ctx);
}

static void
ftl_init_fail_cb(struct spdk_ftl_dev *dev, void *_ctx, int status)
ftl_init_fail_cb(struct spdk_ftl_dev *dev, void *ctx, int status)
{
	struct ftl_init_context *ctx = _ctx;
	struct ftl_dev_init_ctx *init_ctx = ctx;

	if (ctx->cb_fn != NULL) {
		ctx->cb_fn(NULL, ctx->cb_arg, -ENODEV);
	if (init_ctx->cb_fn != NULL) {
		init_ctx->cb_fn(NULL, init_ctx->cb_arg, -ENODEV);
	}

	free(ctx);
	ftl_dev_free_init_ctx(init_ctx);
}

static int _spdk_ftl_dev_free(struct spdk_ftl_dev *dev, spdk_ftl_init_fn cb_fn, void *cb_arg,
			      struct spdk_thread *thread);

static void
ftl_init_fail(struct spdk_ftl_dev *dev)
ftl_init_fail(struct ftl_dev_init_ctx *init_ctx)
{
	struct ftl_init_context *ctx;

	ctx = malloc(sizeof(*ctx));
	if (!ctx) {
		SPDK_ERRLOG("Unable to allocate context to free the device\n");
		return;
	}

	*ctx = dev->init_ctx;
	if (_spdk_ftl_dev_free(dev, ftl_init_fail_cb, ctx, ctx->thread)) {
	if (_spdk_ftl_dev_free(init_ctx->dev, ftl_init_fail_cb, init_ctx, init_ctx->thread)) {
		SPDK_ERRLOG("Unable to free the device\n");
		assert(0);
	}
@@ -623,57 +649,61 @@ ftl_init_fail(struct spdk_ftl_dev *dev)
static void
ftl_write_nv_cache_md_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
	struct spdk_ftl_dev *dev = cb_arg;
	struct ftl_dev_init_ctx *init_ctx = cb_arg;
	struct spdk_ftl_dev *dev = init_ctx->dev;

	spdk_bdev_free_io(bdev_io);
	if (spdk_unlikely(!success)) {
		SPDK_ERRLOG("Writing non-volatile cache's metadata header failed\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

	dev->nv_cache.ready = true;
	ftl_init_complete(dev);
	ftl_init_complete(init_ctx);
}

static void
ftl_clear_nv_cache_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
	struct spdk_ftl_dev *dev = cb_arg;
	struct ftl_dev_init_ctx *init_ctx = cb_arg;
	struct spdk_ftl_dev *dev = init_ctx->dev;
	struct ftl_nv_cache *nv_cache = &dev->nv_cache;

	spdk_bdev_free_io(bdev_io);
	if (spdk_unlikely(!success)) {
		SPDK_ERRLOG("Unable to clear the non-volatile cache bdev\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

	nv_cache->phase = 1;
	if (ftl_nv_cache_write_header(nv_cache, false, ftl_write_nv_cache_md_cb, dev)) {
	if (ftl_nv_cache_write_header(nv_cache, false, ftl_write_nv_cache_md_cb, init_ctx)) {
		SPDK_ERRLOG("Unable to write non-volatile cache metadata header\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
	}
}

static void
_ftl_nv_cache_scrub(void *ctx)
{
	struct spdk_ftl_dev *dev = ctx;
	struct ftl_dev_init_ctx *init_ctx = ctx;
	struct spdk_ftl_dev *dev = init_ctx->dev;
	int rc;

	rc = ftl_nv_cache_scrub(&dev->nv_cache, ftl_clear_nv_cache_cb, dev);
	rc = ftl_nv_cache_scrub(&dev->nv_cache, ftl_clear_nv_cache_cb, init_ctx);

	if (spdk_unlikely(rc != 0)) {
		SPDK_ERRLOG("Unable to clear the non-volatile cache bdev: %s\n",
			    spdk_strerror(-rc));
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
	}
}

static int
ftl_setup_initial_state(struct spdk_ftl_dev *dev)
ftl_setup_initial_state(struct ftl_dev_init_ctx *init_ctx)
{
	struct spdk_ftl_dev *dev = init_ctx->dev;
	struct spdk_ftl_conf *conf = &dev->conf;
	size_t i;

@@ -697,79 +727,88 @@ ftl_setup_initial_state(struct spdk_ftl_dev *dev)
	}

	if (!ftl_dev_has_nv_cache(dev)) {
		ftl_init_complete(dev);
		ftl_init_complete(init_ctx);
	} else {
		spdk_thread_send_msg(ftl_get_core_thread(dev), _ftl_nv_cache_scrub, dev);
		spdk_thread_send_msg(ftl_get_core_thread(dev), _ftl_nv_cache_scrub, init_ctx);
	}

	return 0;
}

static void
ftl_restore_nv_cache_cb(struct spdk_ftl_dev *dev, struct ftl_restore *restore, int status)
ftl_restore_nv_cache_cb(struct ftl_restore *restore, int status, void *cb_arg)
{
	struct ftl_dev_init_ctx *init_ctx = cb_arg;

	if (spdk_unlikely(status != 0)) {
		SPDK_ERRLOG("Failed to restore the non-volatile cache state\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

	ftl_init_complete(dev);
	ftl_init_complete(init_ctx);
}

static void
ftl_restore_device_cb(struct spdk_ftl_dev *dev, struct ftl_restore *restore, int status)
ftl_restore_device_cb(struct ftl_restore *restore, int status, void *cb_arg)
{
	struct ftl_dev_init_ctx *init_ctx = cb_arg;
	struct spdk_ftl_dev *dev = init_ctx->dev;

	if (status) {
		SPDK_ERRLOG("Failed to restore the device from the SSD\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

	if (ftl_init_bands_state(dev)) {
		SPDK_ERRLOG("Unable to finish the initialization\n");
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

	if (!ftl_dev_has_nv_cache(dev)) {
		ftl_init_complete(dev);
		ftl_init_complete(init_ctx);
		return;
	}

	ftl_restore_nv_cache(restore, ftl_restore_nv_cache_cb);
	ftl_restore_nv_cache(restore, ftl_restore_nv_cache_cb, init_ctx);
}

static void
ftl_restore_md_cb(struct spdk_ftl_dev *dev, struct ftl_restore *restore, int status)
ftl_restore_md_cb(struct ftl_restore *restore, int status, void *cb_arg)
{
	struct ftl_dev_init_ctx *init_ctx = cb_arg;

	if (status) {
		SPDK_ERRLOG("Failed to restore the metadata from the SSD\n");
		goto error;
	}

	/* After the metadata is read it should be possible to allocate the L2P */
	if (ftl_dev_l2p_alloc(dev)) {
	if (ftl_dev_l2p_alloc(init_ctx->dev)) {
		SPDK_ERRLOG("Failed to allocate the L2P\n");
		goto error;
	}

	if (ftl_restore_device(restore, ftl_restore_device_cb)) {
	if (ftl_restore_device(restore, ftl_restore_device_cb, init_ctx)) {
		SPDK_ERRLOG("Failed to start device restoration from the SSD\n");
		goto error;
	}

	return;
error:
	ftl_init_fail(dev);
	ftl_init_fail(init_ctx);
}

static int
ftl_restore_state(struct spdk_ftl_dev *dev, struct spdk_uuid uuid)
ftl_restore_state(struct ftl_dev_init_ctx *init_ctx)
{
	dev->uuid = uuid;
	struct spdk_ftl_dev *dev = init_ctx->dev;

	if (ftl_restore_md(dev, ftl_restore_md_cb)) {
	dev->uuid = init_ctx->opts.uuid;

	if (ftl_restore_md(dev, ftl_restore_md_cb, init_ctx)) {
		SPDK_ERRLOG("Failed to start metadata restoration from the SSD\n");
		return -1;
	}
@@ -798,20 +837,6 @@ ftl_dev_update_bands(struct spdk_ftl_dev *dev)
	}
}

static void
ftl_dev_free_init_ctx(struct ftl_dev_init_ctx *init_ctx)
{
	if (!init_ctx) {
		return;
	}

	if (init_ctx->ioch) {
		spdk_put_io_channel(init_ctx->ioch);
	}

	free(init_ctx);
}

static void
ftl_dev_init_state(struct ftl_dev_init_ctx *init_ctx)
{
@@ -821,26 +846,23 @@ ftl_dev_init_state(struct ftl_dev_init_ctx *init_ctx)

	if (ftl_dev_init_core_thread(dev, &init_ctx->opts)) {
		SPDK_ERRLOG("Unable to initialize device thread\n");
		goto fail;
		ftl_init_fail(init_ctx);
		return;
	}

	if (init_ctx->opts.mode & SPDK_FTL_MODE_CREATE) {
		if (ftl_setup_initial_state(dev)) {
		if (ftl_setup_initial_state(init_ctx)) {
			SPDK_ERRLOG("Failed to setup initial state of the device\n");
			goto fail;
			ftl_init_fail(init_ctx);
			return;
		}
	} else {
		if (ftl_restore_state(dev, init_ctx->opts.uuid)) {
		if (ftl_restore_state(init_ctx)) {
			SPDK_ERRLOG("Unable to restore device's state from the SSD\n");
			goto fail;
			ftl_init_fail(init_ctx);
			return;
		}
	}

	ftl_dev_free_init_ctx(init_ctx);
	return;
fail:
	ftl_dev_free_init_ctx(init_ctx);
	ftl_init_fail(dev);
}

static void ftl_dev_get_zone_info(struct ftl_dev_init_ctx *init_ctx);
@@ -859,8 +881,7 @@ ftl_dev_get_zone_info_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_ar

	if (spdk_unlikely(!success)) {
		SPDK_ERRLOG("Unable to read zone info for zone id: %"PRIu64"\n", init_ctx->zone_id);
		ftl_dev_free_init_ctx(init_ctx);
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
		return;
	}

@@ -912,8 +933,7 @@ ftl_dev_get_zone_info(struct ftl_dev_init_ctx *init_ctx)

	if (spdk_unlikely(rc != 0)) {
		SPDK_ERRLOG("Unable to read zone info for zone id: %"PRIu64"\n", init_ctx->zone_id);
		ftl_dev_free_init_ctx(init_ctx);
		ftl_init_fail(dev);
		ftl_init_fail(init_ctx);
	}
}

@@ -1167,6 +1187,9 @@ spdk_ftl_dev_init(const struct spdk_ftl_dev_init_opts *_opts, spdk_ftl_init_fn c

	init_ctx->dev = dev;
	init_ctx->opts = *_opts;
	init_ctx->cb_fn = cb_fn;
	init_ctx->cb_arg = cb_arg;
	init_ctx->thread = spdk_get_thread();

	if (!opts.conf) {
		opts.conf = &g_default_conf;
@@ -1180,9 +1203,6 @@ spdk_ftl_dev_init(const struct spdk_ftl_dev_init_opts *_opts, spdk_ftl_init_fn c

	TAILQ_INIT(&dev->retry_queue);
	dev->conf = *opts.conf;
	dev->init_ctx.cb_fn = cb_fn;
	dev->init_ctx.cb_arg = cb_arg;
	dev->init_ctx.thread = spdk_get_thread();
	dev->limit = SPDK_FTL_LIMIT_MAX;

	dev->name = strdup(opts.name);
@@ -1253,8 +1273,7 @@ fail_sync:
	ftl_dev_free_init_ctx(init_ctx);
	return rc;
fail_async:
	ftl_init_fail(dev);
	ftl_dev_free_init_ctx(init_ctx);
	ftl_init_fail(init_ctx);
	return 0;
}

@@ -1265,28 +1284,22 @@ _ftl_halt_defrag(void *arg)
}

static void
ftl_call_fini_complete(struct spdk_ftl_dev *dev, int status)
ftl_halt_complete_cb(void *ctx)
{
	struct ftl_init_context ctx = dev->fini_ctx;
	struct ftl_dev_init_ctx *fini_ctx = ctx;

	ftl_dev_free_sync(dev);
	if (ctx.cb_fn != NULL) {
		ctx.cb_fn(NULL, ctx.cb_arg, status);
	}
	ftl_dev_free_sync(fini_ctx->dev);
	if (fini_ctx->cb_fn != NULL) {
		fini_ctx->cb_fn(NULL, fini_ctx->cb_arg, fini_ctx->halt_complete_status);
	}

static void
ftl_halt_complete_cb(void *ctx)
{
	struct spdk_ftl_dev *dev = ctx;

	ftl_call_fini_complete(dev, dev->halt_complete_status);
	ftl_dev_free_init_ctx(fini_ctx);
}

static void
ftl_nv_cache_header_fini_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
	struct spdk_ftl_dev *dev = cb_arg;
	struct ftl_dev_init_ctx *fini_ctx = cb_arg;
	int rc = 0;

	spdk_bdev_free_io(bdev_io);
@@ -1295,23 +1308,25 @@ ftl_nv_cache_header_fini_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb
		rc = -EIO;
	}

	dev->halt_complete_status = rc;
	spdk_thread_send_msg(dev->fini_ctx.thread, ftl_halt_complete_cb, dev);
	fini_ctx->halt_complete_status = rc;
	spdk_thread_send_msg(fini_ctx->thread, ftl_halt_complete_cb, fini_ctx);
}

static int
ftl_halt_poller(void *ctx)
{
	struct spdk_ftl_dev *dev = ctx;
	struct ftl_dev_init_ctx *fini_ctx = ctx;
	struct spdk_ftl_dev *dev = fini_ctx->dev;

	if (!dev->core_thread.poller) {
		spdk_poller_unregister(&dev->fini_ctx.poller);
		spdk_poller_unregister(&fini_ctx->poller);

		if (ftl_dev_has_nv_cache(dev)) {
			ftl_nv_cache_write_header(&dev->nv_cache, true, ftl_nv_cache_header_fini_cb, dev);
			ftl_nv_cache_write_header(&dev->nv_cache, true,
						  ftl_nv_cache_header_fini_cb, fini_ctx);
		} else {
			dev->halt_complete_status = 0;
			spdk_thread_send_msg(dev->fini_ctx.thread, ftl_halt_complete_cb, dev);
			fini_ctx->halt_complete_status = 0;
			spdk_thread_send_msg(fini_ctx->thread, ftl_halt_complete_cb, fini_ctx);
		}
	}

@@ -1321,30 +1336,41 @@ ftl_halt_poller(void *ctx)
static void
ftl_add_halt_poller(void *ctx)
{
	struct spdk_ftl_dev *dev = ctx;
	struct ftl_dev_init_ctx *fini_ctx = ctx;
	struct spdk_ftl_dev *dev = fini_ctx->dev;

	dev->halt = 1;

	_ftl_halt_defrag(dev);

	assert(!dev->fini_ctx.poller);
	dev->fini_ctx.poller = spdk_poller_register(ftl_halt_poller, dev, 100);
	assert(!fini_ctx->poller);
	fini_ctx->poller = spdk_poller_register(ftl_halt_poller, fini_ctx, 100);
}

static int
_spdk_ftl_dev_free(struct spdk_ftl_dev *dev, spdk_ftl_init_fn cb_fn, void *cb_arg,
		   struct spdk_thread *thread)
{
	if (dev->fini_ctx.cb_fn != NULL) {
	struct ftl_dev_init_ctx *fini_ctx;

	if (dev->halt_started) {
		dev->halt_started = true;
		return -EBUSY;
	}

	dev->fini_ctx.cb_fn = cb_fn;
	dev->fini_ctx.cb_arg = cb_arg;
	dev->fini_ctx.thread = thread;
	fini_ctx = calloc(1, sizeof(*fini_ctx));
	if (!fini_ctx) {
		return -ENOMEM;
	}

	fini_ctx->dev = dev;
	fini_ctx->cb_fn = cb_fn;
	fini_ctx->cb_arg = cb_arg;
	fini_ctx->thread = thread;

	ftl_rwb_disable_interleaving(dev->rwb);

	spdk_thread_send_msg(ftl_get_core_thread(dev), ftl_add_halt_poller, dev);
	spdk_thread_send_msg(ftl_get_core_thread(dev), ftl_add_halt_poller, fini_ctx);
	return 0;
}

+11 −6
Original line number Diff line number Diff line
@@ -114,6 +114,8 @@ struct ftl_restore {
	struct spdk_ftl_dev		*dev;
	/* Completion callback (called for each phase of the restoration) */
	ftl_restore_fn			cb;
	/* Completion callback context */
	void				*cb_arg;
	/* Number of inflight IOs */
	unsigned int			num_ios;
	/* Current band number (index in the below bands array) */
@@ -160,7 +162,7 @@ ftl_restore_free(struct ftl_restore *restore)
}

static struct ftl_restore *
ftl_restore_init(struct spdk_ftl_dev *dev, ftl_restore_fn cb)
ftl_restore_init(struct spdk_ftl_dev *dev, ftl_restore_fn cb, void *cb_arg)
{
	struct ftl_restore *restore;
	struct ftl_restore_band *rband;
@@ -173,6 +175,7 @@ ftl_restore_init(struct spdk_ftl_dev *dev, ftl_restore_fn cb)

	restore->dev = dev;
	restore->cb = cb;
	restore->cb_arg = cb_arg;
	restore->final_phase = false;

	restore->bands = calloc(ftl_get_num_bands(dev), sizeof(*restore->bands));
@@ -208,7 +211,7 @@ ftl_restore_complete(struct ftl_restore *restore, int status)
	struct ftl_restore *ctx = status ? NULL : restore;
	bool final_phase = restore->final_phase;

	restore->cb(restore->dev, ctx, status);
	restore->cb(ctx, status, restore->cb_arg);
	if (status || final_phase) {
		ftl_restore_free(restore);
	}
@@ -365,11 +368,11 @@ ftl_restore_head_md(void *ctx)
}

int
ftl_restore_md(struct spdk_ftl_dev *dev, ftl_restore_fn cb)
ftl_restore_md(struct spdk_ftl_dev *dev, ftl_restore_fn cb, void *cb_arg)
{
	struct ftl_restore *restore;

	restore = ftl_restore_init(dev, cb);
	restore = ftl_restore_init(dev, cb, cb_arg);
	if (!restore) {
		return -ENOMEM;
	}
@@ -1012,7 +1015,7 @@ out:
}

void
ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb)
ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg)
{
	struct spdk_ftl_dev *dev = restore->dev;
	struct spdk_bdev *bdev;
@@ -1032,6 +1035,7 @@ ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb)

	restore->final_phase = true;
	restore->cb = cb;
	restore->cb_arg = cb_arg;

	for (i = 0; i < FTL_NV_CACHE_RESTORE_DEPTH; ++i) {
		block = &nvc_restore->block[i];
@@ -1318,13 +1322,14 @@ ftl_restore_tail_md(struct ftl_restore_band *rband)
}

int
ftl_restore_device(struct ftl_restore *restore, ftl_restore_fn cb)
ftl_restore_device(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg)
{
	struct spdk_ftl_dev *dev = restore->dev;
	struct ftl_restore_band *rband;

	restore->current = 0;
	restore->cb = cb;
	restore->cb_arg = cb_arg;
	restore->final_phase = dev->nv_cache.bdev_desc == NULL;

	/* If restore_device is called, there must be at least one valid band */