Commit 5004d7b8 authored by Changpeng Liu's avatar Changpeng Liu Committed by Tomasz Zawadzki
Browse files

module/vfu_device: add virtio-scsi emulation



Here we use vfu_tgt library and emulate a virtio-scsi device
as the next use case.

Compared with vhost-user-scsi, the packed ring is supported with this
patch.

Example usage method:

1. scripts/rpc.py bdev_malloc_create -b malloc0 $((512)) 512
2. scripts/rpc.py vfu_virtio_create_scsi_endpoint vfu.0 --cpumask 0x1 --num-io-queues=4 \
                                                  --qsize=128 --packed-ring
3. scripts/rpc.py vfu_virtio_scsi_add_target vfu.0 --scsi-target-num=0 --bdev-name malloc0
4. Start QEMU with '-device vfio-user-pci,socket=/spdk/vfu.0'

Change-Id: I8f35d1d21aaec34844d6ddb59dc997a64f141179
Signed-off-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12673


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 23ef6388
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
@@ -8089,6 +8089,124 @@ Example response:
}
~~~

### vfu_virtio_scsi_add_target {#rpc_vfu_virtio_scsi_add_target}

Add block device to specified SCSI target of vfio-user virtio-scsi PCI endpoint.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
name                    | Required | string      | Endpoint name
scsi_target_num         | Required | number      | SCSI target number
bdev_name               | Required | string      | Block device name

#### Example

Example request:

