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