ublk: add ublk to export block device

ublk could export a backend device as ublk block device (/dev/ublkb*).
A rpc method is used to add ublk device and it should be done
after creating ublk target. Corresponding, ublk_del_dev is
used to delete the specified ublk device.

Signed-off-by: Yifan Bian <yifan.bian@intel.com>
Co-authored-by: Xiaodong Liu <xiaodong.liu@intel.com>
Change-Id: I3a4ba8d8dc5f5ad241511ccbc9d3336b582a6dc5
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15976
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Xiaodong Liu <xiaodong.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
This commit is contained in:
Yifan Bian 2022-12-16 02:04:37 +00:00 committed by Tomasz Zawadzki
parent a1944e0170
commit e8a94a7122
6 changed files with 1412 additions and 5 deletions

View File

@ -10749,6 +10749,88 @@ Example response:
}
~~~
### ublk_start_disk {#rpc_ublk_start_disk}
Start to export one SPDK bdev as a UBLK device
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
bdev_name | Required | string | Bdev name to export
ublk_id | Required | int | Device id
queue_depth | Optional | int | Device queue depth
num_queues | Optional | int | Total number of device queues
#### Response
UBLK device ID
#### Example
Example request:
~~~json
{
"params": {
"ublk_id": "1",
"bdev_name": "Malloc1"
},
"jsonrpc": "2.0",
"method": "ublk_start_disk",
"id": 1
}
~~~
Example response:
~~~json
{
"jsonrpc": "2.0",
"id": 1,
"result": 1
}
~~~
### ublk_stop_disk {#rpc_ublk_stop_disk}
Delete a UBLK device
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
ublk_id | Required | int | Device id to delete
#### Response
True if UBLK device is deleted successfully; False if failed.
#### Example
Example request:
~~~json
{
"params": {
"ublk_id": "1",
},
"jsonrpc": "2.0",
"method": "ublk_stop_disk",
"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

File diff suppressed because it is too large Load Diff

View File

@ -10,12 +10,22 @@
#include "spdk/ublk.h"
#define UBLK_DEV_QUEUE_DEPTH 128
#define UBLK_DEV_NUM_QUEUE 1
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*ublk_del_cb)(void *cb_arg);
int ublk_create_target(const char *cpumask_str);
int ublk_destroy_target(spdk_ublk_fini_cb cb_fn, void *cb_arg);
int ublk_start_disk(const char *bdev_name, uint32_t ublk_id,
uint32_t num_queues, uint32_t queue_depth);
int ublk_stop_disk(uint32_t ublk_id, ublk_del_cb del_cb, void *cb_arg);
struct spdk_ublk_dev *ublk_dev_find_by_id(uint32_t ublk_id);
const char *ublk_dev_get_bdev_name(struct spdk_ublk_dev *ublk);
#ifdef __cplusplus
}

View File

