/* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2018 Intel Corporation. * All rights reserved. */ #include "spdk/stdinc.h" #include "spdk/nvme.h" #include "spdk/thread.h" #include "spdk/string.h" #include "spdk/likely.h" #include "spdk/ftl.h" #include "spdk/likely.h" #include "spdk/string.h" #include "spdk/bdev_module.h" #include "spdk/config.h" #include "ftl_core.h" #include "ftl_io.h" #include "ftl_band.h" #include "ftl_debug.h" #include "ftl_nv_cache.h" #include "ftl_writer.h" #include "ftl_utils.h" #include "mngt/ftl_mngt.h" struct ftl_dev_init_ctx { spdk_ftl_init_fn cb_fn; /* Callback's argument */ void *cb_arg; }; struct ftl_dev_free_ctx { spdk_ftl_fn cb_fn; /* Callback's argument */ void *cb_arg; }; static int init_core_thread(struct spdk_ftl_dev *dev) { struct spdk_cpuset cpumask = {}; /* * If core mask is provided create core thread on first cpu that match with the mask, * otherwise use current user thread */ if (dev->conf.core_mask) { if (spdk_cpuset_parse(&cpumask, dev->conf.core_mask)) { return -EINVAL; } dev->core_thread = spdk_thread_create("ftl_core_thread", &cpumask); } else { dev->core_thread = spdk_get_thread(); } if (dev->core_thread == NULL) { FTL_ERRLOG(dev, "Cannot create thread for mask %s\n", dev->conf.core_mask); return -ENOMEM; } return 0; } static void exit_thread(void *ctx) { struct spdk_thread *thread = ctx; spdk_thread_exit(thread); } static void deinit_core_thread(struct spdk_ftl_dev *dev) { if (dev->core_thread && dev->conf.core_mask) { spdk_thread_send_msg(dev->core_thread, exit_thread, dev->core_thread); dev->core_thread = NULL; } } static void free_dev(struct spdk_ftl_dev *dev) { if (!dev) { return; } deinit_core_thread(dev); spdk_ftl_conf_deinit(&dev->conf); free(dev); } static struct spdk_ftl_dev * allocate_dev(const struct spdk_ftl_conf *conf, int *error) { int rc; struct spdk_ftl_dev *dev = calloc(1, sizeof(*dev)); if (!dev) { FTL_ERRLOG(dev, "Cannot allocate FTL device\n"); *error = -ENOMEM; return NULL; } rc = ftl_conf_init_dev(dev, conf); if (rc) { *error = rc; goto error; } rc = init_core_thread(dev); if (rc) { *error = rc; goto error; } TAILQ_INIT(&dev->rd_sq); TAILQ_INIT(&dev->wr_sq); TAILQ_INIT(&dev->unmap_sq); TAILQ_INIT(&dev->ioch_queue); ftl_writer_init(dev, &dev->writer_user, SPDK_FTL_LIMIT_HIGH, FTL_BAND_TYPE_COMPACTION); ftl_writer_init(dev, &dev->writer_gc, SPDK_FTL_LIMIT_CRIT, FTL_BAND_TYPE_GC); return dev; error: free_dev(dev); return NULL; } static void dev_init_cb(struct spdk_ftl_dev *dev, void *_ctx, int status) { struct ftl_dev_init_ctx *ctx = _ctx; int rc; if (status) { if (dev->init_retry) { FTL_NOTICELOG(dev, "Startup retry\n"); rc = spdk_ftl_dev_init(&dev->conf, ctx->cb_fn, ctx->cb_arg); if (!rc) { free_dev(dev); free(ctx); return; } FTL_NOTICELOG(dev, "Startup retry failed: %d\n", rc); } free_dev(dev); dev = NULL; } ctx->cb_fn(dev, ctx->cb_arg, status); free(ctx); } int spdk_ftl_dev_init(const struct spdk_ftl_conf *conf, spdk_ftl_init_fn cb_fn, void *cb_arg) { int rc = -1; struct ftl_dev_init_ctx *ctx; struct spdk_ftl_dev *dev = NULL; ctx = calloc(1, sizeof(*ctx)); if (!ctx) { rc = -ENOMEM; goto error; } ctx->cb_fn = cb_fn; ctx->cb_arg = cb_arg; dev = allocate_dev(conf, &rc); if (!dev) { goto error; } rc = ftl_mngt_call_dev_startup(dev, dev_init_cb, ctx); if (rc) { goto error; } return 0; error: free(ctx); free_dev(dev); return rc; } static void dev_free_cb(struct spdk_ftl_dev *dev, void *_ctx, int status) { struct ftl_dev_free_ctx *ctx = _ctx; if (!status) { free_dev(dev); } ctx->cb_fn(ctx->cb_arg, status); free(ctx); } int spdk_ftl_dev_free(struct spdk_ftl_dev *dev, spdk_ftl_fn cb_fn, void *cb_arg) { int rc = -1; struct ftl_dev_free_ctx *ctx; ctx = calloc(1, sizeof(*ctx)); if (!ctx) { rc = -ENOMEM; goto error; } ctx->cb_fn = cb_fn; ctx->cb_arg = cb_arg; rc = ftl_mngt_call_dev_shutdown(dev, dev_free_cb, ctx); if (rc) { goto error; } return 0; error: free(ctx); return rc; } SPDK_LOG_REGISTER_COMPONENT(ftl_init)