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} ## 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 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" #include "spdk/ublk.h"
#define UBLK_DEV_QUEUE_DEPTH 128
#define UBLK_DEV_NUM_QUEUE 1
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef void (*ublk_del_cb)(void *cb_arg);
int ublk_create_target(const char *cpumask_str); int ublk_create_target(const char *cpumask_str);
int ublk_destroy_target(spdk_ublk_fini_cb cb_fn, void *cb_arg); 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 #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) 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): def ublk_destroy_target(client):
return client.call('ublk_destroy_target') 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') help='Destroy spdk ublk target for ublk dev')
p.set_defaults(func=ublk_destroy_target) 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 # nbd
def nbd_start_disk(args): def nbd_start_disk(args):
print(rpc.nbd.nbd_start_disk(args.client, print(rpc.nbd.nbd_start_disk(args.client,