Add rpc methond for ublk target creation and destruction. Before to add ublk device, need to initialize ublk target to create ublk threads, corresponding an rpc methond to destroy ublk target is also added. It will deinitialize ublk target and release all ublk devices. Signed-off-by: Yifan Bian <yifan.bian@intel.com> Co-authored-by: Xiaodong Liu <xiaodong.liu@intel.com> Change-Id: I5db0cf9cc68745440df999169aa1c61111010e02 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15962 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Paul Luse <paul.e.luse@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Xiaodong Liu <xiaodong.liu@intel.com>
241 lines
4.8 KiB
C
241 lines
4.8 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2022 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <linux/ublk_cmd.h>
|
|
#include <liburing.h>
|
|
|
|
#include "spdk/stdinc.h"
|
|
#include "spdk/string.h"
|
|
#include "spdk/bdev.h"
|
|
#include "spdk/endian.h"
|
|
#include "spdk/env.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/log.h"
|
|
#include "spdk/util.h"
|
|
#include "spdk/queue.h"
|
|
#include "spdk/json.h"
|
|
#include "spdk/ublk.h"
|
|
#include "spdk/thread.h"
|
|
|
|
#include "ublk_internal.h"
|
|
|
|
#define UBLK_CTRL_DEV "/dev/ublk-control"
|
|
|
|
#define UBLK_CTRL_RING_DEPTH 32
|
|
#define UBLK_THREAD_MAX 128
|
|
|
|
static uint32_t g_num_ublk_threads = 0;
|
|
static struct spdk_cpuset g_core_mask;
|
|
|
|
struct ublk_tgt {
|
|
int ctrl_fd;
|
|
bool active;
|
|
bool is_destroying;
|
|
spdk_ublk_fini_cb cb_fn;
|
|
void *cb_arg;
|
|
struct io_uring ctrl_ring;
|
|
struct spdk_thread *ublk_threads[UBLK_THREAD_MAX];
|
|
};
|
|
|
|
static struct ublk_tgt g_ublk_tgt;
|
|
|
|
/* helpers for using io_uring */
|
|
static inline int
|
|
ublk_setup_ring(uint32_t depth, struct io_uring *r, unsigned flags)
|
|
{
|
|
struct io_uring_params p = {};
|
|
|
|
p.flags = flags | IORING_SETUP_CQSIZE;
|
|
p.cq_entries = depth;
|
|
|
|
return io_uring_queue_init_params(depth, r, &p);
|
|
}
|
|
|
|
void
|
|
spdk_ublk_init(void)
|
|
{
|
|
uint32_t i;
|
|
|
|
assert(spdk_get_thread() == spdk_thread_get_app_thread());
|
|
|
|
spdk_cpuset_zero(&g_core_mask);
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
spdk_cpuset_set_cpu(&g_core_mask, i, true);
|
|
}
|
|
}
|
|
|
|
static int
|
|
ublk_open(void)
|
|
{
|
|
int rc;
|
|
|
|
g_ublk_tgt.ctrl_fd = open(UBLK_CTRL_DEV, O_RDWR);
|
|
if (g_ublk_tgt.ctrl_fd < 0) {
|
|
rc = errno;
|
|
SPDK_ERRLOG("UBLK conrol dev %s can't be opened, error=%s\n", UBLK_CTRL_DEV, spdk_strerror(errno));
|
|
return -rc;
|
|
}
|
|
|
|
rc = ublk_setup_ring(UBLK_CTRL_RING_DEPTH, &g_ublk_tgt.ctrl_ring, IORING_SETUP_SQE128);
|
|
if (rc < 0) {
|
|
SPDK_ERRLOG("UBLK ctrl queue_init: %s\n", spdk_strerror(-rc));
|
|
close(g_ublk_tgt.ctrl_fd);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ublk_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
|
|
{
|
|
int rc;
|
|
struct spdk_cpuset tmp_mask;
|
|
|
|
if (cpumask == NULL) {
|
|
return -EPERM;
|
|
}
|
|
|
|
if (mask == NULL) {
|
|
spdk_cpuset_copy(cpumask, &g_core_mask);
|
|
return 0;
|
|
}
|
|
|
|
rc = spdk_cpuset_parse(cpumask, mask);
|
|
if (rc < 0) {
|
|
SPDK_ERRLOG("invalid cpumask %s\n", mask);
|
|
return -rc;
|
|
}
|
|
|
|
if (spdk_cpuset_count(cpumask) == 0) {
|
|
SPDK_ERRLOG("no cpus specified\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
spdk_cpuset_copy(&tmp_mask, cpumask);
|
|
spdk_cpuset_and(&tmp_mask, &g_core_mask);
|
|
|
|
if (!spdk_cpuset_equal(&tmp_mask, cpumask)) {
|
|
SPDK_ERRLOG("one of selected cpu is outside of core mask(=%s)\n",
|
|
spdk_cpuset_fmt(&g_core_mask));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ublk_create_target(const char *cpumask_str)
|
|
{
|
|
int rc;
|
|
uint32_t i;
|
|
char thread_name[32];
|
|
struct spdk_cpuset cpuset = {};
|
|
struct spdk_cpuset thd_cpuset = {};
|
|
|
|
if (g_ublk_tgt.active == true) {
|
|
SPDK_ERRLOG("UBLK target has been created\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
rc = ublk_parse_core_mask(cpumask_str, &cpuset);
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
rc = ublk_open();
|
|
if (rc != 0) {
|
|
SPDK_ERRLOG("Fail to open UBLK, error=%s\n", spdk_strerror(-rc));
|
|
return rc;
|
|
}
|
|
|
|
SPDK_ENV_FOREACH_CORE(i) {
|
|
if (spdk_cpuset_get_cpu(&cpuset, i)) {
|
|
spdk_cpuset_zero(&thd_cpuset);
|
|
spdk_cpuset_set_cpu(&thd_cpuset, i, true);
|
|
snprintf(thread_name, sizeof(thread_name), "ublk_thread%u", i);
|
|
g_ublk_tgt.ublk_threads[g_num_ublk_threads] = spdk_thread_create(thread_name, &thd_cpuset);
|
|
g_num_ublk_threads++;
|
|
}
|
|
}
|
|
g_ublk_tgt.active = true;
|
|
SPDK_NOTICELOG("UBLK target created successfully\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_ublk_fini_done(void *args)
|
|
{
|
|
g_num_ublk_threads = 0;
|
|
g_ublk_tgt.is_destroying = false;
|
|
g_ublk_tgt.active = false;
|
|
if (g_ublk_tgt.cb_fn) {
|
|
g_ublk_tgt.cb_fn(g_ublk_tgt.cb_arg);
|
|
g_ublk_tgt.cb_fn = NULL;
|
|
g_ublk_tgt.cb_arg = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ublk_thread_exit(void *args)
|
|
{
|
|
struct spdk_thread *ublk_thread = spdk_get_thread();
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < g_num_ublk_threads; i++) {
|
|
if (g_ublk_tgt.ublk_threads[i] == ublk_thread) {
|
|
spdk_thread_exit(ublk_thread);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This function will be used and extended in next patch */
|
|
static void
|
|
_ublk_fini(void *args)
|
|
{
|
|
spdk_for_each_thread(ublk_thread_exit, NULL, _ublk_fini_done);
|
|
}
|
|
|
|
int
|
|
spdk_ublk_fini(spdk_ublk_fini_cb cb_fn, void *cb_arg)
|
|
{
|
|
assert(spdk_get_thread() == spdk_thread_get_app_thread());
|
|
|
|
if (g_ublk_tgt.is_destroying == true) {
|
|
/* UBLK target is being destroying */
|
|
return -EBUSY;
|
|
}
|
|
g_ublk_tgt.cb_fn = cb_fn;
|
|
g_ublk_tgt.cb_arg = cb_arg;
|
|
g_ublk_tgt.is_destroying = true;
|
|
_ublk_fini(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ublk_destroy_target(spdk_ublk_fini_cb cb_fn, void *cb_arg)
|
|
{
|
|
int rc;
|
|
|
|
if (g_ublk_tgt.active == false) {
|
|
/* UBLK target has not been created */
|
|
return -ENOENT;
|
|
}
|
|
|
|
rc = spdk_ublk_fini(cb_fn, cb_arg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
spdk_ublk_write_config_json(struct spdk_json_write_ctx *w)
|
|
{
|
|
spdk_json_write_array_begin(w);
|
|
|
|
spdk_json_write_array_end(w);
|
|
}
|