Commit e8a94a71 authored by Yifan Bian's avatar Yifan Bian Committed by Tomasz Zawadzki
Browse files

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: default avatarYifan Bian <yifan.bian@intel.com>
Co-authored-by: default avatarXiaodong Liu <xiaodong.liu@intel.com>
Change-Id: I3a4ba8d8dc5f5ad241511ccbc9d3336b582a6dc5
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15976


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-by: default avatarXiaodong Liu <xiaodong.liu@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarPaul Luse <paul.e.luse@intel.com>
parent a1944e01
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -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
+1167 −5

File changed.

Preview size limit exceeded, changes collapsed.

+10 −0
Original line number Diff line number Diff line
@@ -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
}
+112 −0
Original line number Diff line number Diff line
@@ -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)
+17 −0
Original line number Diff line number Diff line
@@ -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)
Loading