Spdk/lib/ublk/ublk.c
Yifan Bian a1944e0170 ublk: add ublk target creation and destruction
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>
2023-01-20 07:48:25 +00:00

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);
}