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: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12673
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Changpeng Liu 2022-05-12 09:53:08 +08:00 committed by Tomasz Zawadzki
parent 23ef63882c
commit 5004d7b8e8
8 changed files with 1419 additions and 3 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -49,3 +49,60 @@ def vfu_virtio_create_blk_endpoint(client, name, bdev_name, cpumask, num_queues,
params['packed_ring'] = packed_ring
return client.call('vfu_virtio_create_blk_endpoint', params)
def vfu_virtio_scsi_add_target(client, name, scsi_target_num, bdev_name):
"""Attach a block device to the specified SCSI target.
Args:
name: endpoint name
scsi_target_num: SCSI target number
bdev_name: name of block device
"""
params = {
'name': name,
'scsi_target_num': scsi_target_num,
'bdev_name': bdev_name
}
return client.call('vfu_virtio_scsi_add_target', params)
def vfu_virtio_scsi_remove_target(client, name, scsi_target_num):
"""Remove specified SCSI target of socket endpoint.
Args:
name: endpoint name
scsi_target_num: SCSI target number
"""
params = {
'name': name,
'scsi_target_num': scsi_target_num
}
return client.call('vfu_virtio_scsi_remove_target', params)
def vfu_virtio_create_scsi_endpoint(client, name, cpumask, num_io_queues, qsize, packed_ring):
"""Create virtio-scsi endpoint.
Args:
name: endpoint name
cpumask: CPU core mask
num_io_queues: number of IO vrings
qsize: number of element of each vring
packed_ring: enable packed ring
"""
params = {
'name': name,
}
if cpumask:
params['cpumask'] = cpumask
if num_io_queues:
params['num_io_queues'] = num_io_queues
if qsize:
params['qsize'] = qsize
if packed_ring:
params['packed_ring'] = packed_ring
return client.call('vfu_virtio_create_scsi_endpoint', params)

View File

@ -2649,6 +2649,44 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
p.add_argument("--packed-ring", action='store_true', help='Enable packed ring')
p.set_defaults(func=vfu_virtio_create_blk_endpoint)
def vfu_virtio_scsi_add_target(args):
rpc.vfio_user.vfu_virtio_scsi_add_target(args.client,
name=args.name,
scsi_target_num=args.scsi_target_num,
bdev_name=args.bdev_name)
p = subparsers.add_parser('vfu_virtio_scsi_add_target', help='Attach a block device to SCSI target of PCI endpoint.')
p.add_argument('name', help='Name of the endpoint')
p.add_argument('--scsi-target-num', help='number of SCSI Target', type=int, required=True)
p.add_argument('--bdev-name', help='block device name', type=str, required=True)
p.set_defaults(func=vfu_virtio_scsi_add_target)
def vfu_virtio_scsi_remove_target(args):
rpc.vfio_user.vfu_virtio_scsi_remove_target(args.client,
name=args.name,
scsi_target_num=args.scsi_target_num)
p = subparsers.add_parser('vfu_virtio_scsi_remove_target', help='Remove the specified SCSI target of PCI endpoint.')
p.add_argument('name', help='Name of the endpoint')
p.add_argument('--scsi-target-num', help='number of SCSI Target', type=int, required=True)
p.set_defaults(func=vfu_virtio_scsi_remove_target)
def vfu_virtio_create_scsi_endpoint(args):
rpc.vfio_user.vfu_virtio_create_scsi_endpoint(args.client,
name=args.name,
cpumask=args.cpumask,
num_io_queues=args.num_io_queues,
qsize=args.qsize,
packed_ring=args.packed_ring)
p = subparsers.add_parser('vfu_virtio_create_scsi_endpoint', help='Create virtio-scsi endpoint.')
p.add_argument('name', help='Name of the endpoint')
p.add_argument('--cpumask', help='CPU masks')
p.add_argument('--num-io-queues', help='number of IO vrings', type=int, default=0)
p.add_argument('--qsize', help='number of element for each vring', type=int, default=0)
p.add_argument("--packed-ring", action='store_true', help='Enable packed ring')
p.set_defaults(func=vfu_virtio_create_scsi_endpoint)
# accel_fw
def accel_get_opc_assignments(args):
print_dict(rpc.accel.accel_get_opc_assignments(args.client))