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>
This commit is contained in:
parent
ed2b53f389
commit
a1944e0170
@ -10672,6 +10672,83 @@ Example response:
|
||||
}
|
||||
~~~
|
||||
|
||||
## Linux Userspace Block Device (UBLK) {#jsonrpc_components_ublk}
|
||||
|
||||
SPDK supports exporting bdevs though Linux ublk. The motivation behind it is to back a Linux kernel block device
|
||||
with an SPDK user space bdev.
|
||||
|
||||
To export a device over ublk, first make sure the Linux kernel ublk driver is loaded by running 'modprobe ublk_drv'.
|
||||
|
||||
### ublk_create_target {#rpc_ublk_create_target}
|
||||
|
||||
Start to create ublk threads and initialize ublk target. It will return an error if user calls this RPC twice without
|
||||
ublk_destroy_target in between. It will use current cpumask in SPDK when user does not specify cpumask option.
|
||||
|
||||
#### Parameters
|
||||
|
||||
Name | Optional | Type | Description
|
||||
----------------------- | -------- | ----------- | -----------
|
||||
cpumask | Optional | string | Cpumask for ublk target
|
||||
|
||||
#### Response
|
||||
|
||||
True if ublk target initialization is successful; False if failed.
|
||||
|
||||
#### Example
|
||||
|
||||
Example request:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"params": {
|
||||
"cpumask": "0x2"
|
||||
},
|
||||
"jsonrpc": "2.0",
|
||||
"method": "ublk_create_target",
|
||||
"id": 1
|
||||
}
|
||||
~~~
|
||||
|
||||
Example response:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": true
|
||||
}
|
||||
~~~
|
||||
|
||||
### ublk_destroy_target {#rpc_ublk_destroy_target}
|
||||
|
||||
Release all UBLK devices and destroy ublk target.
|
||||
|
||||
#### Response
|
||||
|
||||
True if ublk target destruction is successful; False if failed.
|
||||
|
||||
#### Example
|
||||
|
||||
Example request:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "ublk_destroy_target",
|
||||
"id": 1
|
||||
}
|
||||
~~~
|
||||
|
||||
Example response:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": true
|
||||
}
|
||||
~~~
|
||||
|
||||
## Linux Network Block Device (NBD) {#jsonrpc_components_nbd}
|
||||
|
||||
SPDK supports exporting bdevs through Linux nbd. These devices then appear as standard Linux kernel block devices
|
||||
|
@ -9,7 +9,7 @@ include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
SO_VER := 1
|
||||
SO_MINOR := 0
|
||||
|
||||
C_SRCS = ublk.c
|
||||
C_SRCS = ublk.c ublk_rpc.c
|
||||
LIBNAME = ublk
|
||||
LOCAL_SYS_LIBS = -luring
|
||||
|
||||
|
208
lib/ublk/ublk.c
208
lib/ublk/ublk.c
@ -6,37 +6,231 @@
|
||||
#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"
|
||||
|
||||
struct ublk_tgt {
|
||||
#include "ublk_internal.h"
|
||||
|
||||
spdk_ublk_fini_cb fini_cb_fn;
|
||||
void *fini_cb_arg;
|
||||
#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 TAILQ_HEAD(, spdk_ublk_dev) g_ublk_bdevs = TAILQ_HEAD_INITIALIZER(g_ublk_bdevs);
|
||||
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());
|
||||
g_ublk_tgt.fini_cb_fn = cb_fn;
|
||||
g_ublk_tgt.fini_cb_arg = cb_arg;
|
||||
g_ublk_tgt.fini_cb_fn(g_ublk_tgt.fini_cb_arg);
|
||||
|
||||
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)
|
||||
{
|
||||
|
24
lib/ublk/ublk_internal.h
Normal file
24
lib/ublk/ublk_internal.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
/** \file
|
||||
* Userspace block device layer
|
||||
*/
|
||||
#ifndef SPDK_UBLK_INTERNAL_H
|
||||
#define SPDK_UBLK_INTERNAL_H
|
||||
|
||||
#include "spdk/ublk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ublk_create_target(const char *cpumask_str);
|
||||
int ublk_destroy_target(spdk_ublk_fini_cb cb_fn, void *cb_arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPDK_UBLK_INTERNAL_H */
|
77
lib/ublk/ublk_rpc.c
Normal file
77
lib/ublk/ublk_rpc.c
Normal file
@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright (C) 2022 Intel Corporation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include "spdk/string.h"
|
||||
#include "spdk/env.h"
|
||||
#include "spdk/rpc.h"
|
||||
#include "spdk/util.h"
|
||||
#include "spdk/log.h"
|
||||
|
||||
#include "ublk_internal.h"
|
||||
|
||||
struct rpc_ublk_create_target {
|
||||
char *cpumask;
|
||||
};
|
||||
|
||||
static const struct spdk_json_object_decoder rpc_ublk_create_target_decoders[] = {
|
||||
{"cpumask", offsetof(struct rpc_ublk_create_target, cpumask), spdk_json_decode_string, true},
|
||||
};
|
||||
|
||||
static void
|
||||
free_rpc_ublk_create_target(struct rpc_ublk_create_target *req)
|
||||
{
|
||||
free(req->cpumask);
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_ublk_create_target(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
|
||||
{
|
||||
int rc = 0;
|
||||
struct rpc_ublk_create_target req = {};
|
||||
|
||||
if (params != NULL) {
|
||||
if (spdk_json_decode_object(params, rpc_ublk_create_target_decoders,
|
||||
SPDK_COUNTOF(rpc_ublk_create_target_decoders),
|
||||
&req)) {
|
||||
SPDK_ERRLOG("spdk_json_decode_object failed\n");
|
||||
rc = -EINVAL;
|
||||
goto invalid;
|
||||
}
|
||||
}
|
||||
rc = ublk_create_target(req.cpumask);
|
||||
if (rc != 0) {
|
||||
goto invalid;
|
||||
}
|
||||
spdk_jsonrpc_send_bool_response(request, true);
|
||||
free_rpc_ublk_create_target(&req);
|
||||
return;
|
||||
invalid:
|
||||
SPDK_ERRLOG("Can't create ublk target: %s\n", spdk_strerror(-rc));
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-rc));
|
||||
free_rpc_ublk_create_target(&req);
|
||||
}
|
||||
SPDK_RPC_REGISTER("ublk_create_target", rpc_ublk_create_target, SPDK_RPC_RUNTIME)
|
||||
|
||||
static void
|
||||
ublk_destroy_target_done(void *arg)
|
||||
{
|
||||
struct spdk_jsonrpc_request *req = arg;
|
||||
|
||||
spdk_jsonrpc_send_bool_response(req, true);
|
||||
SPDK_NOTICELOG("ublk target has been destroyed\n");
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_ublk_destroy_target(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = ublk_destroy_target(ublk_destroy_target_done, request);
|
||||
if (rc != 0) {
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, spdk_strerror(-rc));
|
||||
SPDK_ERRLOG("Can't destroy ublk target: %s\n", spdk_strerror(-rc));
|
||||
}
|
||||
}
|
||||
SPDK_RPC_REGISTER("ublk_destroy_target", rpc_ublk_destroy_target, SPDK_RPC_RUNTIME)
|
@ -21,6 +21,7 @@ from . import iscsi
|
||||
from . import log
|
||||
from . import lvol
|
||||
from . import nbd
|
||||
from . import ublk
|
||||
from . import notify
|
||||
from . import nvme
|
||||
from . import nvmf
|
||||
|
13
python/spdk/rpc/ublk.py
Normal file
13
python/spdk/rpc/ublk.py
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright (C) 2022 Intel Corporation.
|
||||
# All rights reserved.
|
||||
|
||||
def ublk_create_target(client, cpumask=None):
|
||||
params = {}
|
||||
if cpumask:
|
||||
params['cpumask'] = cpumask
|
||||
return client.call('ublk_create_target', params)
|
||||
|
||||
|
||||
def ublk_destroy_target(client):
|
||||
return client.call('ublk_destroy_target')
|
@ -2221,6 +2221,21 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
|
||||
p = subparsers.add_parser('vmd_rescan', help='Force a rescan of the devices behind VMD')
|
||||
p.set_defaults(func=vmd_rescan)
|
||||
|
||||
# ublk
|
||||
def ublk_create_target(args):
|
||||
rpc.ublk.ublk_create_target(args.client,
|
||||
cpumask=args.cpumask)
|
||||
p = subparsers.add_parser('ublk_create_target',
|
||||
help='Create spdk ublk target for ublk dev')
|
||||
p.add_argument('-m', '--cpumask', help='cpu mask for ublk dev')
|
||||
p.set_defaults(func=ublk_create_target)
|
||||
|
||||
def ublk_destroy_target(args):
|
||||
rpc.ublk.ublk_destroy_target(args.client)
|
||||
p = subparsers.add_parser('ublk_destroy_target',
|
||||
help='Destroy spdk ublk target for ublk dev')
|
||||
p.set_defaults(func=ublk_destroy_target)
|
||||
|
||||
# nbd
|
||||
def nbd_start_disk(args):
|
||||
print(rpc.nbd.nbd_start_disk(args.client,
|
||||
|
@ -64,4 +64,5 @@ lib/mlx5/mlx5_crypto
|
||||
|
||||
# Not configured to test ublk
|
||||
lib/ublk/ublk
|
||||
lib/ublk/ublk_rpc
|
||||
module/event/subsystems/ublk/ublk
|
||||
|
Loading…
Reference in New Issue
Block a user