~~~json
{
  "params": {
    "name": "vfu.0",
    "scsi_target_num": 0,
    "bdev_name": "Malloc0"
  },
  "jsonrpc": "2.0",
  "method": "vfu_virtio_scsi_add_target",
  "id": 1
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

### vfu_virtio_scsi_remove_target {#rpc_vfu_virtio_scsi_remove_target}

Remove a SCSI target of vfio-user virtio-scsi PCI endpoint.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
name                    | Required | string      | Endpoint name
scsi_target_num         | Required | number      | SCSI target number

#### Example

Example request:

~~~json
{
  "params": {
    "name": "vfu.0",
    "scsi_target_num": 0
  },
  "jsonrpc": "2.0",
  "method": "vfu_virtio_scsi_remove_target",
  "id": 1
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

### vfu_virtio_create_scsi_endpoint {#rpc_vfu_virtio_create_scsi_endpoint}

Create vfio-user virtio-scsi PCI endpoint.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
name                    | Required | string      | Endpoint name
cpumask                 | Optional | string      | CPU masks
num_io_queues           | Optional | number      | Number of IO queues
qsize                   | Optional | number      | Queue size
packed_ring             | Optional | boolean     | Enable packed ring

#### Example

Example request:

~~~json
{
  "params": {
    "name": "vfu.0",
    "cpumask": "0x2",
    "num_io_queues": 4,
    "qsize": 256
  },
  "jsonrpc": "2.0",
  "method": "vfu_virtio_create_scsi_endpoint",
  "id": 1
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

## Vhost Target {#jsonrpc_components_vhost_tgt}

The following common preconditions need to be met in all target types.
+1 −1
Original line number Diff line number Diff line
@@ -164,5 +164,5 @@ DEPDIRS-event_vfu_tgt := init vfu_tgt
# module/vfu_device

ifeq ($(CONFIG_VFIO_USER),y)
DEPDIRS-vfu_device := $(BDEV_DEPS_THREAD) vfu_tgt
DEPDIRS-vfu_device := $(BDEV_DEPS_THREAD) scsi vfu_tgt
endif
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
SO_VER := 1
SO_MINOR := 0

C_SRCS = vfu_virtio.c vfu_virtio_blk.c vfu_virtio_rpc.c
C_SRCS = vfu_virtio.c vfu_virtio_blk.c vfu_virtio_scsi.c vfu_virtio_rpc.c
LIBNAME = vfu_device

SPDK_MAP_FILE = $(SPDK_ROOT_DIR)/mk/spdk_blank.map
+6 −1
Original line number Diff line number Diff line
@@ -399,5 +399,10 @@ int vfu_virtio_pre_memory_remove(struct spdk_vfu_endpoint *endpoint, void *map_s
int vfu_virtio_pci_reset_cb(struct spdk_vfu_endpoint *endpoint);
int vfu_virtio_blk_add_bdev(const char *name, const char *bdev_name,
			    uint16_t num_queues, uint16_t qsize, bool packed_ring);

/* virtio_scsi */
int vfu_virtio_scsi_add_target(const char *name, uint8_t scsi_target_num,
			       const char *bdev_name);
int vfu_virtio_scsi_remove_target(const char *name, uint8_t scsi_target_num);
int vfu_virtio_scsi_set_options(const char *name, uint16_t num_io_queues, uint16_t qsize,
				bool packed_ring);
#endif
+161 −0
Original line number Diff line number Diff line
@@ -124,3 +124,164 @@ invalid:
}
SPDK_RPC_REGISTER("vfu_virtio_create_blk_endpoint", rpc_vfu_virtio_create_blk_endpoint,
		  SPDK_RPC_RUNTIME)

struct rpc_vfu_virtio_scsi {
	char		*name;
	uint8_t		scsi_target_num;
	char		*bdev_name;
};

static const struct spdk_json_object_decoder rpc_construct_vfu_virtio_scsi[] = {
	{"name", offsetof(struct rpc_vfu_virtio_scsi, name), spdk_json_decode_string },
	{"scsi_target_num", offsetof(struct rpc_vfu_virtio_scsi, scsi_target_num), spdk_json_decode_uint8 },
	{"bdev_name", offsetof(struct rpc_vfu_virtio_scsi, bdev_name), spdk_json_decode_string },
};

static void
free_rpc_vfu_virtio_scsi(struct rpc_vfu_virtio_scsi *req)
{
	free(req->name);
	free(req->bdev_name);
}

static void
rpc_vfu_virtio_scsi_add_target(struct spdk_jsonrpc_request *request,
			       const struct spdk_json_val *params)
{
	struct rpc_vfu_virtio_scsi req = {0};
	int rc;

	if (spdk_json_decode_object(params, rpc_construct_vfu_virtio_scsi,
				    SPDK_COUNTOF(rpc_construct_vfu_virtio_scsi),
				    &req)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		rc = -EINVAL;
		goto invalid;
	}

	rc = vfu_virtio_scsi_add_target(req.name, req.scsi_target_num, req.bdev_name);;
	if (rc < 0) {
		goto invalid;
	}

	free_rpc_vfu_virtio_scsi(&req);
	spdk_jsonrpc_send_bool_response(request, true);
	return;

invalid:
	free_rpc_vfu_virtio_scsi(&req);
	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
					 spdk_strerror(-rc));
}
SPDK_RPC_REGISTER("vfu_virtio_scsi_add_target", rpc_vfu_virtio_scsi_add_target,
		  SPDK_RPC_RUNTIME)

struct rpc_vfu_virtio_scsi_remove {
	char		*name;
	uint8_t		scsi_target_num;
};

static const struct spdk_json_object_decoder rpc_remove_vfu_virtio_scsi_target[] = {
	{"name", offsetof(struct rpc_vfu_virtio_scsi_remove, name), spdk_json_decode_string },
	{"scsi_target_num", offsetof(struct rpc_vfu_virtio_scsi_remove, scsi_target_num), spdk_json_decode_uint8 },
};

static void
free_rpc_vfu_virtio_scsi_remove(struct rpc_vfu_virtio_scsi_remove *req)
{
	free(req->name);
}

static void
rpc_vfu_virtio_scsi_remove_target(struct spdk_jsonrpc_request *request,
				  const struct spdk_json_val *params)
{
	struct rpc_vfu_virtio_scsi_remove req = {0};
	int rc;

	if (spdk_json_decode_object(params, rpc_remove_vfu_virtio_scsi_target,
				    SPDK_COUNTOF(rpc_remove_vfu_virtio_scsi_target),
				    &req)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		rc = -EINVAL;
		goto invalid;
	}

	rc = vfu_virtio_scsi_remove_target(req.name, req.scsi_target_num);
	if (rc < 0) {
		goto invalid;
	}

	free_rpc_vfu_virtio_scsi_remove(&req);
	spdk_jsonrpc_send_bool_response(request, true);
	return;

invalid:
	free_rpc_vfu_virtio_scsi_remove(&req);
	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
					 spdk_strerror(-rc));
}
SPDK_RPC_REGISTER("vfu_virtio_scsi_remove_target", rpc_vfu_virtio_scsi_remove_target,
		  SPDK_RPC_RUNTIME)

struct rpc_vfu_virtio_create_scsi {
	char		*name;
	char		*cpumask;
	uint16_t	num_io_queues;
	uint16_t	qsize;
	bool		packed_ring;
};

static const struct spdk_json_object_decoder rpc_construct_vfu_virtio_create_scsi[] = {
	{"name", offsetof(struct rpc_vfu_virtio_create_scsi, name), spdk_json_decode_string },
	{"cpumask", offsetof(struct rpc_vfu_virtio_create_scsi, cpumask), spdk_json_decode_string, true},
	{"num_io_queues", offsetof(struct rpc_vfu_virtio_create_scsi, num_io_queues), spdk_json_decode_uint16, true },
	{"qsize", offsetof(struct rpc_vfu_virtio_create_scsi, qsize), spdk_json_decode_uint16, true },
	{"packed_ring", offsetof(struct rpc_vfu_virtio_create_scsi, packed_ring), spdk_json_decode_bool, true},
};

static void
free_rpc_vfu_virtio_create_scsi(struct rpc_vfu_virtio_create_scsi *req)
{
	free(req->name);
	free(req->cpumask);
}

static void
rpc_vfu_virtio_create_scsi_endpoint(struct spdk_jsonrpc_request *request,
				    const struct spdk_json_val *params)
{
	struct rpc_vfu_virtio_create_scsi req = {0};
	int rc;

	if (spdk_json_decode_object(params, rpc_construct_vfu_virtio_create_scsi,
				    SPDK_COUNTOF(rpc_construct_vfu_virtio_create_scsi),
				    &req)) {
		SPDK_ERRLOG("spdk_json_decode_object failed\n");
		rc = -EINVAL;
		goto invalid;
	}

	rc = spdk_vfu_create_endpoint(req.name, req.cpumask, "virtio_scsi");
	if (rc) {
		SPDK_ERRLOG("Failed to create virtio_blk endpoint\n");
		goto invalid;
	}

	rc = vfu_virtio_scsi_set_options(req.name, req.num_io_queues, req.qsize, req.packed_ring);
	if (rc < 0) {
		spdk_vfu_delete_endpoint(req.name);
		goto invalid;
	}
	free_rpc_vfu_virtio_create_scsi(&req);

	spdk_jsonrpc_send_bool_response(request, true);
	return;

invalid:
	free_rpc_vfu_virtio_create_scsi(&req);
	spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
					 spdk_strerror(-rc));
}
SPDK_RPC_REGISTER("vfu_virtio_create_scsi_endpoint", rpc_vfu_virtio_create_scsi_endpoint,
		  SPDK_RPC_RUNTIME)
Loading