xref: /spdk/module/bdev/ftl/bdev_ftl.c (revision 45a053c5777494f4e8ce4bc1191c9de3920377f7)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2020 Intel Corporation.
3  *   All rights reserved.
4  *   Copyright 2023 Solidigm All Rights Reserved
5  */
6 
7 #include "spdk/stdinc.h"
8 #include "spdk/bdev.h"
9 #include "spdk/env.h"
10 #include "spdk/thread.h"
11 #include "spdk/json.h"
12 #include "spdk/string.h"
13 #include "spdk/likely.h"
14 #include "spdk/util.h"
15 #include "spdk/string.h"
16 #include "spdk/ftl.h"
17 #include "spdk/log.h"
18 
19 #include "bdev_ftl.h"
20 
21 struct ftl_bdev {
22 	struct spdk_bdev	bdev;
23 	struct spdk_ftl_dev	*dev;
24 	ftl_bdev_init_fn	init_cb;
25 	void			*init_arg;
26 	int			rc;
27 	struct spdk_bdev_desc	*base_bdev_desc;
28 	struct spdk_bdev_desc	*cache_bdev_desc;
29 };
30 
31 struct bdev_ftl_action {
32 	struct spdk_bdev_desc	*ftl_bdev_desc;
33 	struct ftl_bdev		*ftl_bdev_dev;
34 	spdk_ftl_fn		cb_fn;
35 	void			*cb_arg;
36 	int			rc;
37 	size_t			ctx_size;
38 	char			ctx[0];
39 };
40 
41 struct ftl_deferred_init {
42 	struct spdk_ftl_conf		conf;
43 
44 	LIST_ENTRY(ftl_deferred_init)	entry;
45 };
46 
47 static LIST_HEAD(, ftl_deferred_init)	g_deferred_init = LIST_HEAD_INITIALIZER(g_deferred_init);
48 
49 static int bdev_ftl_initialize(void);
50 static void bdev_ftl_finish(void);
51 static void bdev_ftl_examine(struct spdk_bdev *bdev);
52 
53 static void bdev_ftl_action_finish_cb(void *cb_arg, int status);
54 static struct bdev_ftl_action *bdev_ftl_action_start(const char *bdev_name,
55 		size_t ctx_size, spdk_ftl_fn cb_fn, void *cb_arg);
56 static void bdev_ftl_action_finish(struct bdev_ftl_action *action);
57 static void *bdev_ftl_action_ctx(struct bdev_ftl_action *action, size_t size);
58 
59 static int
60 bdev_ftl_get_ctx_size(void)
61 {
62 	return spdk_ftl_io_size();
63 }
64 
65 static struct spdk_bdev_module g_ftl_if = {
66 	.name		= "ftl",
67 	.module_init	= bdev_ftl_initialize,
68 	.module_fini	= bdev_ftl_finish,
69 	.examine_disk	= bdev_ftl_examine,
70 	.get_ctx_size	= bdev_ftl_get_ctx_size,
71 };
72 
73 SPDK_BDEV_MODULE_REGISTER(ftl, &g_ftl_if)
74 
75 static void
76 bdev_ftl_free(struct ftl_bdev *ftl_bdev)
77 {
78 	spdk_bdev_close(ftl_bdev->base_bdev_desc);
79 	spdk_bdev_close(ftl_bdev->cache_bdev_desc);
80 	free(ftl_bdev->bdev.name);
81 	free(ftl_bdev);
82 }
83 
84 static void
85 bdev_ftl_dev_free_cb(void *ctx, int status)
86 {
87 	struct ftl_bdev *ftl_bdev = ctx;
88 
89 	spdk_bdev_destruct_done(&ftl_bdev->bdev, status);
90 	bdev_ftl_free(ftl_bdev);
91 }
92 
93 static int
94 bdev_ftl_destruct(void *ctx)
95 {
96 	struct ftl_bdev *ftl_bdev = ctx;
97 
98 	spdk_ftl_dev_free(ftl_bdev->dev, bdev_ftl_dev_free_cb, ftl_bdev);
99 
100 	/* return 1 to indicate that the destruction is asynchronous */
101 	return 1;
102 }
103 
104 static void
105 bdev_ftl_cb(void *arg, int rc)
106 {
107 	struct spdk_bdev_io *bdev_io = arg;
108 	enum spdk_bdev_io_status status;
109 
110 	switch (rc) {
111 	case 0:
112 		status = SPDK_BDEV_IO_STATUS_SUCCESS;
113 		break;
114 	case -EAGAIN:
115 	case -ENOMEM:
116 		status = SPDK_BDEV_IO_STATUS_NOMEM;
117 		break;
118 	default:
119 		status = SPDK_BDEV_IO_STATUS_FAILED;
120 		break;
121 	}
122 
123 	spdk_bdev_io_complete(bdev_io, status);
124 }
125 
126 static void
127 bdev_ftl_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
128 		    bool success)
129 {
130 	struct ftl_bdev *ftl_bdev;
131 	int rc;
132 
133 	ftl_bdev = bdev_io->bdev->ctxt;
134 
135 	if (!success) {
136 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
137 		return;
138 	}
139 
140 	rc = spdk_ftl_readv(ftl_bdev->dev, (struct ftl_io *)bdev_io->driver_ctx,
141 			    ch,
142 			    bdev_io->u.bdev.offset_blocks,
143 			    bdev_io->u.bdev.num_blocks,
144 			    bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt, bdev_ftl_cb, bdev_io);
145 
146 	if (spdk_unlikely(rc != 0)) {
147 		bdev_ftl_cb(bdev_io, rc);
148 	}
149 }
150 
151 static int
152 _bdev_ftl_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
153 {
154 	struct ftl_bdev *ftl_bdev = (struct ftl_bdev *)bdev_io->bdev->ctxt;
155 
156 	switch (bdev_io->type) {
157 	case SPDK_BDEV_IO_TYPE_READ:
158 		spdk_bdev_io_get_buf(bdev_io, bdev_ftl_get_buf_cb,
159 				     bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
160 		return 0;
161 
162 	case SPDK_BDEV_IO_TYPE_WRITE:
163 		return spdk_ftl_writev(ftl_bdev->dev, (struct ftl_io *)bdev_io->driver_ctx,
164 				       ch, bdev_io->u.bdev.offset_blocks,
165 				       bdev_io->u.bdev.num_blocks, bdev_io->u.bdev.iovs,
166 				       bdev_io->u.bdev.iovcnt, bdev_ftl_cb, bdev_io);
167 
168 	case SPDK_BDEV_IO_TYPE_UNMAP:
169 		return spdk_ftl_unmap(ftl_bdev->dev, (struct ftl_io *)bdev_io->driver_ctx,
170 				      ch, bdev_io->u.bdev.offset_blocks,
171 				      bdev_io->u.bdev.num_blocks, bdev_ftl_cb, bdev_io);
172 	case SPDK_BDEV_IO_TYPE_FLUSH:
173 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
174 		return 0;
175 	default:
176 		return -ENOTSUP;
177 	}
178 }
179 
180 static void
181 bdev_ftl_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
182 {
183 	int rc = _bdev_ftl_submit_request(ch, bdev_io);
184 
185 	if (spdk_unlikely(rc != 0)) {
186 		bdev_ftl_cb(bdev_io, rc);
187 	}
188 }
189 
190 static bool
191 bdev_ftl_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
192 {
193 	switch (io_type) {
194 	case SPDK_BDEV_IO_TYPE_READ:
195 	case SPDK_BDEV_IO_TYPE_WRITE:
196 	case SPDK_BDEV_IO_TYPE_FLUSH:
197 	case SPDK_BDEV_IO_TYPE_UNMAP:
198 		return true;
199 	default:
200 		return false;
201 	}
202 }
203 
204 static struct spdk_io_channel *
205 bdev_ftl_get_io_channel(void *ctx)
206 {
207 	struct ftl_bdev *ftl_bdev = ctx;
208 
209 	return spdk_ftl_get_io_channel(ftl_bdev->dev);
210 }
211 
212 static void
213 bdev_ftl_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
214 {
215 	struct ftl_bdev *ftl_bdev = bdev->ctxt;
216 	struct spdk_ftl_conf conf;
217 
218 	spdk_ftl_dev_get_conf(ftl_bdev->dev, &conf, sizeof(conf));
219 
220 	spdk_json_write_object_begin(w);
221 
222 	spdk_json_write_named_string(w, "method", "bdev_ftl_create");
223 
224 	spdk_json_write_named_object_begin(w, "params");
225 	spdk_json_write_named_string(w, "name", ftl_bdev->bdev.name);
226 
227 	spdk_json_write_named_uint64(w, "overprovisioning", conf.overprovisioning);
228 	spdk_json_write_named_uint64(w, "l2p_dram_limit", conf.l2p_dram_limit);
229 
230 	if (conf.core_mask) {
231 		spdk_json_write_named_string(w, "core_mask", conf.core_mask);
232 	}
233 
234 	spdk_json_write_named_uuid(w, "uuid", &conf.uuid);
235 
236 	spdk_json_write_named_bool(w, "fast_shutdown", conf.fast_shutdown);
237 
238 	spdk_json_write_named_string(w, "base_bdev", conf.base_bdev);
239 
240 	if (conf.cache_bdev) {
241 		spdk_json_write_named_string(w, "cache", conf.cache_bdev);
242 	}
243 
244 	spdk_json_write_object_end(w);
245 	spdk_json_write_object_end(w);
246 }
247 
248 static int
249 bdev_ftl_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
250 {
251 	struct ftl_bdev *ftl_bdev = ctx;
252 	struct spdk_ftl_attrs attrs;
253 	struct spdk_ftl_conf conf;
254 
255 	spdk_ftl_dev_get_attrs(ftl_bdev->dev, &attrs, sizeof(attrs));
256 	spdk_ftl_dev_get_conf(ftl_bdev->dev, &conf, sizeof(conf));
257 
258 	spdk_json_write_named_object_begin(w, "ftl");
259 
260 	spdk_json_write_named_string(w, "base_bdev", conf.base_bdev);
261 
262 	if (conf.cache_bdev) {
263 		spdk_json_write_named_string(w, "cache", conf.cache_bdev);
264 	}
265 
266 	/* ftl */
267 	spdk_json_write_object_end(w);
268 
269 	return 0;
270 }
271 
272 static const struct spdk_bdev_fn_table ftl_fn_table = {
273 	.destruct		= bdev_ftl_destruct,
274 	.submit_request		= bdev_ftl_submit_request,
275 	.io_type_supported	= bdev_ftl_io_type_supported,
276 	.get_io_channel		= bdev_ftl_get_io_channel,
277 	.write_config_json	= bdev_ftl_write_config_json,
278 	.dump_info_json		= bdev_ftl_dump_info_json,
279 };
280 
281 static void
282 bdev_ftl_create_err_complete(struct ftl_bdev *ftl_bdev)
283 {
284 	ftl_bdev_init_fn init_cb = ftl_bdev->init_cb;
285 	void *init_arg = ftl_bdev->init_arg;
286 	int rc = ftl_bdev->rc;
287 
288 	bdev_ftl_free(ftl_bdev);
289 
290 	assert(rc);
291 	init_cb(NULL, init_arg, rc);
292 }
293 
294 static void
295 bdev_ftl_create_err_cleanup_cb(void *ctx, int status)
296 {
297 	struct ftl_bdev *ftl_bdev = ctx;
298 
299 	if (status) {
300 		SPDK_ERRLOG("Fatal ERROR of FTL cleanup, name %s\n", ftl_bdev->bdev.name);
301 	}
302 
303 	bdev_ftl_create_err_complete(ftl_bdev);
304 }
305 
306 static void
307 bdev_ftl_create_cb(struct spdk_ftl_dev *dev, void *ctx, int status)
308 {
309 	struct ftl_bdev		*ftl_bdev = ctx;
310 	struct ftl_bdev_info	info = {};
311 	struct spdk_ftl_attrs	attrs;
312 	struct spdk_ftl_conf	conf;
313 	ftl_bdev_init_fn	init_cb = ftl_bdev->init_cb;
314 	void			*init_arg = ftl_bdev->init_arg;
315 
316 	if (status) {
317 		SPDK_ERRLOG("Failed to create FTL device (%d)\n", status);
318 		ftl_bdev->rc = status;
319 		goto error;
320 	}
321 
322 	spdk_ftl_dev_get_attrs(dev, &attrs, sizeof(attrs));
323 	spdk_ftl_dev_get_conf(dev, &conf, sizeof(conf));
324 
325 	ftl_bdev->dev = dev;
326 	ftl_bdev->bdev.product_name = "FTL disk";
327 	ftl_bdev->bdev.write_cache = 0;
328 	ftl_bdev->bdev.blocklen = attrs.block_size;
329 	ftl_bdev->bdev.blockcnt = attrs.num_blocks;
330 	ftl_bdev->bdev.uuid = conf.uuid;
331 	ftl_bdev->bdev.optimal_io_boundary = attrs.optimum_io_size;
332 	ftl_bdev->bdev.split_on_optimal_io_boundary = true;
333 
334 	SPDK_DEBUGLOG(bdev_ftl, "Creating bdev %s:\n", ftl_bdev->bdev.name);
335 	SPDK_DEBUGLOG(bdev_ftl, "\tblock_len:\t%zu\n", attrs.block_size);
336 	SPDK_DEBUGLOG(bdev_ftl, "\tnum_blocks:\t%"PRIu64"\n", attrs.num_blocks);
337 
338 	ftl_bdev->bdev.ctxt = ftl_bdev;
339 	ftl_bdev->bdev.fn_table = &ftl_fn_table;
340 	ftl_bdev->bdev.module = &g_ftl_if;
341 
342 	status = spdk_bdev_register(&ftl_bdev->bdev);
343 	if (status) {
344 		ftl_bdev->rc = status;
345 		goto error;
346 	}
347 
348 	info.name = ftl_bdev->bdev.name;
349 	info.uuid = ftl_bdev->bdev.uuid;
350 
351 	init_cb(&info, init_arg, 0);
352 	return;
353 
354 error:
355 	if (ftl_bdev->dev) {
356 		/* Cleanup all FTL */
357 		spdk_ftl_dev_set_fast_shutdown(ftl_bdev->dev, false);
358 
359 		/* FTL was created, but we have got an error, so we need to delete it */
360 		spdk_ftl_dev_free(dev, bdev_ftl_create_err_cleanup_cb, ftl_bdev);
361 	} else {
362 		bdev_ftl_create_err_complete(ftl_bdev);
363 	}
364 }
365 
366 static void
367 bdev_ftl_defer_free(struct ftl_deferred_init *init)
368 {
369 	spdk_ftl_conf_deinit(&init->conf);
370 	free(init);
371 }
372 
373 int
374 bdev_ftl_defer_init(const struct spdk_ftl_conf *conf)
375 {
376 	struct ftl_deferred_init *init;
377 	int rc;
378 
379 	init = calloc(1, sizeof(*init));
380 	if (!init) {
381 		return -ENOMEM;
382 	}
383 
384 	rc = spdk_ftl_conf_copy(&init->conf, conf);
385 	if (rc) {
386 		free(init);
387 		return -ENOMEM;
388 	}
389 
390 	LIST_INSERT_HEAD(&g_deferred_init, init, entry);
391 
392 	return 0;
393 }
394 
395 static void
396 bdev_ftl_create_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *ctx)
397 {
398 }
399 
400 int
401 bdev_ftl_create_bdev(const struct spdk_ftl_conf *conf, ftl_bdev_init_fn cb, void *cb_arg)
402 {
403 	struct ftl_bdev *ftl_bdev;
404 	struct spdk_bdev_desc *base_bdev_desc, *cache_bdev_desc;
405 	int rc;
406 
407 	rc = spdk_bdev_open_ext(conf->base_bdev, false, bdev_ftl_create_bdev_event_cb, NULL,
408 				&base_bdev_desc);
409 	if (rc) {
410 		return rc;
411 	}
412 	rc = spdk_bdev_open_ext(conf->cache_bdev, false, bdev_ftl_create_bdev_event_cb, NULL,
413 				&cache_bdev_desc);
414 	if (rc) {
415 		spdk_bdev_close(base_bdev_desc);
416 		return rc;
417 	}
418 
419 	ftl_bdev = calloc(1, sizeof(*ftl_bdev));
420 	if (!ftl_bdev) {
421 		SPDK_ERRLOG("Could not allocate ftl_bdev\n");
422 		spdk_bdev_close(base_bdev_desc);
423 		spdk_bdev_close(cache_bdev_desc);
424 		return -ENOMEM;
425 	}
426 
427 	ftl_bdev->base_bdev_desc = base_bdev_desc;
428 	ftl_bdev->cache_bdev_desc = cache_bdev_desc;
429 
430 	ftl_bdev->bdev.name = strdup(conf->name);
431 	if (!ftl_bdev->bdev.name) {
432 		rc = -ENOMEM;
433 		goto error;
434 	}
435 
436 	ftl_bdev->init_cb = cb;
437 	ftl_bdev->init_arg = cb_arg;
438 
439 	rc = spdk_ftl_dev_init(conf, bdev_ftl_create_cb, ftl_bdev);
440 	if (rc) {
441 		SPDK_ERRLOG("Could not create FTL device\n");
442 		goto error;
443 	}
444 
445 	return 0;
446 
447 error:
448 	bdev_ftl_free(ftl_bdev);
449 	return rc;
450 }
451 
452 static int
453 bdev_ftl_initialize(void)
454 {
455 	return spdk_ftl_init();
456 }
457 
458 static void
459 bdev_ftl_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *ctx)
460 {
461 }
462 
463 void
464 bdev_ftl_delete_bdev(const char *name, bool fast_shutdown, spdk_bdev_unregister_cb cb_fn,
465 		     void *cb_arg)
466 {
467 	struct spdk_bdev_desc	*ftl_bdev_desc;
468 	struct spdk_bdev *bdev;
469 	struct ftl_bdev *ftl;
470 	int rc;
471 
472 	rc = spdk_bdev_open_ext(name, false, bdev_ftl_event_cb, NULL, &ftl_bdev_desc);
473 
474 	if (rc) {
475 		goto not_found;
476 	}
477 
478 	bdev = spdk_bdev_desc_get_bdev(ftl_bdev_desc);
479 
480 	if (bdev->module != &g_ftl_if) {
481 		goto bdev_opened;
482 	}
483 
484 	ftl = bdev->ctxt;
485 	assert(ftl);
486 	spdk_ftl_dev_set_fast_shutdown(ftl->dev, fast_shutdown);
487 	spdk_bdev_close(ftl_bdev_desc);
488 
489 	rc = spdk_bdev_unregister_by_name(name, &g_ftl_if, cb_fn, cb_arg);
490 	if (rc) {
491 		cb_fn(cb_arg, rc);
492 	}
493 
494 	return;
495 bdev_opened:
496 	spdk_bdev_close(ftl_bdev_desc);
497 not_found:
498 	cb_fn(cb_arg, -ENODEV);
499 }
500 
501 void
502 bdev_ftl_unmap(const char *name, uint64_t lba, uint64_t num_blocks, spdk_ftl_fn cb_fn,
503 	       void *cb_arg)
504 {
505 	struct bdev_ftl_action *action;
506 
507 	action = bdev_ftl_action_start(name, 0, cb_fn, cb_arg);
508 	if (!action) {
509 		return;
510 	}
511 
512 	/* It's ok to pass NULL as IO channel - FTL will detect this and use it's internal IO channel for management operations */
513 	action->rc = spdk_ftl_unmap(action->ftl_bdev_dev->dev, NULL, NULL, lba, num_blocks,
514 				    bdev_ftl_action_finish_cb, action);
515 	if (action->rc) {
516 		bdev_ftl_action_finish(action);
517 	}
518 }
519 
520 static void
521 bdev_ftl_get_stats_cb(struct ftl_stats *stats, void *cb_arg)
522 {
523 	struct bdev_ftl_action *action = cb_arg;
524 
525 	bdev_ftl_action_finish(action);
526 }
527 
528 void
529 bdev_ftl_get_stats(const char *name, spdk_ftl_fn cb, struct rpc_ftl_stats_ctx *ftl_stats_ctx)
530 {
531 	struct bdev_ftl_action *action;
532 
533 	action = bdev_ftl_action_start(name, 0, cb, ftl_stats_ctx);
534 	if (!action) {
535 		return;
536 	}
537 	ftl_stats_ctx->ftl_bdev_desc = action->ftl_bdev_desc;
538 	action->rc = spdk_ftl_get_stats(action->ftl_bdev_dev->dev, &ftl_stats_ctx->ftl_stats,
539 					bdev_ftl_get_stats_cb, action);
540 	if (action->rc) {
541 		bdev_ftl_action_finish(action);
542 	}
543 }
544 
545 void
546 bdev_ftl_get_properties(const char *name, spdk_ftl_fn cb_fn, struct spdk_jsonrpc_request *request)
547 {
548 	struct bdev_ftl_action *action;
549 
550 	action = bdev_ftl_action_start(name, 0, cb_fn, request);
551 	if (!action) {
552 		return;
553 	}
554 
555 	action->rc = spdk_ftl_get_properties(action->ftl_bdev_dev->dev, request,
556 					     bdev_ftl_action_finish_cb, action);
557 	if (action->rc) {
558 		bdev_ftl_action_finish(action);
559 	}
560 }
561 
562 struct bdev_ftl_set_property_args {
563 	char *property;
564 	char *value;
565 };
566 
567 static void
568 bdev_ftl_set_property_cb(void *cb_arg, int status)
569 {
570 	struct bdev_ftl_action *action = cb_arg;
571 	struct bdev_ftl_set_property_args *args = bdev_ftl_action_ctx(action, sizeof(*args));
572 
573 	free(args->property);
574 	free(args->value);
575 	args->property = NULL;
576 	args->value = NULL;
577 	bdev_ftl_action_finish_cb(cb_arg, status);
578 }
579 
580 void
581 bdev_ftl_set_property(const char *name, const char *property, const char *value,
582 		      spdk_ftl_fn cb_fn, void *cb_arg)
583 {
584 	struct bdev_ftl_action *action;
585 	struct bdev_ftl_set_property_args *args;
586 
587 	action = bdev_ftl_action_start(name, sizeof(*args), cb_fn, cb_arg);
588 	if (!action) {
589 		return;
590 	}
591 
592 	args = bdev_ftl_action_ctx(action, sizeof(*args));
593 	args->property = strdup(property);
594 	args->value = strdup(value);
595 
596 	if (!args->property || !args->value) {
597 		free(args->property);
598 		free(args->value);
599 		action->rc = -ENOMEM;
600 		bdev_ftl_action_finish(action);
601 		return;
602 	}
603 
604 	action->rc = spdk_ftl_set_property(action->ftl_bdev_dev->dev,
605 					   args->property, args->value, strlen(args->value) + 1,
606 					   bdev_ftl_set_property_cb, action);
607 	if (action->rc) {
608 		bdev_ftl_action_finish(action);
609 	}
610 }
611 
612 static void
613 bdev_ftl_finish(void)
614 {
615 	spdk_ftl_fini();
616 }
617 
618 static void
619 bdev_ftl_create_deferred_cb(const struct ftl_bdev_info *info, void *ctx, int status)
620 {
621 	struct ftl_deferred_init *opts = ctx;
622 
623 	if (status) {
624 		SPDK_ERRLOG("Failed to initialize FTL bdev '%s'\n", opts->conf.name);
625 	}
626 
627 	bdev_ftl_defer_free(opts);
628 
629 	spdk_bdev_module_examine_done(&g_ftl_if);
630 }
631 
632 static void
633 bdev_ftl_examine(struct spdk_bdev *bdev)
634 {
635 	struct ftl_deferred_init *opts;
636 	int rc;
637 
638 	LIST_FOREACH(opts, &g_deferred_init, entry) {
639 		/* spdk_bdev_module_examine_done will be called by bdev_ftl_create_deferred_cb */
640 		rc = bdev_ftl_create_bdev(&opts->conf, bdev_ftl_create_deferred_cb, opts);
641 		if (rc == -ENODEV) {
642 			continue;
643 		}
644 
645 		LIST_REMOVE(opts, entry);
646 
647 		if (rc) {
648 			bdev_ftl_create_deferred_cb(NULL, opts, rc);
649 		}
650 		return;
651 	}
652 
653 	spdk_bdev_module_examine_done(&g_ftl_if);
654 }
655 
656 SPDK_LOG_REGISTER_COMPONENT(bdev_ftl)
657 
658 /*
659  * Generic function to execute an action on the FTL bdev
660  */
661 static void
662 bdev_ftl_action_finish(struct bdev_ftl_action *action)
663 {
664 	action->cb_fn(action->cb_arg, action->rc);
665 	if (action->ftl_bdev_desc) {
666 		spdk_bdev_close(action->ftl_bdev_desc);
667 	}
668 	free(action);
669 }
670 
671 static struct bdev_ftl_action *
672 bdev_ftl_action_start(const char *bdev_name, size_t ctx_size, spdk_ftl_fn cb_fn, void *cb_arg)
673 {
674 	struct spdk_bdev *bdev;
675 	struct bdev_ftl_action *action = calloc(1, sizeof(*action) + ctx_size);
676 
677 	if (NULL == action) {
678 		cb_fn(cb_arg, -ENOMEM);
679 		return NULL;
680 	}
681 	action->cb_arg = cb_arg;
682 	action->cb_fn = cb_fn;
683 	action->ctx_size = ctx_size;
684 
685 	action->rc = spdk_bdev_open_ext(bdev_name, false, bdev_ftl_event_cb, NULL, &action->ftl_bdev_desc);
686 	if (action->rc) {
687 		goto error;
688 	}
689 
690 	bdev = spdk_bdev_desc_get_bdev(action->ftl_bdev_desc);
691 	if (bdev->module != &g_ftl_if) {
692 		action->rc = -ENODEV;
693 		goto error;
694 	}
695 
696 	action->ftl_bdev_dev = bdev->ctxt;
697 	assert(action->ftl_bdev_dev);
698 
699 	return action;
700 error:
701 	bdev_ftl_action_finish(action);
702 	return NULL;
703 }
704 
705 static void
706 bdev_ftl_action_finish_cb(void *cb_arg, int status)
707 {
708 	struct bdev_ftl_action *action = cb_arg;
709 
710 	action->rc = status;
711 	bdev_ftl_action_finish(action);
712 }
713 
714 static void *
715 bdev_ftl_action_ctx(struct bdev_ftl_action *action, size_t size)
716 {
717 	assert(action->ctx_size);
718 	assert(size == action->ctx_size);
719 	return action->ctx;
720 }
721