@ -75,3 +75,115 @@ rpc_ublk_destroy_target(struct spdk_jsonrpc_request *request, const struct spdk_
}
}
SPDK_RPC_REGISTER("ublk_destroy_target", rpc_ublk_destroy_target, SPDK_RPC_RUNTIME)
struct rpc_ublk_start_disk {
char *bdev_name;
uint32_t ublk_id;
uint32_t num_queues;
uint32_t queue_depth;
};
static const struct spdk_json_object_decoder rpc_ublk_start_disk_decoders[] = {
{"bdev_name", offsetof(struct rpc_ublk_start_disk, bdev_name), spdk_json_decode_string},
{"ublk_id", offsetof(struct rpc_ublk_start_disk, ublk_id), spdk_json_decode_uint32},
{"num_queues", offsetof(struct rpc_ublk_start_disk, num_queues), spdk_json_decode_uint32, true},
{"queue_depth", offsetof(struct rpc_ublk_start_disk, queue_depth), spdk_json_decode_uint32, true},
};
static void
rpc_ublk_start_disk(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct spdk_json_write_ctx *w;
struct rpc_ublk_start_disk req = {};
int rc;
req.queue_depth = UBLK_DEV_QUEUE_DEPTH;
req.num_queues = UBLK_DEV_NUM_QUEUE;
if (spdk_json_decode_object(params, rpc_ublk_start_disk_decoders,
SPDK_COUNTOF(rpc_ublk_start_disk_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto out;
}
rc = ublk_start_disk(req.bdev_name, req.ublk_id, req.num_queues, req.queue_depth);
if (rc != 0) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto out;
}
w = spdk_jsonrpc_begin_result(request);
spdk_json_write_uint32(w, req.ublk_id);
spdk_jsonrpc_end_result(request, w);
goto out;
out:
free(req.bdev_name);
}
SPDK_RPC_REGISTER("ublk_start_disk", rpc_ublk_start_disk, SPDK_RPC_RUNTIME)
struct rpc_ublk_stop_disk {
uint32_t ublk_id;
struct spdk_jsonrpc_request *request;
};
static void
free_rpc_ublk_stop_disk(struct rpc_ublk_stop_disk *req)
{
free(req);
}
static const struct spdk_json_object_decoder rpc_ublk_stop_disk_decoders[] = {
{"ublk_id", offsetof(struct rpc_ublk_stop_disk, ublk_id), spdk_json_decode_uint32},
};
static void
rpc_ublk_stop_disk_done(void *cb_arg)
{
struct rpc_ublk_stop_disk *req = cb_arg;
spdk_jsonrpc_send_bool_response(req->request, true);
free_rpc_ublk_stop_disk(req);
}
static void
rpc_ublk_stop_disk(struct spdk_jsonrpc_request *request,
const struct spdk_json_val *params)
{
struct rpc_ublk_stop_disk *req;
int rc;
req = calloc(1, sizeof(*req));
if (req == NULL) {
SPDK_ERRLOG("could not allocate request.\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Out of memory");
return;
}
req->request = request;
if (spdk_json_decode_object(params, rpc_ublk_stop_disk_decoders,
SPDK_COUNTOF(rpc_ublk_stop_disk_decoders),
req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto invalid;
}
rc = ublk_stop_disk(req->ublk_id, rpc_ublk_stop_disk_done, req);
if (rc) {
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
goto invalid;
}
return;
invalid:
free_rpc_ublk_stop_disk(req);
}
SPDK_RPC_REGISTER("ublk_stop_disk", rpc_ublk_stop_disk, SPDK_RPC_RUNTIME)

View File

@ -11,3 +11,20 @@ def ublk_create_target(client, cpumask=None):
def ublk_destroy_target(client):
return client.call('ublk_destroy_target')
def ublk_start_disk(client, bdev_name, ublk_id=1, num_queues=1, queue_depth=128):
params = {
'bdev_name': bdev_name,
'ublk_id': ublk_id
}
if num_queues:
params['num_queues'] = num_queues
if queue_depth:
params['queue_depth'] = queue_depth
return client.call('ublk_start_disk', params)
def ublk_stop_disk(client, ublk_id=1):
params = {'ublk_id': ublk_id}
return client.call('ublk_stop_disk', params)

View File

@ -2236,6 +2236,30 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
help='Destroy spdk ublk target for ublk dev')
p.set_defaults(func=ublk_destroy_target)
def ublk_start_disk(args):
print(rpc.ublk.ublk_start_disk(args.client,
bdev_name=args.bdev_name,
ublk_id=args.ublk_id,
num_queues=args.num_queues,
queue_depth=args.queue_depth))
p = subparsers.add_parser('ublk_start_disk',
help='Export a bdev as a ublk device')
p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
p.add_argument('ublk_id', help='ublk device id to be assigned. Example: 1.', type=int)
p.add_argument('-q', '--num-queues', help="the total number of queues. Example: 1", type=int, required=False)
p.add_argument('-d', '--queue-depth', help="queue depth. Example: 128", type=int, required=False)
p.set_defaults(func=ublk_start_disk)
def ublk_stop_disk(args):
rpc.ublk.ublk_stop_disk(args.client,
ublk_id=args.ublk_id)
p = subparsers.add_parser('ublk_stop_disk',
help='Stop a ublk device')
p.add_argument('ublk_id', help='ublk device id to be deleted. Example: 1.', type=int)
p.set_defaults(func=ublk_stop_disk)
# nbd
def nbd_start_disk(args):
print(rpc.nbd.nbd_start_disk(args.client,