* [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space
2018-01-02 3:55 [Qemu-devel] [PATCH v8 0/4] Introduce a new vhost-user-blk host device to QEMU Changpeng Liu
@ 2018-01-02 3:55 ` Changpeng Liu
2018-01-02 15:19 ` Marc-André Lureau
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 2/4] vhost-user-blk: introduce a new vhost-user-blk host device Changpeng Liu
` (2 subsequent siblings)
3 siblings, 1 reply; 13+ messages in thread
From: Changpeng Liu @ 2018-01-02 3:55 UTC (permalink / raw)
To: changpeng.liu, qemu-devel
Cc: stefanha, pbonzini, mst, marcandre.lureau, felipe, james.r.harris
Add VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages which can be
used for live migration of vhost user devices, also vhost user devices
can benefit from the messages to get/set virtio config space from/to the
I/O target. For the purpose to support virtio config space change,
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG message is added as the event notifier
in case virtio config space change in the slave I/O target.
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
---
docs/interop/vhost-user.txt | 53 +++++++++++++++++
hw/virtio/vhost-user.c | 118 ++++++++++++++++++++++++++++++++++++++
hw/virtio/vhost.c | 32 +++++++++++
include/hw/virtio/vhost-backend.h | 12 ++++
include/hw/virtio/vhost.h | 15 +++++
5 files changed, 230 insertions(+)
diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
index 954771d..1788fff 100644
--- a/docs/interop/vhost-user.txt
+++ b/docs/interop/vhost-user.txt
@@ -116,6 +116,19 @@ Depending on the request type, payload can be:
- 3: IOTLB invalidate
- 4: IOTLB access fail
+ * Virtio device config space
+ -----------------------------------
+ | offset | size | flags | payload |
+ -----------------------------------
+
+ Offset: a 32-bit offset of virtio device's configuration space
+ Size: a 32-bit configuration space access size in bytes
+ Flags: a 32-bit value:
+ - 0: Vhost master messages used for writeable fields
+ - 1: Vhost master messages used for live migration
+ Payload: Size bytes array holding the contents of the virtio
+ device's configuration space
+
In QEMU the vhost-user message is implemented with the following struct:
typedef struct VhostUserMsg {
@@ -129,6 +142,7 @@ typedef struct VhostUserMsg {
VhostUserMemory memory;
VhostUserLog log;
struct vhost_iotlb_msg iotlb;
+ VhostUserConfig config;
};
} QEMU_PACKED VhostUserMsg;
@@ -596,6 +610,30 @@ Master message types
and expect this message once (per VQ) during device configuration
(ie. before the master starts the VQ).
+ * VHOST_USER_GET_CONFIG
+
+ Id: 24
+ Equivalent ioctl: N/A
+ Slave payload: virtio device config space
+
+ Submitted by the vhost-user master to fetch the contents of the virtio
+ device configuration space, vhost-user slave's payload size MUST match
+ master's request, vhost-user slave uses zero length of payload to
+ indicate an error to vhost-user master. The vhost-user master may
+ cache the contents to avoid repeated VHOST_USER_GET_CONFIG calls.
+
+* VHOST_USER_SET_CONFIG
+
+ Id: 25
+ Equivalent ioctl: N/A
+ Master payload: virtio device config space
+
+ Submitted by the vhost-user master when the Guest changes the virtio
+ device configuration space and also can be used for live migration
+ on the destination host. The vhost-user slave must check the flags
+ field, and slaves MUST NOT accept SET_CONFIG for read-only
+ configuration space fields unless the live migration bit is set.
+
Slave message types
-------------------
@@ -614,6 +652,21 @@ Slave message types
This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature
has been successfully negotiated.
+* VHOST_USER_SLAVE_CONFIG_CHANGE_MSG
+
+ Id: 2
+ Equivalent ioctl: N/A
+ Slave payload: N/A
+ Master payload: N/A
+
+ Vhost-user slave sends such messages to notify that the virtio device's
+ configuration space has changed, for those host devices which can support
+ such feature, host driver can send VHOST_USER_GET_CONFIG message to slave
+ to get the latest content. If VHOST_USER_PROTOCOL_F_REPLY_ACK is
+ negotiated, and slave set the VHOST_USER_NEED_REPLY flag, master must
+ respond with zero when operation is successfully completed, or non-zero
+ otherwise.
+
VHOST_USER_PROTOCOL_F_REPLY_ACK:
-------------------------------
The original vhost-user specification only demands replies for certain
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 093675e..8b94688 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -26,6 +26,11 @@
#define VHOST_MEMORY_MAX_NREGIONS 8
#define VHOST_USER_F_PROTOCOL_FEATURES 30
+/*
+ * Maximum size of virtio device config space
+ */
+#define VHOST_USER_MAX_CONFIG_SIZE 256
+
enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_MQ = 0,
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
@@ -65,12 +70,15 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_SLAVE_REQ_FD = 21,
VHOST_USER_IOTLB_MSG = 22,
VHOST_USER_SET_VRING_ENDIAN = 23,
+ VHOST_USER_GET_CONFIG = 24,
+ VHOST_USER_SET_CONFIG = 25,
VHOST_USER_MAX
} VhostUserRequest;
typedef enum VhostUserSlaveRequest {
VHOST_USER_SLAVE_NONE = 0,
VHOST_USER_SLAVE_IOTLB_MSG = 1,
+ VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
VHOST_USER_SLAVE_MAX
} VhostUserSlaveRequest;
@@ -92,6 +100,18 @@ typedef struct VhostUserLog {
uint64_t mmap_offset;
} VhostUserLog;
+typedef struct VhostUserConfig {
+ uint32_t offset;
+ uint32_t size;
+ uint32_t flags;
+ uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
+} VhostUserConfig;
+
+static VhostUserConfig c __attribute__ ((unused));
+#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
+ + sizeof(c.size) \
+ + sizeof(c.flags))
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -109,6 +129,7 @@ typedef struct VhostUserMsg {
VhostUserMemory memory;
VhostUserLog log;
struct vhost_iotlb_msg iotlb;
+ VhostUserConfig config;
} payload;
} QEMU_PACKED VhostUserMsg;
@@ -608,6 +629,21 @@ static int vhost_user_reset_device(struct vhost_dev *dev)
return 0;
}
+static int vhost_user_slave_handle_config_change(struct vhost_dev *dev)
+{
+ int ret = -1;
+
+ if (!dev->config_ops) {
+ return -1;
+ }
+
+ if (dev->config_ops->vhost_dev_config_notifier) {
+ ret = dev->config_ops->vhost_dev_config_notifier(dev);
+ }
+
+ return ret;
+}
+
static void slave_read(void *opaque)
{
struct vhost_dev *dev = opaque;
@@ -640,6 +676,9 @@ static void slave_read(void *opaque)
case VHOST_USER_SLAVE_IOTLB_MSG:
ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb);
break;
+ case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
+ ret = vhost_user_slave_handle_config_change(dev);
+ break;
default:
error_report("Received unexpected msg type.");
ret = -EINVAL;
@@ -922,6 +961,83 @@ static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled)
/* No-op as the receive channel is not dedicated to IOTLB messages. */
}
+static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config,
+ uint32_t config_len)
+{
+ VhostUserMsg msg = {
+ msg.request = VHOST_USER_GET_CONFIG,
+ msg.flags = VHOST_USER_VERSION,
+ msg.size = VHOST_USER_CONFIG_HDR_SIZE + config_len,
+ };
+
+ if (config_len > VHOST_USER_MAX_CONFIG_SIZE) {
+ return -1;
+ }
+
+ msg.payload.config.offset = 0;
+ msg.payload.config.size = config_len;
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
+
+ if (vhost_user_read(dev, &msg) < 0) {
+ return -1;
+ }
+
+ if (msg.request != VHOST_USER_GET_CONFIG) {
+ error_report("Received unexpected msg type. Expected %d received %d",
+ VHOST_USER_GET_CONFIG, msg.request);
+ return -1;
+ }
+
+ if (msg.size != VHOST_USER_CONFIG_HDR_SIZE + config_len) {
+ error_report("Received bad msg size.");
+ return -1;
+ }
+
+ memcpy(config, msg.payload.config.region, config_len);
+
+ return 0;
+}
+
+static int vhost_user_set_config(struct vhost_dev *dev, const uint8_t *data,
+ uint32_t offset, uint32_t size, uint32_t flags)
+{
+ uint8_t *p;
+ bool reply_supported = virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_REPLY_ACK);
+
+ VhostUserMsg msg = {
+ msg.request = VHOST_USER_SET_CONFIG,
+ msg.flags = VHOST_USER_VERSION,
+ msg.size = VHOST_USER_CONFIG_HDR_SIZE + size,
+ };
+
+ if (reply_supported) {
+ msg.flags |= VHOST_USER_NEED_REPLY_MASK;
+ }
+
+ if (size > VHOST_USER_MAX_CONFIG_SIZE) {
+ return -1;
+ }
+
+ msg.payload.config.offset = offset,
+ msg.payload.config.size = size,
+ msg.payload.config.flags = flags,
+ p = msg.payload.config.region;
+ memcpy(p, data, size);
+
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
+
+ if (reply_supported) {
+ return process_message_reply(dev, &msg);
+ }
+
+ return 0;
+}
+
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
.vhost_backend_init = vhost_user_init,
@@ -948,4 +1064,6 @@ const VhostOps user_ops = {
.vhost_net_set_mtu = vhost_user_net_set_mtu,
.vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
.vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg,
+ .vhost_get_config = vhost_user_get_config,
+ .vhost_set_config = vhost_user_set_config,
};
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index e4290ce..386aef8 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1505,6 +1505,38 @@ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
}
}
+int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
+ uint32_t config_len)
+{
+ assert(hdev->vhost_ops);
+
+ if (hdev->vhost_ops->vhost_get_config) {
+ return hdev->vhost_ops->vhost_get_config(hdev, config, config_len);
+ }
+
+ return -1;
+}
+
+int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
+ uint32_t offset, uint32_t size, uint32_t flags)
+{
+ assert(hdev->vhost_ops);
+
+ if (hdev->vhost_ops->vhost_set_config) {
+ return hdev->vhost_ops->vhost_set_config(hdev, data, offset,
+ size, flags);
+ }
+
+ return -1;
+}
+
+void vhost_dev_set_config_notifier(struct vhost_dev *hdev,
+ const VhostDevConfigOps *ops)
+{
+ assert(hdev->vhost_ops);
+ hdev->config_ops = ops;
+}
+
/* Host notifiers must be enabled at this point. */
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
{
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index a7a5f22..592254f 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -20,6 +20,11 @@ typedef enum VhostBackendType {
VHOST_BACKEND_TYPE_MAX = 3,
} VhostBackendType;
+typedef enum VhostSetConfigType {
+ VHOST_SET_CONFIG_TYPE_MASTER = 0,
+ VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
+} VhostSetConfigType;
+
struct vhost_dev;
struct vhost_log;
struct vhost_memory;
@@ -84,6 +89,11 @@ typedef void (*vhost_set_iotlb_callback_op)(struct vhost_dev *dev,
int enabled);
typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev,
struct vhost_iotlb_msg *imsg);
+typedef int (*vhost_set_config_op)(struct vhost_dev *dev, const uint8_t *data,
+ uint32_t offset, uint32_t size,
+ uint32_t flags);
+typedef int (*vhost_get_config_op)(struct vhost_dev *dev, uint8_t *config,
+ uint32_t config_len);
typedef struct VhostOps {
VhostBackendType backend_type;
@@ -118,6 +128,8 @@ typedef struct VhostOps {
vhost_vsock_set_running_op vhost_vsock_set_running;
vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
vhost_send_device_iotlb_msg_op vhost_send_device_iotlb_msg;
+ vhost_get_config_op vhost_get_config;
+ vhost_set_config_op vhost_set_config;
} VhostOps;
extern const VhostOps user_ops;
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 467dc77..1dc2d73 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -46,6 +46,12 @@ struct vhost_iommu {
QLIST_ENTRY(vhost_iommu) iommu_next;
};
+typedef struct VhostDevConfigOps {
+ /* Vhost device config space changed callback
+ */
+ int (*vhost_dev_config_notifier)(struct vhost_dev *dev);
+} VhostDevConfigOps;
+
struct vhost_memory;
struct vhost_dev {
VirtIODevice *vdev;
@@ -76,6 +82,7 @@ struct vhost_dev {
QLIST_ENTRY(vhost_dev) entry;
QLIST_HEAD(, vhost_iommu) iommu_list;
IOMMUNotifier n;
+ const VhostDevConfigOps *config_ops;
};
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
@@ -106,4 +113,12 @@ int vhost_net_set_backend(struct vhost_dev *hdev,
struct vhost_vring_file *file);
int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
+int vhost_dev_get_config(struct vhost_dev *dev, uint8_t *config,
+ uint32_t config_len);
+int vhost_dev_set_config(struct vhost_dev *dev, const uint8_t *data,
+ uint32_t offset, uint32_t size, uint32_t flags);
+/* notifier callback in case vhost device config space changed
+ */
+void vhost_dev_set_config_notifier(struct vhost_dev *dev,
+ const VhostDevConfigOps *ops);
#endif
--
1.9.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space Changpeng Liu
@ 2018-01-02 15:19 ` Marc-André Lureau
2018-01-03 1:36 ` Liu, Changpeng
0 siblings, 1 reply; 13+ messages in thread
From: Marc-André Lureau @ 2018-01-02 15:19 UTC (permalink / raw)
To: Changpeng Liu
Cc: QEMU, james.r.harris, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
Hi
On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> Add VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages which can be
> used for live migration of vhost user devices, also vhost user devices
> can benefit from the messages to get/set virtio config space from/to the
> I/O target. For the purpose to support virtio config space change,
> VHOST_USER_SLAVE_CONFIG_CHANGE_MSG message is added as the event notifier
> in case virtio config space change in the slave I/O target.
>
> Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> ---
> docs/interop/vhost-user.txt | 53 +++++++++++++++++
> hw/virtio/vhost-user.c | 118 ++++++++++++++++++++++++++++++++++++++
> hw/virtio/vhost.c | 32 +++++++++++
> include/hw/virtio/vhost-backend.h | 12 ++++
> include/hw/virtio/vhost.h | 15 +++++
> 5 files changed, 230 insertions(+)
>
> diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
> index 954771d..1788fff 100644
> --- a/docs/interop/vhost-user.txt
> +++ b/docs/interop/vhost-user.txt
> @@ -116,6 +116,19 @@ Depending on the request type, payload can be:
> - 3: IOTLB invalidate
> - 4: IOTLB access fail
>
> + * Virtio device config space
> + -----------------------------------
> + | offset | size | flags | payload |
> + -----------------------------------
> +
> + Offset: a 32-bit offset of virtio device's configuration space
> + Size: a 32-bit configuration space access size in bytes
> + Flags: a 32-bit value:
> + - 0: Vhost master messages used for writeable fields
> + - 1: Vhost master messages used for live migration
> + Payload: Size bytes array holding the contents of the virtio
> + device's configuration space
> +
> In QEMU the vhost-user message is implemented with the following struct:
>
> typedef struct VhostUserMsg {
> @@ -129,6 +142,7 @@ typedef struct VhostUserMsg {
> VhostUserMemory memory;
> VhostUserLog log;
> struct vhost_iotlb_msg iotlb;
> + VhostUserConfig config;
> };
> } QEMU_PACKED VhostUserMsg;
>
> @@ -596,6 +610,30 @@ Master message types
> and expect this message once (per VQ) during device configuration
> (ie. before the master starts the VQ).
>
> + * VHOST_USER_GET_CONFIG
> +
> + Id: 24
> + Equivalent ioctl: N/A
Please document the Master payload. (msg.size != 0)
> + Slave payload: virtio device config space
> +
> + Submitted by the vhost-user master to fetch the contents of the virtio
> + device configuration space, vhost-user slave's payload size MUST match
> + master's request, vhost-user slave uses zero length of payload to
> + indicate an error to vhost-user master. The vhost-user master may
> + cache the contents to avoid repeated VHOST_USER_GET_CONFIG calls.
> +
> +* VHOST_USER_SET_CONFIG
> +
> + Id: 25
> + Equivalent ioctl: N/A
Same here
> + Master payload: virtio device config space
> +
> + Submitted by the vhost-user master when the Guest changes the virtio
> + device configuration space and also can be used for live migration
> + on the destination host. The vhost-user slave must check the flags
> + field, and slaves MUST NOT accept SET_CONFIG for read-only
> + configuration space fields unless the live migration bit is set.
> +
> Slave message types
> -------------------
>
> @@ -614,6 +652,21 @@ Slave message types
> This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature
> has been successfully negotiated.
>
> +* VHOST_USER_SLAVE_CONFIG_CHANGE_MSG
> +
> + Id: 2
> + Equivalent ioctl: N/A
> + Slave payload: N/A
> + Master payload: N/A
> +
> + Vhost-user slave sends such messages to notify that the virtio device's
> + configuration space has changed, for those host devices which can support
> + such feature, host driver can send VHOST_USER_GET_CONFIG message to slave
> + to get the latest content. If VHOST_USER_PROTOCOL_F_REPLY_ACK is
> + negotiated, and slave set the VHOST_USER_NEED_REPLY flag, master must
> + respond with zero when operation is successfully completed, or non-zero
> + otherwise.
> +
> VHOST_USER_PROTOCOL_F_REPLY_ACK:
> -------------------------------
> The original vhost-user specification only demands replies for certain
> diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
> index 093675e..8b94688 100644
> --- a/hw/virtio/vhost-user.c
> +++ b/hw/virtio/vhost-user.c
> @@ -26,6 +26,11 @@
> #define VHOST_MEMORY_MAX_NREGIONS 8
> #define VHOST_USER_F_PROTOCOL_FEATURES 30
>
> +/*
> + * Maximum size of virtio device config space
> + */
> +#define VHOST_USER_MAX_CONFIG_SIZE 256
> +
> enum VhostUserProtocolFeature {
> VHOST_USER_PROTOCOL_F_MQ = 0,
> VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
> @@ -65,12 +70,15 @@ typedef enum VhostUserRequest {
> VHOST_USER_SET_SLAVE_REQ_FD = 21,
> VHOST_USER_IOTLB_MSG = 22,
> VHOST_USER_SET_VRING_ENDIAN = 23,
> + VHOST_USER_GET_CONFIG = 24,
> + VHOST_USER_SET_CONFIG = 25,
> VHOST_USER_MAX
> } VhostUserRequest;
>
> typedef enum VhostUserSlaveRequest {
> VHOST_USER_SLAVE_NONE = 0,
> VHOST_USER_SLAVE_IOTLB_MSG = 1,
> + VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
> VHOST_USER_SLAVE_MAX
> } VhostUserSlaveRequest;
>
> @@ -92,6 +100,18 @@ typedef struct VhostUserLog {
> uint64_t mmap_offset;
> } VhostUserLog;
>
> +typedef struct VhostUserConfig {
> + uint32_t offset;
> + uint32_t size;
> + uint32_t flags;
> + uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
> +} VhostUserConfig;
> +
> +static VhostUserConfig c __attribute__ ((unused));
> +#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
> + + sizeof(c.size) \
> + + sizeof(c.flags))
> +
> typedef struct VhostUserMsg {
> VhostUserRequest request;
>
> @@ -109,6 +129,7 @@ typedef struct VhostUserMsg {
> VhostUserMemory memory;
> VhostUserLog log;
> struct vhost_iotlb_msg iotlb;
> + VhostUserConfig config;
> } payload;
> } QEMU_PACKED VhostUserMsg;
>
> @@ -608,6 +629,21 @@ static int vhost_user_reset_device(struct vhost_dev *dev)
> return 0;
> }
>
> +static int vhost_user_slave_handle_config_change(struct vhost_dev *dev)
> +{
> + int ret = -1;
> +
> + if (!dev->config_ops) {
> + return -1;
> + }
> +
> + if (dev->config_ops->vhost_dev_config_notifier) {
> + ret = dev->config_ops->vhost_dev_config_notifier(dev);
> + }
> +
> + return ret;
> +}
> +
> static void slave_read(void *opaque)
> {
> struct vhost_dev *dev = opaque;
> @@ -640,6 +676,9 @@ static void slave_read(void *opaque)
> case VHOST_USER_SLAVE_IOTLB_MSG:
> ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb);
> break;
> + case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
> + ret = vhost_user_slave_handle_config_change(dev);
> + break;
> default:
> error_report("Received unexpected msg type.");
> ret = -EINVAL;
> @@ -922,6 +961,83 @@ static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled)
> /* No-op as the receive channel is not dedicated to IOTLB messages. */
> }
>
> +static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config,
> + uint32_t config_len)
> +{
> + VhostUserMsg msg = {
> + msg.request = VHOST_USER_GET_CONFIG,
> + msg.flags = VHOST_USER_VERSION,
> + msg.size = VHOST_USER_CONFIG_HDR_SIZE + config_len,
Not really clear to me why we would need to send config_len zero's for region[].
> + };
> +
> + if (config_len > VHOST_USER_MAX_CONFIG_SIZE) {
> + return -1;
> + }
> +
> + msg.payload.config.offset = 0;
> + msg.payload.config.size = config_len;
> + if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
> + return -1;
> + }
> +
> + if (vhost_user_read(dev, &msg) < 0) {
> + return -1;
> + }
> +
> + if (msg.request != VHOST_USER_GET_CONFIG) {
> + error_report("Received unexpected msg type. Expected %d received %d",
> + VHOST_USER_GET_CONFIG, msg.request);
> + return -1;
> + }
> +
> + if (msg.size != VHOST_USER_CONFIG_HDR_SIZE + config_len) {
> + error_report("Received bad msg size.");
> + return -1;
> + }
> +
> + memcpy(config, msg.payload.config.region, config_len);
> +
> + return 0;
> +}
> +
> +static int vhost_user_set_config(struct vhost_dev *dev, const uint8_t *data,
> + uint32_t offset, uint32_t size, uint32_t flags)
> +{
> + uint8_t *p;
> + bool reply_supported = virtio_has_feature(dev->protocol_features,
> + VHOST_USER_PROTOCOL_F_REPLY_ACK);
> +
> + VhostUserMsg msg = {
> + msg.request = VHOST_USER_SET_CONFIG,
> + msg.flags = VHOST_USER_VERSION,
> + msg.size = VHOST_USER_CONFIG_HDR_SIZE + size,
> + };
> +
> + if (reply_supported) {
> + msg.flags |= VHOST_USER_NEED_REPLY_MASK;
> + }
> +
> + if (size > VHOST_USER_MAX_CONFIG_SIZE) {
> + return -1;
> + }
> +
> + msg.payload.config.offset = offset,
> + msg.payload.config.size = size,
> + msg.payload.config.flags = flags,
> + p = msg.payload.config.region;
> + memcpy(p, data, size);
> +
> + if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
> + return -1;
> + }
> +
> + if (reply_supported) {
> + return process_message_reply(dev, &msg);
> + }
> +
> + return 0;
> +}
> +
> const VhostOps user_ops = {
> .backend_type = VHOST_BACKEND_TYPE_USER,
> .vhost_backend_init = vhost_user_init,
> @@ -948,4 +1064,6 @@ const VhostOps user_ops = {
> .vhost_net_set_mtu = vhost_user_net_set_mtu,
> .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
> .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg,
> + .vhost_get_config = vhost_user_get_config,
> + .vhost_set_config = vhost_user_set_config,
> };
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index e4290ce..386aef8 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -1505,6 +1505,38 @@ void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
> }
> }
>
> +int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
> + uint32_t config_len)
> +{
> + assert(hdev->vhost_ops);
> +
> + if (hdev->vhost_ops->vhost_get_config) {
> + return hdev->vhost_ops->vhost_get_config(hdev, config, config_len);
> + }
> +
> + return -1;
> +}
> +
> +int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
> + uint32_t offset, uint32_t size, uint32_t flags)
> +{
> + assert(hdev->vhost_ops);
> +
> + if (hdev->vhost_ops->vhost_set_config) {
> + return hdev->vhost_ops->vhost_set_config(hdev, data, offset,
> + size, flags);
> + }
> +
> + return -1;
> +}
> +
> +void vhost_dev_set_config_notifier(struct vhost_dev *hdev,
> + const VhostDevConfigOps *ops)
> +{
> + assert(hdev->vhost_ops);
> + hdev->config_ops = ops;
> +}
> +
> /* Host notifiers must be enabled at this point. */
> int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
> {
> diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
> index a7a5f22..592254f 100644
> --- a/include/hw/virtio/vhost-backend.h
> +++ b/include/hw/virtio/vhost-backend.h
> @@ -20,6 +20,11 @@ typedef enum VhostBackendType {
> VHOST_BACKEND_TYPE_MAX = 3,
> } VhostBackendType;
>
> +typedef enum VhostSetConfigType {
> + VHOST_SET_CONFIG_TYPE_MASTER = 0,
> + VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
> +} VhostSetConfigType;
> +
> struct vhost_dev;
> struct vhost_log;
> struct vhost_memory;
> @@ -84,6 +89,11 @@ typedef void (*vhost_set_iotlb_callback_op)(struct vhost_dev *dev,
> int enabled);
> typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev,
> struct vhost_iotlb_msg *imsg);
> +typedef int (*vhost_set_config_op)(struct vhost_dev *dev, const uint8_t *data,
> + uint32_t offset, uint32_t size,
> + uint32_t flags);
> +typedef int (*vhost_get_config_op)(struct vhost_dev *dev, uint8_t *config,
> + uint32_t config_len);
>
> typedef struct VhostOps {
> VhostBackendType backend_type;
> @@ -118,6 +128,8 @@ typedef struct VhostOps {
> vhost_vsock_set_running_op vhost_vsock_set_running;
> vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
> vhost_send_device_iotlb_msg_op vhost_send_device_iotlb_msg;
> + vhost_get_config_op vhost_get_config;
> + vhost_set_config_op vhost_set_config;
> } VhostOps;
>
> extern const VhostOps user_ops;
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index 467dc77..1dc2d73 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -46,6 +46,12 @@ struct vhost_iommu {
> QLIST_ENTRY(vhost_iommu) iommu_next;
> };
>
> +typedef struct VhostDevConfigOps {
> + /* Vhost device config space changed callback
> + */
> + int (*vhost_dev_config_notifier)(struct vhost_dev *dev);
> +} VhostDevConfigOps;
> +
> struct vhost_memory;
> struct vhost_dev {
> VirtIODevice *vdev;
> @@ -76,6 +82,7 @@ struct vhost_dev {
> QLIST_ENTRY(vhost_dev) entry;
> QLIST_HEAD(, vhost_iommu) iommu_list;
> IOMMUNotifier n;
> + const VhostDevConfigOps *config_ops;
> };
>
> int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
> @@ -106,4 +113,12 @@ int vhost_net_set_backend(struct vhost_dev *hdev,
> struct vhost_vring_file *file);
>
> int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
> +int vhost_dev_get_config(struct vhost_dev *dev, uint8_t *config,
> + uint32_t config_len);
> +int vhost_dev_set_config(struct vhost_dev *dev, const uint8_t *data,
> + uint32_t offset, uint32_t size, uint32_t flags);
> +/* notifier callback in case vhost device config space changed
> + */
> +void vhost_dev_set_config_notifier(struct vhost_dev *dev,
> + const VhostDevConfigOps *ops);
> #endif
> --
> 1.9.3
>
>
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space
2018-01-02 15:19 ` Marc-André Lureau
@ 2018-01-03 1:36 ` Liu, Changpeng
0 siblings, 0 replies; 13+ messages in thread
From: Liu, Changpeng @ 2018-01-03 1:36 UTC (permalink / raw)
To: Marc-André Lureau
Cc: QEMU, Harris, James R, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
> -----Original Message-----
> From: Marc-André Lureau [mailto:marcandre.lureau@gmail.com]
> Sent: Tuesday, January 2, 2018 11:20 PM
> To: Liu, Changpeng <changpeng.liu@intel.com>
> Cc: QEMU <qemu-devel@nongnu.org>; Harris, James R <james.r.harris@intel.com>;
> Michael S. Tsirkin <mst@redhat.com>; Stefan Hajnoczi <stefanha@gmail.com>;
> Paolo Bonzini <pbonzini@redhat.com>; Felipe Franciosi <felipe@nutanix.com>
> Subject: Re: [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages
> to support virtio config space
>
> Hi
>
> On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> > Add VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages which can
> be
> > used for live migration of vhost user devices, also vhost user devices
> > can benefit from the messages to get/set virtio config space from/to the
> > I/O target. For the purpose to support virtio config space change,
> > VHOST_USER_SLAVE_CONFIG_CHANGE_MSG message is added as the event
> notifier
> > in case virtio config space change in the slave I/O target.
> >
> > Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> > ---
> > docs/interop/vhost-user.txt | 53 +++++++++++++++++
> > hw/virtio/vhost-user.c | 118 ++++++++++++++++++++++++++++++++++++++
> > hw/virtio/vhost.c | 32 +++++++++++
> > include/hw/virtio/vhost-backend.h | 12 ++++
> > include/hw/virtio/vhost.h | 15 +++++
> > 5 files changed, 230 insertions(+)
> >
> > diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt
> > index 954771d..1788fff 100644
> > --- a/docs/interop/vhost-user.txt
> > +++ b/docs/interop/vhost-user.txt
> > @@ -116,6 +116,19 @@ Depending on the request type, payload can be:
> > - 3: IOTLB invalidate
> > - 4: IOTLB access fail
> >
> > + * Virtio device config space
> > + -----------------------------------
> > + | offset | size | flags | payload |
> > + -----------------------------------
> > +
> > + Offset: a 32-bit offset of virtio device's configuration space
> > + Size: a 32-bit configuration space access size in bytes
> > + Flags: a 32-bit value:
> > + - 0: Vhost master messages used for writeable fields
> > + - 1: Vhost master messages used for live migration
> > + Payload: Size bytes array holding the contents of the virtio
> > + device's configuration space
> > +
> > In QEMU the vhost-user message is implemented with the following struct:
> >
> > typedef struct VhostUserMsg {
> > @@ -129,6 +142,7 @@ typedef struct VhostUserMsg {
> > VhostUserMemory memory;
> > VhostUserLog log;
> > struct vhost_iotlb_msg iotlb;
> > + VhostUserConfig config;
> > };
> > } QEMU_PACKED VhostUserMsg;
> >
> > @@ -596,6 +610,30 @@ Master message types
> > and expect this message once (per VQ) during device configuration
> > (ie. before the master starts the VQ).
> >
> > + * VHOST_USER_GET_CONFIG
> > +
> > + Id: 24
> > + Equivalent ioctl: N/A
>
> Please document the Master payload. (msg.size != 0)
Ok, will add.
>
>
> > + Slave payload: virtio device config space
> > +
> > + Submitted by the vhost-user master to fetch the contents of the virtio
> > + device configuration space, vhost-user slave's payload size MUST match
> > + master's request, vhost-user slave uses zero length of payload to
> > + indicate an error to vhost-user master. The vhost-user master may
> > + cache the contents to avoid repeated VHOST_USER_GET_CONFIG calls.
> > +
> > +* VHOST_USER_SET_CONFIG
> > +
> > + Id: 25
> > + Equivalent ioctl: N/A
>
> Same here
Ok.
>
> > + Master payload: virtio device config space
> > +
> > + Submitted by the vhost-user master when the Guest changes the virtio
> > + device configuration space and also can be used for live migration
> > + on the destination host. The vhost-user slave must check the flags
> > + field, and slaves MUST NOT accept SET_CONFIG for read-only
> > + configuration space fields unless the live migration bit is set.
> > +
> > Slave message types
> > -------------------
> >
> > @@ -614,6 +652,21 @@ Slave message types
> > This request should be send only when VIRTIO_F_IOMMU_PLATFORM
> feature
> > has been successfully negotiated.
> >
> > +* VHOST_USER_SLAVE_CONFIG_CHANGE_MSG
> > +
> > + Id: 2
> > + Equivalent ioctl: N/A
> > + Slave payload: N/A
> > + Master payload: N/A
> > +
> > + Vhost-user slave sends such messages to notify that the virtio device's
> > + configuration space has changed, for those host devices which can support
> > + such feature, host driver can send VHOST_USER_GET_CONFIG message to
> slave
> > + to get the latest content. If VHOST_USER_PROTOCOL_F_REPLY_ACK is
> > + negotiated, and slave set the VHOST_USER_NEED_REPLY flag, master must
> > + respond with zero when operation is successfully completed, or non-zero
> > + otherwise.
> > +
> > VHOST_USER_PROTOCOL_F_REPLY_ACK:
> > -------------------------------
> > The original vhost-user specification only demands replies for certain
> > diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
> > index 093675e..8b94688 100644
> > --- a/hw/virtio/vhost-user.c
> > +++ b/hw/virtio/vhost-user.c
> > @@ -26,6 +26,11 @@
> > #define VHOST_MEMORY_MAX_NREGIONS 8
> > #define VHOST_USER_F_PROTOCOL_FEATURES 30
> >
> > +/*
> > + * Maximum size of virtio device config space
> > + */
> > +#define VHOST_USER_MAX_CONFIG_SIZE 256
> > +
> > enum VhostUserProtocolFeature {
> > VHOST_USER_PROTOCOL_F_MQ = 0,
> > VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
> > @@ -65,12 +70,15 @@ typedef enum VhostUserRequest {
> > VHOST_USER_SET_SLAVE_REQ_FD = 21,
> > VHOST_USER_IOTLB_MSG = 22,
> > VHOST_USER_SET_VRING_ENDIAN = 23,
> > + VHOST_USER_GET_CONFIG = 24,
> > + VHOST_USER_SET_CONFIG = 25,
> > VHOST_USER_MAX
> > } VhostUserRequest;
> >
> > typedef enum VhostUserSlaveRequest {
> > VHOST_USER_SLAVE_NONE = 0,
> > VHOST_USER_SLAVE_IOTLB_MSG = 1,
> > + VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
> > VHOST_USER_SLAVE_MAX
> > } VhostUserSlaveRequest;
> >
> > @@ -92,6 +100,18 @@ typedef struct VhostUserLog {
> > uint64_t mmap_offset;
> > } VhostUserLog;
> >
> > +typedef struct VhostUserConfig {
> > + uint32_t offset;
> > + uint32_t size;
> > + uint32_t flags;
> > + uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
> > +} VhostUserConfig;
> > +
> > +static VhostUserConfig c __attribute__ ((unused));
> > +#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
> > + + sizeof(c.size) \
> > + + sizeof(c.flags))
> > +
> > typedef struct VhostUserMsg {
> > VhostUserRequest request;
> >
> > @@ -109,6 +129,7 @@ typedef struct VhostUserMsg {
> > VhostUserMemory memory;
> > VhostUserLog log;
> > struct vhost_iotlb_msg iotlb;
> > + VhostUserConfig config;
> > } payload;
> > } QEMU_PACKED VhostUserMsg;
> >
> > @@ -608,6 +629,21 @@ static int vhost_user_reset_device(struct vhost_dev
> *dev)
> > return 0;
> > }
> >
> > +static int vhost_user_slave_handle_config_change(struct vhost_dev *dev)
> > +{
> > + int ret = -1;
> > +
> > + if (!dev->config_ops) {
> > + return -1;
> > + }
> > +
> > + if (dev->config_ops->vhost_dev_config_notifier) {
> > + ret = dev->config_ops->vhost_dev_config_notifier(dev);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > static void slave_read(void *opaque)
> > {
> > struct vhost_dev *dev = opaque;
> > @@ -640,6 +676,9 @@ static void slave_read(void *opaque)
> > case VHOST_USER_SLAVE_IOTLB_MSG:
> > ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb);
> > break;
> > + case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
> > + ret = vhost_user_slave_handle_config_change(dev);
> > + break;
> > default:
> > error_report("Received unexpected msg type.");
> > ret = -EINVAL;
> > @@ -922,6 +961,83 @@ static void vhost_user_set_iotlb_callback(struct
> vhost_dev *dev, int enabled)
> > /* No-op as the receive channel is not dedicated to IOTLB messages. */
> > }
> >
> > +static int vhost_user_get_config(struct vhost_dev *dev, uint8_t *config,
> > + uint32_t config_len)
> > +{
> > + VhostUserMsg msg = {
> > + msg.request = VHOST_USER_GET_CONFIG,
> > + msg.flags = VHOST_USER_VERSION,
> > + msg.size = VHOST_USER_CONFIG_HDR_SIZE + config_len,
>
> Not really clear to me why we would need to send config_len zero's for region[].
It's better to let host device driver, e.g: vhost-user-blk driver to get the whole configuration space
at once, some parameters inside the struct virtio_blk_config may not continuous, so that the master
should send such request for many times.
>
> > + };
> > +
> > + if (config_len > VHOST_USER_MAX_CONFIG_SIZE) {
> > + return -1;
> > + }
> > +
> > + msg.payload.config.offset = 0;
> > + msg.payload.config.size = config_len;
> > + if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
> > + return -1;
> > + }
> > +
> > + if (vhost_user_read(dev, &msg) < 0) {
> > + return -1;
> > + }
> > +
> > + if (msg.request != VHOST_USER_GET_CONFIG) {
> > + error_report("Received unexpected msg type. Expected %d received %d",
> > + VHOST_USER_GET_CONFIG, msg.request);
> > + return -1;
> > + }
> > +
> > + if (msg.size != VHOST_USER_CONFIG_HDR_SIZE + config_len) {
> > + error_report("Received bad msg size.");
> > + return -1;
> > + }
> > +
> > + memcpy(config, msg.payload.config.region, config_len);
> > +
> > + return 0;
> > +}
> > +
> > +static int vhost_user_set_config(struct vhost_dev *dev, const uint8_t *data,
> > + uint32_t offset, uint32_t size, uint32_t flags)
> > +{
> > + uint8_t *p;
> > + bool reply_supported = virtio_has_feature(dev->protocol_features,
> > + VHOST_USER_PROTOCOL_F_REPLY_ACK);
> > +
> > + VhostUserMsg msg = {
> > + msg.request = VHOST_USER_SET_CONFIG,
> > + msg.flags = VHOST_USER_VERSION,
> > + msg.size = VHOST_USER_CONFIG_HDR_SIZE + size,
> > + };
> > +
> > + if (reply_supported) {
> > + msg.flags |= VHOST_USER_NEED_REPLY_MASK;
> > + }
> > +
> > + if (size > VHOST_USER_MAX_CONFIG_SIZE) {
> > + return -1;
> > + }
> > +
> > + msg.payload.config.offset = offset,
> > + msg.payload.config.size = size,
> > + msg.payload.config.flags = flags,
> > + p = msg.payload.config.region;
> > + memcpy(p, data, size);
> > +
> > + if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
> > + return -1;
> > + }
> > +
> > + if (reply_supported) {
> > + return process_message_reply(dev, &msg);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > const VhostOps user_ops = {
> > .backend_type = VHOST_BACKEND_TYPE_USER,
> > .vhost_backend_init = vhost_user_init,
> > @@ -948,4 +1064,6 @@ const VhostOps user_ops = {
> > .vhost_net_set_mtu = vhost_user_net_set_mtu,
> > .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
> > .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg,
> > + .vhost_get_config = vhost_user_get_config,
> > + .vhost_set_config = vhost_user_set_config,
> > };
> > diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> > index e4290ce..386aef8 100644
> > --- a/hw/virtio/vhost.c
> > +++ b/hw/virtio/vhost.c
> > @@ -1505,6 +1505,38 @@ void vhost_ack_features(struct vhost_dev *hdev,
> const int *feature_bits,
> > }
> > }
> >
> > +int vhost_dev_get_config(struct vhost_dev *hdev, uint8_t *config,
> > + uint32_t config_len)
> > +{
> > + assert(hdev->vhost_ops);
> > +
> > + if (hdev->vhost_ops->vhost_get_config) {
> > + return hdev->vhost_ops->vhost_get_config(hdev, config, config_len);
> > + }
> > +
> > + return -1;
> > +}
> > +
> > +int vhost_dev_set_config(struct vhost_dev *hdev, const uint8_t *data,
> > + uint32_t offset, uint32_t size, uint32_t flags)
> > +{
> > + assert(hdev->vhost_ops);
> > +
> > + if (hdev->vhost_ops->vhost_set_config) {
> > + return hdev->vhost_ops->vhost_set_config(hdev, data, offset,
> > + size, flags);
> > + }
> > +
> > + return -1;
> > +}
> > +
> > +void vhost_dev_set_config_notifier(struct vhost_dev *hdev,
> > + const VhostDevConfigOps *ops)
> > +{
> > + assert(hdev->vhost_ops);
> > + hdev->config_ops = ops;
> > +}
> > +
> > /* Host notifiers must be enabled at this point. */
> > int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
> > {
> > diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-
> backend.h
> > index a7a5f22..592254f 100644
> > --- a/include/hw/virtio/vhost-backend.h
> > +++ b/include/hw/virtio/vhost-backend.h
> > @@ -20,6 +20,11 @@ typedef enum VhostBackendType {
> > VHOST_BACKEND_TYPE_MAX = 3,
> > } VhostBackendType;
> >
> > +typedef enum VhostSetConfigType {
> > + VHOST_SET_CONFIG_TYPE_MASTER = 0,
> > + VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
> > +} VhostSetConfigType;
> > +
> > struct vhost_dev;
> > struct vhost_log;
> > struct vhost_memory;
> > @@ -84,6 +89,11 @@ typedef void (*vhost_set_iotlb_callback_op)(struct
> vhost_dev *dev,
> > int enabled);
> > typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev,
> > struct vhost_iotlb_msg *imsg);
> > +typedef int (*vhost_set_config_op)(struct vhost_dev *dev, const uint8_t *data,
> > + uint32_t offset, uint32_t size,
> > + uint32_t flags);
> > +typedef int (*vhost_get_config_op)(struct vhost_dev *dev, uint8_t *config,
> > + uint32_t config_len);
> >
> > typedef struct VhostOps {
> > VhostBackendType backend_type;
> > @@ -118,6 +128,8 @@ typedef struct VhostOps {
> > vhost_vsock_set_running_op vhost_vsock_set_running;
> > vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
> > vhost_send_device_iotlb_msg_op vhost_send_device_iotlb_msg;
> > + vhost_get_config_op vhost_get_config;
> > + vhost_set_config_op vhost_set_config;
> > } VhostOps;
> >
> > extern const VhostOps user_ops;
> > diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> > index 467dc77..1dc2d73 100644
> > --- a/include/hw/virtio/vhost.h
> > +++ b/include/hw/virtio/vhost.h
> > @@ -46,6 +46,12 @@ struct vhost_iommu {
> > QLIST_ENTRY(vhost_iommu) iommu_next;
> > };
> >
> > +typedef struct VhostDevConfigOps {
> > + /* Vhost device config space changed callback
> > + */
> > + int (*vhost_dev_config_notifier)(struct vhost_dev *dev);
> > +} VhostDevConfigOps;
> > +
> > struct vhost_memory;
> > struct vhost_dev {
> > VirtIODevice *vdev;
> > @@ -76,6 +82,7 @@ struct vhost_dev {
> > QLIST_ENTRY(vhost_dev) entry;
> > QLIST_HEAD(, vhost_iommu) iommu_list;
> > IOMMUNotifier n;
> > + const VhostDevConfigOps *config_ops;
> > };
> >
> > int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
> > @@ -106,4 +113,12 @@ int vhost_net_set_backend(struct vhost_dev *hdev,
> > struct vhost_vring_file *file);
> >
> > int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
> > +int vhost_dev_get_config(struct vhost_dev *dev, uint8_t *config,
> > + uint32_t config_len);
> > +int vhost_dev_set_config(struct vhost_dev *dev, const uint8_t *data,
> > + uint32_t offset, uint32_t size, uint32_t flags);
> > +/* notifier callback in case vhost device config space changed
> > + */
> > +void vhost_dev_set_config_notifier(struct vhost_dev *dev,
> > + const VhostDevConfigOps *ops);
> > #endif
> > --
> > 1.9.3
> >
> >
>
>
>
> --
> Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 2/4] vhost-user-blk: introduce a new vhost-user-blk host device
2018-01-02 3:55 [Qemu-devel] [PATCH v8 0/4] Introduce a new vhost-user-blk host device to QEMU Changpeng Liu
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space Changpeng Liu
@ 2018-01-02 3:55 ` Changpeng Liu
2018-01-02 15:21 ` Marc-André Lureau
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages Changpeng Liu
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application Changpeng Liu
3 siblings, 1 reply; 13+ messages in thread
From: Changpeng Liu @ 2018-01-02 3:55 UTC (permalink / raw)
To: changpeng.liu, qemu-devel
Cc: stefanha, pbonzini, mst, marcandre.lureau, felipe, james.r.harris
This commit introduces a new vhost-user device for block, it uses a
chardev to connect with the backend, same with Qemu virito-blk device,
Guest OS still uses the virtio-blk frontend driver.
To use it, start QEMU with command line like this:
qemu-system-x86_64 \
-chardev socket,id=char0,path=/path/vhost.socket \
-device vhost-user-blk-pci,chardev=char0,num-queues=2, \
bootindex=2... \
Users can use different parameters for `num-queues` and `bootindex`.
Different with exist Qemu virtio-blk host device, it makes more easy
for users to implement their own I/O processing logic, such as all
user space I/O stack against hardware block device. It uses the new
vhost messages(VHOST_USER_GET_CONFIG) to get block virtio config
information from backend process.
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
---
default-configs/pci.mak | 1 +
hw/block/Makefile.objs | 3 +
hw/block/vhost-user-blk.c | 359 +++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 55 ++++++
hw/virtio/virtio-pci.h | 18 ++
include/hw/virtio/vhost-user-blk.h | 41 +++++
6 files changed, 477 insertions(+)
create mode 100644 hw/block/vhost-user-blk.c
create mode 100644 include/hw/virtio/vhost-user-blk.h
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index e514bde..49a0f28 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -43,3 +43,4 @@ CONFIG_VGA_PCI=y
CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM)
CONFIG_ROCKER=y
CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
+CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index e0ed980..4c19a58 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -13,3 +13,6 @@ obj-$(CONFIG_SH4) += tc58128.o
obj-$(CONFIG_VIRTIO) += virtio-blk.o
obj-$(CONFIG_VIRTIO) += dataplane/
+ifeq ($(CONFIG_VIRTIO),y)
+obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o
+endif
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
new file mode 100644
index 0000000..b53b4c9
--- /dev/null
+++ b/hw/block/vhost-user-blk.c
@@ -0,0 +1,359 @@
+/*
+ * vhost-user-blk host device
+ *
+ * Copyright(C) 2017 Intel Corporation.
+ *
+ * Authors:
+ * Changpeng Liu <changpeng.liu@intel.com>
+ *
+ * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by:
+ * Felipe Franciosi <felipe@nutanix.com>
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Nicholas Bellinger <nab@risingtidesystems.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/typedefs.h"
+#include "qemu/cutils.h"
+#include "qom/object.h"
+#include "hw/qdev-core.h"
+#include "hw/virtio/vhost.h"
+#include "hw/virtio/vhost-user-blk.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
+
+static const int user_feature_bits[] = {
+ VIRTIO_BLK_F_SIZE_MAX,
+ VIRTIO_BLK_F_SEG_MAX,
+ VIRTIO_BLK_F_GEOMETRY,
+ VIRTIO_BLK_F_BLK_SIZE,
+ VIRTIO_BLK_F_TOPOLOGY,
+ VIRTIO_BLK_F_MQ,
+ VIRTIO_BLK_F_RO,
+ VIRTIO_BLK_F_FLUSH,
+ VIRTIO_BLK_F_CONFIG_WCE,
+ VIRTIO_F_VERSION_1,
+ VIRTIO_RING_F_INDIRECT_DESC,
+ VIRTIO_RING_F_EVENT_IDX,
+ VIRTIO_F_NOTIFY_ON_EMPTY,
+ VHOST_INVALID_FEATURE_BIT
+};
+
+static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+
+ memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
+}
+
+static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
+ int ret;
+
+ if (blkcfg->wce == s->blkcfg.wce) {
+ return;
+ }
+
+ ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
+ offsetof(struct virtio_blk_config, wce),
+ sizeof(blkcfg->wce),
+ VHOST_SET_CONFIG_TYPE_MASTER);
+ if (ret) {
+ error_report("set device config space failed");
+ return;
+ }
+
+ s->blkcfg.wce = blkcfg->wce;
+}
+
+static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
+{
+ int ret;
+ struct virtio_blk_config blkcfg;
+ VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
+
+ ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
+ sizeof(struct virtio_blk_config));
+ if (ret < 0) {
+ error_report("get config space failed");
+ return -1;
+ }
+
+ /* valid for resize only */
+ if (blkcfg.capacity != s->blkcfg.capacity) {
+ s->blkcfg.capacity = blkcfg.capacity;
+ memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config));
+ virtio_notify_config(dev->vdev);
+ }
+
+ return 0;
+}
+
+const VhostDevConfigOps blk_ops = {
+ .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
+};
+
+static void vhost_user_blk_start(VirtIODevice *vdev)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int i, ret;
+
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
+ return;
+ }
+
+ ret = vhost_dev_enable_notifiers(&s->dev, vdev);
+ if (ret < 0) {
+ error_report("Error enabling host notifiers: %d", -ret);
+ return;
+ }
+
+ ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
+ if (ret < 0) {
+ error_report("Error binding guest notifier: %d", -ret);
+ goto err_host_notifiers;
+ }
+
+ s->dev.acked_features = vdev->guest_features;
+ ret = vhost_dev_start(&s->dev, vdev);
+ if (ret < 0) {
+ error_report("Error starting vhost: %d", -ret);
+ goto err_guest_notifiers;
+ }
+
+ /* guest_notifier_mask/pending not used yet, so just unmask
+ * everything here. virtio-pci will do the right thing by
+ * enabling/disabling irqfd.
+ */
+ for (i = 0; i < s->dev.nvqs; i++) {
+ vhost_virtqueue_mask(&s->dev, vdev, i, false);
+ }
+
+ return;
+
+err_guest_notifiers:
+ k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
+err_host_notifiers:
+ vhost_dev_disable_notifiers(&s->dev, vdev);
+}
+
+static void vhost_user_blk_stop(VirtIODevice *vdev)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+
+ if (!k->set_guest_notifiers) {
+ return;
+ }
+
+ vhost_dev_stop(&s->dev, vdev);
+
+ ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
+ if (ret < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", ret);
+ return;
+ }
+
+ vhost_dev_disable_notifiers(&s->dev, vdev);
+}
+
+static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
+
+ if (!vdev->vm_running) {
+ should_start = false;
+ }
+
+ if (s->dev.started == should_start) {
+ return;
+ }
+
+ if (should_start) {
+ vhost_user_blk_start(vdev);
+ } else {
+ vhost_user_blk_stop(vdev);
+ }
+
+}
+
+static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
+ uint64_t features,
+ Error **errp)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ uint64_t get_features;
+
+ /* Turn on pre-defined features */
+ virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
+ virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
+ virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
+ virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
+ virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
+
+ if (s->config_wce) {
+ virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
+ }
+ if (s->config_ro) {
+ virtio_add_feature(&features, VIRTIO_BLK_F_RO);
+ }
+ if (s->num_queues > 1) {
+ virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
+ }
+
+ get_features = vhost_get_features(&s->dev, user_feature_bits, features);
+
+ return get_features;
+}
+
+static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+
+}
+
+static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserBlk *s = VHOST_USER_BLK(vdev);
+ int i, ret;
+
+ if (!s->chardev.chr) {
+ error_setg(errp, "vhost-user-blk: chardev is mandatory");
+ return;
+ }
+
+ if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
+ error_setg(errp, "vhost-user-blk: invalid number of IO queues");
+ return;
+ }
+
+ if (!s->queue_size) {
+ error_setg(errp, "vhost-user-blk: queue size must be non-zero");
+ return;
+ }
+
+ virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
+ sizeof(struct virtio_blk_config));
+
+ for (i = 0; i < s->num_queues; i++) {
+ virtio_add_queue(vdev, s->queue_size,
+ vhost_user_blk_handle_output);
+ }
+
+ s->dev.nvqs = s->num_queues;
+ s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs);
+ s->dev.vq_index = 0;
+ s->dev.backend_features = 0;
+
+ ret = vhost_dev_init(&s->dev, &s->chardev, VHOST_BACKEND_TYPE_USER, 0);
+ if (ret < 0) {
+ error_setg(errp, "vhost-user-blk: vhost initialization failed: %s",
+ strerror(-ret));
+ goto virtio_err;
+ }
+
+ ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
+ sizeof(struct virtio_blk_config));
+ if (ret < 0) {
+ error_setg(errp, "vhost-user-blk: get block config failed");
+ goto vhost_err;
+ }
+
+ if (s->blkcfg.num_queues != s->num_queues) {
+ s->blkcfg.num_queues = s->num_queues;
+ }
+
+ vhost_dev_set_config_notifier(&s->dev, &blk_ops);
+
+ return;
+
+vhost_err:
+ vhost_dev_cleanup(&s->dev);
+virtio_err:
+ g_free(s->dev.vqs);
+ virtio_cleanup(vdev);
+}
+
+static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserBlk *s = VHOST_USER_BLK(dev);
+
+ vhost_user_blk_set_status(vdev, 0);
+ vhost_dev_cleanup(&s->dev);
+ g_free(s->dev.vqs);
+ virtio_cleanup(vdev);
+}
+
+static void vhost_user_blk_instance_init(Object *obj)
+{
+ VHostUserBlk *s = VHOST_USER_BLK(obj);
+
+ device_add_bootindex_property(obj, &s->bootindex, "bootindex",
+ "/disk@0,0", DEVICE(obj), NULL);
+}
+
+static const VMStateDescription vmstate_vhost_user_blk = {
+ .name = "vhost-user-blk",
+ .minimum_version_id = 1,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_VIRTIO_DEVICE,
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static Property vhost_user_blk_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
+ DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1),
+ DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
+ DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true),
+ DEFINE_PROP_BIT("config-ro", VHostUserBlk, config_ro, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ dc->props = vhost_user_blk_properties;
+ dc->vmsd = &vmstate_vhost_user_blk;
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ vdc->realize = vhost_user_blk_device_realize;
+ vdc->unrealize = vhost_user_blk_device_unrealize;
+ vdc->get_config = vhost_user_blk_update_config;
+ vdc->set_config = vhost_user_blk_set_config;
+ vdc->get_features = vhost_user_blk_get_features;
+ vdc->set_status = vhost_user_blk_set_status;
+}
+
+static const TypeInfo vhost_user_blk_info = {
+ .name = TYPE_VHOST_USER_BLK,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VHostUserBlk),
+ .instance_init = vhost_user_blk_instance_init,
+ .class_init = vhost_user_blk_class_init,
+};
+
+static void virtio_register_types(void)
+{
+ type_register_static(&vhost_user_blk_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index e92837c..b5e05d9 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1976,6 +1976,58 @@ static const TypeInfo virtio_blk_pci_info = {
.class_init = virtio_blk_pci_class_init,
};
+#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
+/* vhost-user-blk */
+
+static Property vhost_user_blk_pci_properties[] = {
+ DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+ dc->props = vhost_user_blk_pci_properties;
+ k->realize = vhost_user_blk_pci_realize;
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
+ pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+ pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void vhost_user_blk_pci_instance_init(Object *obj)
+{
+ VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_BLK);
+ object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
+ "bootindex", &error_abort);
+}
+
+static const TypeInfo vhost_user_blk_pci_info = {
+ .name = TYPE_VHOST_USER_BLK_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VHostUserBlkPCI),
+ .instance_init = vhost_user_blk_pci_instance_init,
+ .class_init = vhost_user_blk_pci_class_init,
+};
+#endif
+
/* virtio-scsi-pci */
static Property virtio_scsi_pci_properties[] = {
@@ -2622,6 +2674,9 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_9p_pci_info);
#endif
type_register_static(&virtio_blk_pci_info);
+#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
+ type_register_static(&vhost_user_blk_pci_info);
+#endif
type_register_static(&virtio_scsi_pci_info);
type_register_static(&virtio_balloon_pci_info);
type_register_static(&virtio_serial_pci_info);
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 12d3a90..813082b 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -27,6 +27,9 @@
#include "hw/virtio/virtio-gpu.h"
#include "hw/virtio/virtio-crypto.h"
#include "hw/virtio/vhost-user-scsi.h"
+#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
+#include "hw/virtio/vhost-user-blk.h"
+#endif
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
@@ -46,6 +49,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
+typedef struct VHostUserBlkPCI VHostUserBlkPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
typedef struct VirtIOInputPCI VirtIOInputPCI;
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
@@ -244,6 +248,20 @@ struct VHostUserSCSIPCI {
VHostUserSCSI vdev;
};
+#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
+/*
+ * vhost-user-blk-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci"
+#define VHOST_USER_BLK_PCI(obj) \
+ OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI)
+
+struct VHostUserBlkPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserBlk vdev;
+};
+#endif
+
/*
* virtio-blk-pci: This extends VirtioPCIProxy.
*/
diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
new file mode 100644
index 0000000..5804cc9
--- /dev/null
+++ b/include/hw/virtio/vhost-user-blk.h
@@ -0,0 +1,41 @@
+/*
+ * vhost-user-blk host device
+ * Copyright(C) 2017 Intel Corporation.
+ *
+ * Authors:
+ * Changpeng Liu <changpeng.liu@intel.com>
+ *
+ * Based on vhost-scsi.h, Copyright IBM, Corp. 2011
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef VHOST_USER_BLK_H
+#define VHOST_USER_BLK_H
+
+#include "standard-headers/linux/virtio_blk.h"
+#include "qemu-common.h"
+#include "hw/qdev.h"
+#include "hw/block/block.h"
+#include "chardev/char-fe.h"
+#include "hw/virtio/vhost.h"
+
+#define TYPE_VHOST_USER_BLK "vhost-user-blk"
+#define VHOST_USER_BLK(obj) \
+ OBJECT_CHECK(VHostUserBlk, (obj), TYPE_VHOST_USER_BLK)
+
+typedef struct VHostUserBlk {
+ VirtIODevice parent_obj;
+ CharBackend chardev;
+ int32_t bootindex;
+ struct virtio_blk_config blkcfg;
+ uint16_t num_queues;
+ uint32_t queue_size;
+ uint32_t config_wce;
+ uint32_t config_ro;
+ struct vhost_dev dev;
+} VHostUserBlk;
+
+#endif
--
1.9.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 2/4] vhost-user-blk: introduce a new vhost-user-blk host device
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 2/4] vhost-user-blk: introduce a new vhost-user-blk host device Changpeng Liu
@ 2018-01-02 15:21 ` Marc-André Lureau
0 siblings, 0 replies; 13+ messages in thread
From: Marc-André Lureau @ 2018-01-02 15:21 UTC (permalink / raw)
To: Changpeng Liu
Cc: QEMU, james.r.harris, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> This commit introduces a new vhost-user device for block, it uses a
> chardev to connect with the backend, same with Qemu virito-blk device,
> Guest OS still uses the virtio-blk frontend driver.
>
> To use it, start QEMU with command line like this:
>
> qemu-system-x86_64 \
> -chardev socket,id=char0,path=/path/vhost.socket \
> -device vhost-user-blk-pci,chardev=char0,num-queues=2, \
> bootindex=2... \
>
> Users can use different parameters for `num-queues` and `bootindex`.
>
> Different with exist Qemu virtio-blk host device, it makes more easy
> for users to implement their own I/O processing logic, such as all
> user space I/O stack against hardware block device. It uses the new
> vhost messages(VHOST_USER_GET_CONFIG) to get block virtio config
> information from backend process.
>
> Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> default-configs/pci.mak | 1 +
> hw/block/Makefile.objs | 3 +
> hw/block/vhost-user-blk.c | 359 +++++++++++++++++++++++++++++++++++++
> hw/virtio/virtio-pci.c | 55 ++++++
> hw/virtio/virtio-pci.h | 18 ++
> include/hw/virtio/vhost-user-blk.h | 41 +++++
> 6 files changed, 477 insertions(+)
> create mode 100644 hw/block/vhost-user-blk.c
> create mode 100644 include/hw/virtio/vhost-user-blk.h
>
> diff --git a/default-configs/pci.mak b/default-configs/pci.mak
> index e514bde..49a0f28 100644
> --- a/default-configs/pci.mak
> +++ b/default-configs/pci.mak
> @@ -43,3 +43,4 @@ CONFIG_VGA_PCI=y
> CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM)
> CONFIG_ROCKER=y
> CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
> +CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX))
> diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
> index e0ed980..4c19a58 100644
> --- a/hw/block/Makefile.objs
> +++ b/hw/block/Makefile.objs
> @@ -13,3 +13,6 @@ obj-$(CONFIG_SH4) += tc58128.o
>
> obj-$(CONFIG_VIRTIO) += virtio-blk.o
> obj-$(CONFIG_VIRTIO) += dataplane/
> +ifeq ($(CONFIG_VIRTIO),y)
> +obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o
> +endif
> diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
> new file mode 100644
> index 0000000..b53b4c9
> --- /dev/null
> +++ b/hw/block/vhost-user-blk.c
> @@ -0,0 +1,359 @@
> +/*
> + * vhost-user-blk host device
> + *
> + * Copyright(C) 2017 Intel Corporation.
> + *
> + * Authors:
> + * Changpeng Liu <changpeng.liu@intel.com>
> + *
> + * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by:
> + * Felipe Franciosi <felipe@nutanix.com>
> + * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
> + * Nicholas Bellinger <nab@risingtidesystems.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "qemu/typedefs.h"
> +#include "qemu/cutils.h"
> +#include "qom/object.h"
> +#include "hw/qdev-core.h"
> +#include "hw/virtio/vhost.h"
> +#include "hw/virtio/vhost-user-blk.h"
> +#include "hw/virtio/virtio.h"
> +#include "hw/virtio/virtio-bus.h"
> +#include "hw/virtio/virtio-access.h"
> +
> +static const int user_feature_bits[] = {
> + VIRTIO_BLK_F_SIZE_MAX,
> + VIRTIO_BLK_F_SEG_MAX,
> + VIRTIO_BLK_F_GEOMETRY,
> + VIRTIO_BLK_F_BLK_SIZE,
> + VIRTIO_BLK_F_TOPOLOGY,
> + VIRTIO_BLK_F_MQ,
> + VIRTIO_BLK_F_RO,
> + VIRTIO_BLK_F_FLUSH,
> + VIRTIO_BLK_F_CONFIG_WCE,
> + VIRTIO_F_VERSION_1,
> + VIRTIO_RING_F_INDIRECT_DESC,
> + VIRTIO_RING_F_EVENT_IDX,
> + VIRTIO_F_NOTIFY_ON_EMPTY,
> + VHOST_INVALID_FEATURE_BIT
> +};
> +
> +static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> +
> + memcpy(config, &s->blkcfg, sizeof(struct virtio_blk_config));
> +}
> +
> +static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
> + int ret;
> +
> + if (blkcfg->wce == s->blkcfg.wce) {
> + return;
> + }
> +
> + ret = vhost_dev_set_config(&s->dev, &blkcfg->wce,
> + offsetof(struct virtio_blk_config, wce),
> + sizeof(blkcfg->wce),
> + VHOST_SET_CONFIG_TYPE_MASTER);
> + if (ret) {
> + error_report("set device config space failed");
> + return;
> + }
> +
> + s->blkcfg.wce = blkcfg->wce;
> +}
> +
> +static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
> +{
> + int ret;
> + struct virtio_blk_config blkcfg;
> + VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
> +
> + ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
> + sizeof(struct virtio_blk_config));
> + if (ret < 0) {
> + error_report("get config space failed");
> + return -1;
> + }
> +
> + /* valid for resize only */
> + if (blkcfg.capacity != s->blkcfg.capacity) {
> + s->blkcfg.capacity = blkcfg.capacity;
> + memcpy(dev->vdev->config, &s->blkcfg, sizeof(struct virtio_blk_config));
> + virtio_notify_config(dev->vdev);
> + }
> +
> + return 0;
> +}
> +
> +const VhostDevConfigOps blk_ops = {
> + .vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
> +};
> +
> +static void vhost_user_blk_start(VirtIODevice *vdev)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
> + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> + int i, ret;
> +
> + if (!k->set_guest_notifiers) {
> + error_report("binding does not support guest notifiers");
> + return;
> + }
> +
> + ret = vhost_dev_enable_notifiers(&s->dev, vdev);
> + if (ret < 0) {
> + error_report("Error enabling host notifiers: %d", -ret);
> + return;
> + }
> +
> + ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
> + if (ret < 0) {
> + error_report("Error binding guest notifier: %d", -ret);
> + goto err_host_notifiers;
> + }
> +
> + s->dev.acked_features = vdev->guest_features;
> + ret = vhost_dev_start(&s->dev, vdev);
> + if (ret < 0) {
> + error_report("Error starting vhost: %d", -ret);
> + goto err_guest_notifiers;
> + }
> +
> + /* guest_notifier_mask/pending not used yet, so just unmask
> + * everything here. virtio-pci will do the right thing by
> + * enabling/disabling irqfd.
> + */
> + for (i = 0; i < s->dev.nvqs; i++) {
> + vhost_virtqueue_mask(&s->dev, vdev, i, false);
> + }
> +
> + return;
> +
> +err_guest_notifiers:
> + k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
> +err_host_notifiers:
> + vhost_dev_disable_notifiers(&s->dev, vdev);
> +}
> +
> +static void vhost_user_blk_stop(VirtIODevice *vdev)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
> + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> + int ret;
> +
> + if (!k->set_guest_notifiers) {
> + return;
> + }
> +
> + vhost_dev_stop(&s->dev, vdev);
> +
> + ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
> + if (ret < 0) {
> + error_report("vhost guest notifier cleanup failed: %d", ret);
> + return;
> + }
> +
> + vhost_dev_disable_notifiers(&s->dev, vdev);
> +}
> +
> +static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
> +
> + if (!vdev->vm_running) {
> + should_start = false;
> + }
> +
> + if (s->dev.started == should_start) {
> + return;
> + }
> +
> + if (should_start) {
> + vhost_user_blk_start(vdev);
> + } else {
> + vhost_user_blk_stop(vdev);
> + }
> +
> +}
> +
> +static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
> + uint64_t features,
> + Error **errp)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + uint64_t get_features;
> +
> + /* Turn on pre-defined features */
> + virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX);
> + virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY);
> + virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY);
> + virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE);
> + virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH);
> +
> + if (s->config_wce) {
> + virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE);
> + }
> + if (s->config_ro) {
> + virtio_add_feature(&features, VIRTIO_BLK_F_RO);
> + }
> + if (s->num_queues > 1) {
> + virtio_add_feature(&features, VIRTIO_BLK_F_MQ);
> + }
> +
> + get_features = vhost_get_features(&s->dev, user_feature_bits, features);
> +
> + return get_features;
> +}
> +
> +static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +
> +}
> +
> +static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
> +{
> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> + VHostUserBlk *s = VHOST_USER_BLK(vdev);
> + int i, ret;
> +
> + if (!s->chardev.chr) {
> + error_setg(errp, "vhost-user-blk: chardev is mandatory");
> + return;
> + }
> +
> + if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) {
> + error_setg(errp, "vhost-user-blk: invalid number of IO queues");
> + return;
> + }
> +
> + if (!s->queue_size) {
> + error_setg(errp, "vhost-user-blk: queue size must be non-zero");
> + return;
> + }
> +
> + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
> + sizeof(struct virtio_blk_config));
> +
> + for (i = 0; i < s->num_queues; i++) {
> + virtio_add_queue(vdev, s->queue_size,
> + vhost_user_blk_handle_output);
> + }
> +
> + s->dev.nvqs = s->num_queues;
> + s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs);
> + s->dev.vq_index = 0;
> + s->dev.backend_features = 0;
> +
> + ret = vhost_dev_init(&s->dev, &s->chardev, VHOST_BACKEND_TYPE_USER, 0);
> + if (ret < 0) {
> + error_setg(errp, "vhost-user-blk: vhost initialization failed: %s",
> + strerror(-ret));
> + goto virtio_err;
> + }
> +
> + ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
> + sizeof(struct virtio_blk_config));
> + if (ret < 0) {
> + error_setg(errp, "vhost-user-blk: get block config failed");
> + goto vhost_err;
> + }
> +
> + if (s->blkcfg.num_queues != s->num_queues) {
> + s->blkcfg.num_queues = s->num_queues;
> + }
> +
> + vhost_dev_set_config_notifier(&s->dev, &blk_ops);
> +
> + return;
> +
> +vhost_err:
> + vhost_dev_cleanup(&s->dev);
> +virtio_err:
> + g_free(s->dev.vqs);
> + virtio_cleanup(vdev);
> +}
> +
> +static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
> +{
> + VirtIODevice *vdev = VIRTIO_DEVICE(dev);
> + VHostUserBlk *s = VHOST_USER_BLK(dev);
> +
> + vhost_user_blk_set_status(vdev, 0);
> + vhost_dev_cleanup(&s->dev);
> + g_free(s->dev.vqs);
> + virtio_cleanup(vdev);
> +}
> +
> +static void vhost_user_blk_instance_init(Object *obj)
> +{
> + VHostUserBlk *s = VHOST_USER_BLK(obj);
> +
> + device_add_bootindex_property(obj, &s->bootindex, "bootindex",
> + "/disk@0,0", DEVICE(obj), NULL);
> +}
> +
> +static const VMStateDescription vmstate_vhost_user_blk = {
> + .name = "vhost-user-blk",
> + .minimum_version_id = 1,
> + .version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_VIRTIO_DEVICE,
> + VMSTATE_END_OF_LIST()
> + },
> +};
> +
> +static Property vhost_user_blk_properties[] = {
> + DEFINE_PROP_CHR("chardev", VHostUserBlk, chardev),
> + DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1),
> + DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128),
> + DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true),
> + DEFINE_PROP_BIT("config-ro", VHostUserBlk, config_ro, 0, false),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
> +
> + dc->props = vhost_user_blk_properties;
> + dc->vmsd = &vmstate_vhost_user_blk;
> + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
> + vdc->realize = vhost_user_blk_device_realize;
> + vdc->unrealize = vhost_user_blk_device_unrealize;
> + vdc->get_config = vhost_user_blk_update_config;
> + vdc->set_config = vhost_user_blk_set_config;
> + vdc->get_features = vhost_user_blk_get_features;
> + vdc->set_status = vhost_user_blk_set_status;
> +}
> +
> +static const TypeInfo vhost_user_blk_info = {
> + .name = TYPE_VHOST_USER_BLK,
> + .parent = TYPE_VIRTIO_DEVICE,
> + .instance_size = sizeof(VHostUserBlk),
> + .instance_init = vhost_user_blk_instance_init,
> + .class_init = vhost_user_blk_class_init,
> +};
> +
> +static void virtio_register_types(void)
> +{
> + type_register_static(&vhost_user_blk_info);
> +}
> +
> +type_init(virtio_register_types)
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index e92837c..b5e05d9 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -1976,6 +1976,58 @@ static const TypeInfo virtio_blk_pci_info = {
> .class_init = virtio_blk_pci_class_init,
> };
>
> +#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
> +/* vhost-user-blk */
> +
> +static Property vhost_user_blk_pci_properties[] = {
> + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
> + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
> +{
> + VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev);
> + DeviceState *vdev = DEVICE(&dev->vdev);
> +
> + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
> + object_property_set_bool(OBJECT(vdev), true, "realized", errp);
> +}
> +
> +static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
> + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
> +
> + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
> + dc->props = vhost_user_blk_pci_properties;
> + k->realize = vhost_user_blk_pci_realize;
> + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
> + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
> + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
> + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
> +}
> +
> +static void vhost_user_blk_pci_instance_init(Object *obj)
> +{
> + VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj);
> +
> + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
> + TYPE_VHOST_USER_BLK);
> + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev),
> + "bootindex", &error_abort);
> +}
> +
> +static const TypeInfo vhost_user_blk_pci_info = {
> + .name = TYPE_VHOST_USER_BLK_PCI,
> + .parent = TYPE_VIRTIO_PCI,
> + .instance_size = sizeof(VHostUserBlkPCI),
> + .instance_init = vhost_user_blk_pci_instance_init,
> + .class_init = vhost_user_blk_pci_class_init,
> +};
> +#endif
> +
> /* virtio-scsi-pci */
>
> static Property virtio_scsi_pci_properties[] = {
> @@ -2622,6 +2674,9 @@ static void virtio_pci_register_types(void)
> type_register_static(&virtio_9p_pci_info);
> #endif
> type_register_static(&virtio_blk_pci_info);
> +#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
> + type_register_static(&vhost_user_blk_pci_info);
> +#endif
> type_register_static(&virtio_scsi_pci_info);
> type_register_static(&virtio_balloon_pci_info);
> type_register_static(&virtio_serial_pci_info);
> diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
> index 12d3a90..813082b 100644
> --- a/hw/virtio/virtio-pci.h
> +++ b/hw/virtio/virtio-pci.h
> @@ -27,6 +27,9 @@
> #include "hw/virtio/virtio-gpu.h"
> #include "hw/virtio/virtio-crypto.h"
> #include "hw/virtio/vhost-user-scsi.h"
> +#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
> +#include "hw/virtio/vhost-user-blk.h"
> +#endif
>
> #ifdef CONFIG_VIRTFS
> #include "hw/9pfs/virtio-9p.h"
> @@ -46,6 +49,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
> typedef struct VirtIONetPCI VirtIONetPCI;
> typedef struct VHostSCSIPCI VHostSCSIPCI;
> typedef struct VHostUserSCSIPCI VHostUserSCSIPCI;
> +typedef struct VHostUserBlkPCI VHostUserBlkPCI;
> typedef struct VirtIORngPCI VirtIORngPCI;
> typedef struct VirtIOInputPCI VirtIOInputPCI;
> typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
> @@ -244,6 +248,20 @@ struct VHostUserSCSIPCI {
> VHostUserSCSI vdev;
> };
>
> +#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
> +/*
> + * vhost-user-blk-pci: This extends VirtioPCIProxy.
> + */
> +#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci"
> +#define VHOST_USER_BLK_PCI(obj) \
> + OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI)
> +
> +struct VHostUserBlkPCI {
> + VirtIOPCIProxy parent_obj;
> + VHostUserBlk vdev;
> +};
> +#endif
> +
> /*
> * virtio-blk-pci: This extends VirtioPCIProxy.
> */
> diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h
> new file mode 100644
> index 0000000..5804cc9
> --- /dev/null
> +++ b/include/hw/virtio/vhost-user-blk.h
> @@ -0,0 +1,41 @@
> +/*
> + * vhost-user-blk host device
> + * Copyright(C) 2017 Intel Corporation.
> + *
> + * Authors:
> + * Changpeng Liu <changpeng.liu@intel.com>
> + *
> + * Based on vhost-scsi.h, Copyright IBM, Corp. 2011
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +#ifndef VHOST_USER_BLK_H
> +#define VHOST_USER_BLK_H
> +
> +#include "standard-headers/linux/virtio_blk.h"
> +#include "qemu-common.h"
> +#include "hw/qdev.h"
> +#include "hw/block/block.h"
> +#include "chardev/char-fe.h"
> +#include "hw/virtio/vhost.h"
> +
> +#define TYPE_VHOST_USER_BLK "vhost-user-blk"
> +#define VHOST_USER_BLK(obj) \
> + OBJECT_CHECK(VHostUserBlk, (obj), TYPE_VHOST_USER_BLK)
> +
> +typedef struct VHostUserBlk {
> + VirtIODevice parent_obj;
> + CharBackend chardev;
> + int32_t bootindex;
> + struct virtio_blk_config blkcfg;
> + uint16_t num_queues;
> + uint32_t queue_size;
> + uint32_t config_wce;
> + uint32_t config_ro;
> + struct vhost_dev dev;
> +} VHostUserBlk;
> +
> +#endif
> --
> 1.9.3
>
>
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages
2018-01-02 3:55 [Qemu-devel] [PATCH v8 0/4] Introduce a new vhost-user-blk host device to QEMU Changpeng Liu
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 1/4] vhost-user: add new vhost user messages to support virtio config space Changpeng Liu
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 2/4] vhost-user-blk: introduce a new vhost-user-blk host device Changpeng Liu
@ 2018-01-02 3:55 ` Changpeng Liu
2018-01-02 15:29 ` Marc-André Lureau
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application Changpeng Liu
3 siblings, 1 reply; 13+ messages in thread
From: Changpeng Liu @ 2018-01-02 3:55 UTC (permalink / raw)
To: changpeng.liu, qemu-devel
Cc: stefanha, pbonzini, mst, marcandre.lureau, felipe, james.r.harris
Enable VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages in
libvhost-user library, users can implement their own I/O target
based on the library. This enable the virtio config space delivered
between QEMU host device and the I/O target.
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
---
contrib/libvhost-user/libvhost-user.c | 54 +++++++++++++++++++++++++++++++++++
contrib/libvhost-user/libvhost-user.h | 34 +++++++++++++++++++++-
2 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
index f409bd3..9e12eb1 100644
--- a/contrib/libvhost-user/libvhost-user.c
+++ b/contrib/libvhost-user/libvhost-user.c
@@ -84,6 +84,8 @@ vu_request_to_string(unsigned int req)
REQ(VHOST_USER_SET_SLAVE_REQ_FD),
REQ(VHOST_USER_IOTLB_MSG),
REQ(VHOST_USER_SET_VRING_ENDIAN),
+ REQ(VHOST_USER_GET_CONFIG),
+ REQ(VHOST_USER_SET_CONFIG),
REQ(VHOST_USER_MAX),
};
#undef REQ
@@ -798,6 +800,53 @@ vu_set_slave_req_fd(VuDev *dev, VhostUserMsg *vmsg)
}
static bool
+vu_get_config(VuDev *dev, VhostUserMsg *vmsg)
+{
+ int ret = -1;
+
+ if (dev->iface->get_config) {
+ ret = dev->iface->get_config(dev, vmsg->payload.config.region,
+ vmsg->payload.config.size);
+ }
+
+ if (ret) {
+ /* resize to zero to indicate an error to master */
+ vmsg->size = 0;
+ }
+
+ return true;
+}
+
+static bool
+vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
+{
+ int ret = -1;
+ bool reply_supported = !!(dev->protocol_features &
+ (1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK));
+
+ if (dev->iface->set_config) {
+ ret = dev->iface->set_config(dev, vmsg->payload.config.region,
+ vmsg->payload.config.offset,
+ vmsg->payload.config.size,
+ vmsg->payload.config.flags);
+ }
+
+ vmsg->size = sizeof(vmsg->payload.u64);
+ if (!ret) {
+ vmsg->payload.u64 = 0;
+ } else {
+ /* indicate an error in case reply supported */
+ vmsg->payload.u64 = 1;
+ }
+
+ if (reply_supported) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
{
int do_reply = 0;
@@ -862,6 +911,10 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
return vu_set_vring_enable_exec(dev, vmsg);
case VHOST_USER_SET_SLAVE_REQ_FD:
return vu_set_slave_req_fd(dev, vmsg);
+ case VHOST_USER_GET_CONFIG:
+ return vu_get_config(dev, vmsg);
+ case VHOST_USER_SET_CONFIG:
+ return vu_set_config(dev, vmsg);
case VHOST_USER_NONE:
break;
default:
@@ -970,6 +1023,7 @@ vu_init(VuDev *dev,
dev->iface = iface;
dev->log_call_fd = -1;
dev->slave_fd = -1;
+
for (i = 0; i < VHOST_MAX_NR_VIRTQUEUE; i++) {
dev->vq[i] = (VuVirtq) {
.call_fd = -1, .kick_fd = -1, .err_fd = -1,
diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
index 2f5864b..b38959e 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -30,6 +30,16 @@
#define VHOST_MEMORY_MAX_NREGIONS 8
+typedef enum VhostSetConfigType {
+ VHOST_SET_CONFIG_TYPE_MASTER = 0,
+ VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
+} VhostSetConfigType;
+
+/*
+ * Maximum size of virtio device config space
+ */
+#define VHOST_USER_MAX_CONFIG_SIZE 256
+
enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_MQ = 0,
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
@@ -38,7 +48,6 @@ enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_NET_MTU = 4,
VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
-
VHOST_USER_PROTOCOL_F_MAX
};
@@ -69,6 +78,8 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_SLAVE_REQ_FD = 21,
VHOST_USER_IOTLB_MSG = 22,
VHOST_USER_SET_VRING_ENDIAN = 23,
+ VHOST_USER_GET_CONFIG = 24,
+ VHOST_USER_SET_CONFIG = 25,
VHOST_USER_MAX
} VhostUserRequest;
@@ -90,6 +101,18 @@ typedef struct VhostUserLog {
uint64_t mmap_offset;
} VhostUserLog;
+typedef struct VhostUserConfig {
+ uint32_t offset;
+ uint32_t size;
+ uint32_t flags;
+ uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
+} VhostUserConfig;
+
+static VhostUserConfig c __attribute__ ((unused));
+#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
+ + sizeof(c.size) \
+ + sizeof(c.flags))
+
#if defined(_WIN32)
# define VU_PACKED __attribute__((gcc_struct, packed))
#else
@@ -112,6 +135,7 @@ typedef struct VhostUserMsg {
struct vhost_vring_addr addr;
VhostUserMemory memory;
VhostUserLog log;
+ VhostUserConfig config;
} payload;
int fds[VHOST_MEMORY_MAX_NREGIONS];
@@ -140,6 +164,10 @@ typedef int (*vu_process_msg_cb) (VuDev *dev, VhostUserMsg *vmsg,
int *do_reply);
typedef void (*vu_queue_set_started_cb) (VuDev *dev, int qidx, bool started);
typedef bool (*vu_queue_is_processed_in_order_cb) (VuDev *dev, int qidx);
+typedef int (*vu_get_config_cb) (VuDev *dev, uint8_t *config, uint32_t len);
+typedef int (*vu_set_config_cb) (VuDev *dev, const uint8_t *data,
+ uint32_t offset, uint32_t size,
+ uint32_t flags);
typedef struct VuDevIface {
/* called by VHOST_USER_GET_FEATURES to get the features bitmask */
@@ -162,6 +190,10 @@ typedef struct VuDevIface {
* on unmanaged exit/crash.
*/
vu_queue_is_processed_in_order_cb queue_is_processed_in_order;
+ /* get the config space of the device */
+ vu_get_config_cb get_config;
+ /* set the config space of the device */
+ vu_set_config_cb set_config;
} VuDevIface;
typedef void (*vu_queue_handler_cb) (VuDev *dev, int qidx);
--
1.9.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages Changpeng Liu
@ 2018-01-02 15:29 ` Marc-André Lureau
2018-01-03 1:37 ` Liu, Changpeng
0 siblings, 1 reply; 13+ messages in thread
From: Marc-André Lureau @ 2018-01-02 15:29 UTC (permalink / raw)
To: Changpeng Liu
Cc: QEMU, james.r.harris, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
Hi
On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> Enable VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages in
> libvhost-user library, users can implement their own I/O target
> based on the library. This enable the virtio config space delivered
> between QEMU host device and the I/O target.
>
> Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> ---
> contrib/libvhost-user/libvhost-user.c | 54 +++++++++++++++++++++++++++++++++++
> contrib/libvhost-user/libvhost-user.h | 34 +++++++++++++++++++++-
> 2 files changed, 87 insertions(+), 1 deletion(-)
>
> diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c
> index f409bd3..9e12eb1 100644
> --- a/contrib/libvhost-user/libvhost-user.c
> +++ b/contrib/libvhost-user/libvhost-user.c
> @@ -84,6 +84,8 @@ vu_request_to_string(unsigned int req)
> REQ(VHOST_USER_SET_SLAVE_REQ_FD),
> REQ(VHOST_USER_IOTLB_MSG),
> REQ(VHOST_USER_SET_VRING_ENDIAN),
> + REQ(VHOST_USER_GET_CONFIG),
> + REQ(VHOST_USER_SET_CONFIG),
> REQ(VHOST_USER_MAX),
> };
> #undef REQ
> @@ -798,6 +800,53 @@ vu_set_slave_req_fd(VuDev *dev, VhostUserMsg *vmsg)
> }
>
> static bool
> +vu_get_config(VuDev *dev, VhostUserMsg *vmsg)
> +{
> + int ret = -1;
> +
> + if (dev->iface->get_config) {
> + ret = dev->iface->get_config(dev, vmsg->payload.config.region,
> + vmsg->payload.config.size);
> + }
> +
> + if (ret) {
> + /* resize to zero to indicate an error to master */
> + vmsg->size = 0;
> + }
> +
> + return true;
> +}
> +
> +static bool
> +vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
> +{
> + int ret = -1;
> + bool reply_supported = !!(dev->protocol_features &
> + (1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK));
> +
None of the other messages in libvhost-user support the REPLY_ACK
flag. Do you need it? If not, please drop it.
If you need it, it's a better idea to make the reply explicit and
mandatory in the protocol for SET_CONFIG.
> + if (dev->iface->set_config) {
> + ret = dev->iface->set_config(dev, vmsg->payload.config.region,
> + vmsg->payload.config.offset,
> + vmsg->payload.config.size,
> + vmsg->payload.config.flags);
> + }
> +
> + vmsg->size = sizeof(vmsg->payload.u64);
> + if (!ret) {
> + vmsg->payload.u64 = 0;
> + } else {
> + /* indicate an error in case reply supported */
> + vmsg->payload.u64 = 1;
> + }
> +
> + if (reply_supported) {
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool
> vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
> {
> int do_reply = 0;
> @@ -862,6 +911,10 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
> return vu_set_vring_enable_exec(dev, vmsg);
> case VHOST_USER_SET_SLAVE_REQ_FD:
> return vu_set_slave_req_fd(dev, vmsg);
> + case VHOST_USER_GET_CONFIG:
> + return vu_get_config(dev, vmsg);
> + case VHOST_USER_SET_CONFIG:
> + return vu_set_config(dev, vmsg);
> case VHOST_USER_NONE:
> break;
> default:
> @@ -970,6 +1023,7 @@ vu_init(VuDev *dev,
> dev->iface = iface;
> dev->log_call_fd = -1;
> dev->slave_fd = -1;
> +
unrelated coding style change, please drop
> for (i = 0; i < VHOST_MAX_NR_VIRTQUEUE; i++) {
> dev->vq[i] = (VuVirtq) {
> .call_fd = -1, .kick_fd = -1, .err_fd = -1,
> diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h
> index 2f5864b..b38959e 100644
> --- a/contrib/libvhost-user/libvhost-user.h
> +++ b/contrib/libvhost-user/libvhost-user.h
> @@ -30,6 +30,16 @@
>
> #define VHOST_MEMORY_MAX_NREGIONS 8
>
> +typedef enum VhostSetConfigType {
> + VHOST_SET_CONFIG_TYPE_MASTER = 0,
> + VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
> +} VhostSetConfigType;
> +
> +/*
> + * Maximum size of virtio device config space
> + */
> +#define VHOST_USER_MAX_CONFIG_SIZE 256
> +
> enum VhostUserProtocolFeature {
> VHOST_USER_PROTOCOL_F_MQ = 0,
> VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
> @@ -38,7 +48,6 @@ enum VhostUserProtocolFeature {
> VHOST_USER_PROTOCOL_F_NET_MTU = 4,
> VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
> VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
> -
unrelated coding style change, please drop
> VHOST_USER_PROTOCOL_F_MAX
> };
>
> @@ -69,6 +78,8 @@ typedef enum VhostUserRequest {
> VHOST_USER_SET_SLAVE_REQ_FD = 21,
> VHOST_USER_IOTLB_MSG = 22,
> VHOST_USER_SET_VRING_ENDIAN = 23,
> + VHOST_USER_GET_CONFIG = 24,
> + VHOST_USER_SET_CONFIG = 25,
> VHOST_USER_MAX
> } VhostUserRequest;
>
> @@ -90,6 +101,18 @@ typedef struct VhostUserLog {
> uint64_t mmap_offset;
> } VhostUserLog;
>
> +typedef struct VhostUserConfig {
> + uint32_t offset;
> + uint32_t size;
> + uint32_t flags;
> + uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
> +} VhostUserConfig;
> +
> +static VhostUserConfig c __attribute__ ((unused));
> +#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
> + + sizeof(c.size) \
> + + sizeof(c.flags))
> +
> #if defined(_WIN32)
> # define VU_PACKED __attribute__((gcc_struct, packed))
> #else
> @@ -112,6 +135,7 @@ typedef struct VhostUserMsg {
> struct vhost_vring_addr addr;
> VhostUserMemory memory;
> VhostUserLog log;
> + VhostUserConfig config;
> } payload;
>
> int fds[VHOST_MEMORY_MAX_NREGIONS];
> @@ -140,6 +164,10 @@ typedef int (*vu_process_msg_cb) (VuDev *dev, VhostUserMsg *vmsg,
> int *do_reply);
> typedef void (*vu_queue_set_started_cb) (VuDev *dev, int qidx, bool started);
> typedef bool (*vu_queue_is_processed_in_order_cb) (VuDev *dev, int qidx);
> +typedef int (*vu_get_config_cb) (VuDev *dev, uint8_t *config, uint32_t len);
> +typedef int (*vu_set_config_cb) (VuDev *dev, const uint8_t *data,
> + uint32_t offset, uint32_t size,
> + uint32_t flags);
>
> typedef struct VuDevIface {
> /* called by VHOST_USER_GET_FEATURES to get the features bitmask */
> @@ -162,6 +190,10 @@ typedef struct VuDevIface {
> * on unmanaged exit/crash.
> */
> vu_queue_is_processed_in_order_cb queue_is_processed_in_order;
> + /* get the config space of the device */
> + vu_get_config_cb get_config;
> + /* set the config space of the device */
> + vu_set_config_cb set_config;
> } VuDevIface;
>
> typedef void (*vu_queue_handler_cb) (VuDev *dev, int qidx);
> --
> 1.9.3
>
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages
2018-01-02 15:29 ` Marc-André Lureau
@ 2018-01-03 1:37 ` Liu, Changpeng
0 siblings, 0 replies; 13+ messages in thread
From: Liu, Changpeng @ 2018-01-03 1:37 UTC (permalink / raw)
To: Marc-André Lureau
Cc: QEMU, Harris, James R, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
> -----Original Message-----
> From: Marc-André Lureau [mailto:marcandre.lureau@gmail.com]
> Sent: Tuesday, January 2, 2018 11:30 PM
> To: Liu, Changpeng <changpeng.liu@intel.com>
> Cc: QEMU <qemu-devel@nongnu.org>; Harris, James R <james.r.harris@intel.com>;
> Michael S. Tsirkin <mst@redhat.com>; Stefan Hajnoczi <stefanha@gmail.com>;
> Paolo Bonzini <pbonzini@redhat.com>; Felipe Franciosi <felipe@nutanix.com>
> Subject: Re: [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio
> config space messages
>
> Hi
>
> On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> > Enable VHOST_USER_GET_CONFIG/VHOST_USER_SET_CONFIG messages in
> > libvhost-user library, users can implement their own I/O target
> > based on the library. This enable the virtio config space delivered
> > between QEMU host device and the I/O target.
> >
> > Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> > ---
> > contrib/libvhost-user/libvhost-user.c | 54
> +++++++++++++++++++++++++++++++++++
> > contrib/libvhost-user/libvhost-user.h | 34 +++++++++++++++++++++-
> > 2 files changed, 87 insertions(+), 1 deletion(-)
> >
> > diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-
> user.c
> > index f409bd3..9e12eb1 100644
> > --- a/contrib/libvhost-user/libvhost-user.c
> > +++ b/contrib/libvhost-user/libvhost-user.c
> > @@ -84,6 +84,8 @@ vu_request_to_string(unsigned int req)
> > REQ(VHOST_USER_SET_SLAVE_REQ_FD),
> > REQ(VHOST_USER_IOTLB_MSG),
> > REQ(VHOST_USER_SET_VRING_ENDIAN),
> > + REQ(VHOST_USER_GET_CONFIG),
> > + REQ(VHOST_USER_SET_CONFIG),
> > REQ(VHOST_USER_MAX),
> > };
> > #undef REQ
> > @@ -798,6 +800,53 @@ vu_set_slave_req_fd(VuDev *dev, VhostUserMsg *vmsg)
> > }
> >
> > static bool
> > +vu_get_config(VuDev *dev, VhostUserMsg *vmsg)
> > +{
> > + int ret = -1;
> > +
> > + if (dev->iface->get_config) {
> > + ret = dev->iface->get_config(dev, vmsg->payload.config.region,
> > + vmsg->payload.config.size);
> > + }
> > +
> > + if (ret) {
> > + /* resize to zero to indicate an error to master */
> > + vmsg->size = 0;
> > + }
> > +
> > + return true;
> > +}
> > +
> > +static bool
> > +vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
> > +{
> > + int ret = -1;
> > + bool reply_supported = !!(dev->protocol_features &
> > + (1ULL << VHOST_USER_PROTOCOL_F_REPLY_ACK));
> > +
>
> None of the other messages in libvhost-user support the REPLY_ACK
> flag. Do you need it? If not, please drop it.
Ok, will drop it.
>
> If you need it, it's a better idea to make the reply explicit and
> mandatory in the protocol for SET_CONFIG.
>
> > + if (dev->iface->set_config) {
> > + ret = dev->iface->set_config(dev, vmsg->payload.config.region,
> > + vmsg->payload.config.offset,
> > + vmsg->payload.config.size,
> > + vmsg->payload.config.flags);
> > + }
> > +
> > + vmsg->size = sizeof(vmsg->payload.u64);
> > + if (!ret) {
> > + vmsg->payload.u64 = 0;
> > + } else {
> > + /* indicate an error in case reply supported */
> > + vmsg->payload.u64 = 1;
> > + }
> > +
> > + if (reply_supported) {
> > + return true;
> > + }
> > +
> > + return false;
> > +}
> > +
> > +static bool
> > vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
> > {
> > int do_reply = 0;
> > @@ -862,6 +911,10 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
> > return vu_set_vring_enable_exec(dev, vmsg);
> > case VHOST_USER_SET_SLAVE_REQ_FD:
> > return vu_set_slave_req_fd(dev, vmsg);
> > + case VHOST_USER_GET_CONFIG:
> > + return vu_get_config(dev, vmsg);
> > + case VHOST_USER_SET_CONFIG:
> > + return vu_set_config(dev, vmsg);
> > case VHOST_USER_NONE:
> > break;
> > default:
> > @@ -970,6 +1023,7 @@ vu_init(VuDev *dev,
> > dev->iface = iface;
> > dev->log_call_fd = -1;
> > dev->slave_fd = -1;
> > +
>
> unrelated coding style change, please drop
Thanks.
>
> > for (i = 0; i < VHOST_MAX_NR_VIRTQUEUE; i++) {
> > dev->vq[i] = (VuVirtq) {
> > .call_fd = -1, .kick_fd = -1, .err_fd = -1,
> > diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-
> user.h
> > index 2f5864b..b38959e 100644
> > --- a/contrib/libvhost-user/libvhost-user.h
> > +++ b/contrib/libvhost-user/libvhost-user.h
> > @@ -30,6 +30,16 @@
> >
> > #define VHOST_MEMORY_MAX_NREGIONS 8
> >
> > +typedef enum VhostSetConfigType {
> > + VHOST_SET_CONFIG_TYPE_MASTER = 0,
> > + VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
> > +} VhostSetConfigType;
> > +
> > +/*
> > + * Maximum size of virtio device config space
> > + */
> > +#define VHOST_USER_MAX_CONFIG_SIZE 256
> > +
> > enum VhostUserProtocolFeature {
> > VHOST_USER_PROTOCOL_F_MQ = 0,
> > VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
> > @@ -38,7 +48,6 @@ enum VhostUserProtocolFeature {
> > VHOST_USER_PROTOCOL_F_NET_MTU = 4,
> > VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
> > VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
> > -
>
>
> unrelated coding style change, please drop
Thanks.
>
> > VHOST_USER_PROTOCOL_F_MAX
> > };
> >
> > @@ -69,6 +78,8 @@ typedef enum VhostUserRequest {
> > VHOST_USER_SET_SLAVE_REQ_FD = 21,
> > VHOST_USER_IOTLB_MSG = 22,
> > VHOST_USER_SET_VRING_ENDIAN = 23,
> > + VHOST_USER_GET_CONFIG = 24,
> > + VHOST_USER_SET_CONFIG = 25,
> > VHOST_USER_MAX
> > } VhostUserRequest;
> >
> > @@ -90,6 +101,18 @@ typedef struct VhostUserLog {
> > uint64_t mmap_offset;
> > } VhostUserLog;
> >
> > +typedef struct VhostUserConfig {
> > + uint32_t offset;
> > + uint32_t size;
> > + uint32_t flags;
> > + uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
> > +} VhostUserConfig;
> > +
> > +static VhostUserConfig c __attribute__ ((unused));
> > +#define VHOST_USER_CONFIG_HDR_SIZE (sizeof(c.offset) \
> > + + sizeof(c.size) \
> > + + sizeof(c.flags))
> > +
> > #if defined(_WIN32)
> > # define VU_PACKED __attribute__((gcc_struct, packed))
> > #else
> > @@ -112,6 +135,7 @@ typedef struct VhostUserMsg {
> > struct vhost_vring_addr addr;
> > VhostUserMemory memory;
> > VhostUserLog log;
> > + VhostUserConfig config;
> > } payload;
> >
> > int fds[VHOST_MEMORY_MAX_NREGIONS];
> > @@ -140,6 +164,10 @@ typedef int (*vu_process_msg_cb) (VuDev *dev,
> VhostUserMsg *vmsg,
> > int *do_reply);
> > typedef void (*vu_queue_set_started_cb) (VuDev *dev, int qidx, bool started);
> > typedef bool (*vu_queue_is_processed_in_order_cb) (VuDev *dev, int qidx);
> > +typedef int (*vu_get_config_cb) (VuDev *dev, uint8_t *config, uint32_t len);
> > +typedef int (*vu_set_config_cb) (VuDev *dev, const uint8_t *data,
> > + uint32_t offset, uint32_t size,
> > + uint32_t flags);
> >
> > typedef struct VuDevIface {
> > /* called by VHOST_USER_GET_FEATURES to get the features bitmask */
> > @@ -162,6 +190,10 @@ typedef struct VuDevIface {
> > * on unmanaged exit/crash.
> > */
> > vu_queue_is_processed_in_order_cb queue_is_processed_in_order;
> > + /* get the config space of the device */
> > + vu_get_config_cb get_config;
> > + /* set the config space of the device */
> > + vu_set_config_cb set_config;
> > } VuDevIface;
> >
> > typedef void (*vu_queue_handler_cb) (VuDev *dev, int qidx);
> > --
> > 1.9.3
> >
>
>
> --
> Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application
2018-01-02 3:55 [Qemu-devel] [PATCH v8 0/4] Introduce a new vhost-user-blk host device to QEMU Changpeng Liu
` (2 preceding siblings ...)
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 3/4] contrib/libvhost-user: enable virtio config space messages Changpeng Liu
@ 2018-01-02 3:55 ` Changpeng Liu
2018-01-02 15:43 ` Marc-André Lureau
3 siblings, 1 reply; 13+ messages in thread
From: Changpeng Liu @ 2018-01-02 3:55 UTC (permalink / raw)
To: changpeng.liu, qemu-devel
Cc: stefanha, pbonzini, mst, marcandre.lureau, felipe, james.r.harris
This commit introcudes a vhost-user-blk backend device, it uses UNIX
domain socket to communicate with QEMU. The vhost-user-blk sample
application should be used with QEMU vhost-user-blk-pci device.
To use it, complie with:
make vhost-user-blk
and start like this:
vhost-user-blk -b /dev/sdb -s /path/vhost.socket
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
---
.gitignore | 1 +
Makefile | 3 +
Makefile.objs | 1 +
contrib/vhost-user-blk/Makefile.objs | 1 +
contrib/vhost-user-blk/vhost-user-blk.c | 543 ++++++++++++++++++++++++++++++++
5 files changed, 549 insertions(+)
create mode 100644 contrib/vhost-user-blk/Makefile.objs
create mode 100644 contrib/vhost-user-blk/vhost-user-blk.c
diff --git a/.gitignore b/.gitignore
index 433f64f..704b222 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@
/module_block.h
/scsi/qemu-pr-helper
/vhost-user-scsi
+/vhost-user-blk
/fsdev/virtfs-proxy-helper
*.tmp
*.[1-9]
diff --git a/Makefile b/Makefile
index d86ecd2..f021fc8 100644
--- a/Makefile
+++ b/Makefile
@@ -331,6 +331,7 @@ dummy := $(call unnest-vars,, \
ivshmem-server-obj-y \
libvhost-user-obj-y \
vhost-user-scsi-obj-y \
+ vhost-user-blk-obj-y \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
@@ -562,6 +563,8 @@ ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
endif
vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
$(call LINK, $^)
+vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
+ $(call LINK, $^)
module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
$(call quiet-command,$(PYTHON) $< $@ \
diff --git a/Makefile.objs b/Makefile.objs
index 285c6f3..ae9aef7 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -115,6 +115,7 @@ libvhost-user-obj-y = contrib/libvhost-user/
vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
+vhost-user-blk-obj-y = contrib/vhost-user-blk/
######################################################################
trace-events-subdirs =
diff --git a/contrib/vhost-user-blk/Makefile.objs b/contrib/vhost-user-blk/Makefile.objs
new file mode 100644
index 0000000..72e2cdc
--- /dev/null
+++ b/contrib/vhost-user-blk/Makefile.objs
@@ -0,0 +1 @@
+vhost-user-blk-obj-y = vhost-user-blk.o
diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c
new file mode 100644
index 0000000..4649435
--- /dev/null
+++ b/contrib/vhost-user-blk/vhost-user-blk.c
@@ -0,0 +1,543 @@
+/*
+ * vhost-user-blk sample application
+ *
+ * Copyright (c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Author:
+ * Changpeng Liu <changpeng.liu@intel.com>
+ *
+ * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
+ * implemention by:
+ * Felipe Franciosi <felipe@nutanix.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 only.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/virtio_blk.h"
+#include "contrib/libvhost-user/libvhost-user-glib.h"
+#include "contrib/libvhost-user/libvhost-user.h"
+
+#include <glib.h>
+
+struct virtio_blk_inhdr {
+ unsigned char status;
+};
+
+/* vhost user block device */
+typedef struct VubDev {
+ VugDev parent;
+ int blk_fd;
+ struct virtio_blk_config blkcfg;
+ char *blk_name;
+ GMainLoop *loop;
+} VubDev;
+
+typedef struct VubReq {
+ VuVirtqElement *elem;
+ int64_t sector_num;
+ size_t size;
+ struct virtio_blk_inhdr *in;
+ struct virtio_blk_outhdr *out;
+ VubDev *vdev_blk;
+ struct VuVirtq *vq;
+} VubReq;
+
+/** refer util/iov.c **/
+static size_t vub_iov_size(const struct iovec *iov,
+ const unsigned int iov_cnt)
+{
+ size_t len;
+ unsigned int i;
+
+ len = 0;
+ for (i = 0; i < iov_cnt; i++) {
+ len += iov[i].iov_len;
+ }
+ return len;
+}
+
+static void vub_panic_cb(VuDev *vu_dev, const char *buf)
+{
+ VugDev *gdev;
+ VubDev *vdev_blk;
+
+ assert(vu_dev);
+
+ gdev = container_of(vu_dev, VugDev, parent);
+ vdev_blk = container_of(gdev, VubDev, parent);
+ if (buf) {
+ g_warning("vu_panic: %s", buf);
+ }
+
+ g_main_loop_quit(vdev_blk->loop);
+}
+
+static void vub_req_complete(VubReq *req)
+{
+ VugDev *gdev = &req->vdev_blk->parent;
+ VuDev *vu_dev = &gdev->parent;
+
+ /* IO size with 1 extra status byte */
+ vu_queue_push(vu_dev, req->vq, req->elem,
+ req->size + 1);
+ vu_queue_notify(vu_dev, req->vq);
+
+ if (req->elem) {
+ free(req->elem);
+ }
+ g_free(req);
+}
+
+static int vub_open(const char *file_name, bool wce)
+{
+ int fd;
+ int flags = O_RDWR;
+
+ if (!wce) {
+ flags |= O_DIRECT;
+ }
+
+ fd = open(file_name, flags);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open file %s, %s\n", file_name,
+ strerror(errno));
+ return -1;
+ }
+
+ return fd;
+}
+
+static ssize_t
+vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt)
+{
+ VubDev *vdev_blk = req->vdev_blk;
+ ssize_t rc;
+
+ if (!iovcnt) {
+ fprintf(stderr, "Invalid Read IOV count\n");
+ return -1;
+ }
+
+ req->size = vub_iov_size(iov, iovcnt);
+ rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
+ if (rc < 0) {
+ fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
+ vdev_blk->blk_name, req->sector_num, req->size,
+ strerror(errno));
+ return -1;
+ }
+
+ return rc;
+}
+
+static ssize_t
+vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt)
+{
+ VubDev *vdev_blk = req->vdev_blk;
+ ssize_t rc;
+
+ if (!iovcnt) {
+ fprintf(stderr, "Invalid Write IOV count\n");
+ return -1;
+ }
+
+ req->size = vub_iov_size(iov, iovcnt);
+ rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
+ if (rc < 0) {
+ fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
+ vdev_blk->blk_name, req->sector_num, req->size,
+ strerror(errno));
+ return -1;
+ }
+
+ return rc;
+}
+
+static void
+vub_flush(VubReq *req)
+{
+ VubDev *vdev_blk = req->vdev_blk;
+
+ fdatasync(vdev_blk->blk_fd);
+}
+
+static int vub_virtio_process_req(VubDev *vdev_blk,
+ VuVirtq *vq)
+{
+ VugDev *gdev = &vdev_blk->parent;
+ VuDev *vu_dev = &gdev->parent;
+ VuVirtqElement *elem;
+ uint32_t type;
+ unsigned in_num;
+ unsigned out_num;
+ VubReq *req;
+
+ elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
+ if (!elem) {
+ return -1;
+ }
+
+ /* refer to hw/block/virtio_blk.c */
+ if (elem->out_num < 1 || elem->in_num < 1) {
+ fprintf(stderr, "virtio-blk request missing headers\n");
+ free(elem);
+ return -1;
+ }
+
+ req = g_new0(VubReq, 1);
+ req->vdev_blk = vdev_blk;
+ req->vq = vq;
+ req->elem = elem;
+
+ in_num = elem->in_num;
+ out_num = elem->out_num;
+
+ /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
+ if (elem->out_sg[0].iov_len < sizeof(struct virtio_blk_outhdr)) {
+ fprintf(stderr, "Invalid outhdr size\n");
+ goto err;
+ }
+ req->out = (struct virtio_blk_outhdr *)elem->out_sg[0].iov_base;
+ out_num--;
+
+ if (elem->in_sg[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
+ fprintf(stderr, "Invalid inhdr size\n");
+ goto err;
+ }
+ req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base;
+ in_num--;
+
+ type = le32toh(req->out->type);
+ switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
+ case VIRTIO_BLK_T_IN: {
+ ssize_t ret = 0;
+ bool is_write = type & VIRTIO_BLK_T_OUT;
+ req->sector_num = le64toh(req->out->sector);
+ if (is_write) {
+ ret = vub_writev(req, &elem->out_sg[1], out_num);
+ } else {
+ ret = vub_readv(req, &elem->in_sg[0], in_num);
+ }
+ if (ret >= 0) {
+ req->in->status = VIRTIO_BLK_S_OK;
+ } else {
+ req->in->status = VIRTIO_BLK_S_IOERR;
+ }
+ vub_req_complete(req);
+ break;
+ }
+ case VIRTIO_BLK_T_FLUSH: {
+ vub_flush(req);
+ req->in->status = VIRTIO_BLK_S_OK;
+ vub_req_complete(req);
+ break;
+ }
+ case VIRTIO_BLK_T_GET_ID: {
+ size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num),
+ VIRTIO_BLK_ID_BYTES);
+ snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
+ req->in->status = VIRTIO_BLK_S_OK;
+ req->size = elem->in_sg[0].iov_len;
+ vub_req_complete(req);
+ break;
+ }
+ default: {
+ req->in->status = VIRTIO_BLK_S_UNSUPP;
+ vub_req_complete(req);
+ break;
+ }
+ }
+
+ return 0;
+
+err:
+ free(elem);
+ g_free(req);
+ return -1;
+}
+
+static void vub_process_vq(VuDev *vu_dev, int idx)
+{
+ VugDev *gdev;
+ VubDev *vdev_blk;
+ VuVirtq *vq;
+ int ret;
+
+ if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
+ fprintf(stderr, "VQ Index out of range: %d\n", idx);
+ vub_panic_cb(vu_dev, NULL);
+ return;
+ }
+
+ gdev = container_of(vu_dev, VugDev, parent);
+ vdev_blk = container_of(gdev, VubDev, parent);
+ assert(vdev_blk);
+
+ vq = vu_get_queue(vu_dev, idx);
+ assert(vq);
+
+ while (1) {
+ ret = vub_virtio_process_req(vdev_blk, vq);
+ if (ret) {
+ break;
+ }
+ }
+}
+
+static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
+{
+ VuVirtq *vq;
+
+ assert(vu_dev);
+
+ vq = vu_get_queue(vu_dev, idx);
+ vu_set_queue_handler(vu_dev, vq, started ? vub_process_vq : NULL);
+}
+
+static uint64_t
+vub_get_features(VuDev *dev)
+{
+ return 1ull << VIRTIO_BLK_F_SIZE_MAX |
+ 1ull << VIRTIO_BLK_F_SEG_MAX |
+ 1ull << VIRTIO_BLK_F_TOPOLOGY |
+ 1ull << VIRTIO_BLK_F_BLK_SIZE |
+ 1ull << VIRTIO_BLK_F_FLUSH |
+ 1ull << VIRTIO_BLK_F_CONFIG_WCE |
+ 1ull << VIRTIO_F_VERSION_1 |
+ 1ull << VHOST_USER_F_PROTOCOL_FEATURES;
+}
+
+static int
+vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
+{
+ VugDev *gdev;
+ VubDev *vdev_blk;
+
+ gdev = container_of(vu_dev, VugDev, parent);
+ vdev_blk = container_of(gdev, VubDev, parent);
+ memcpy(config, &vdev_blk->blkcfg, len);
+
+ return 0;
+}
+
+static int
+vub_set_config(VuDev *vu_dev, const uint8_t *data,
+ uint32_t offset, uint32_t size, uint32_t flags)
+{
+ VugDev *gdev;
+ VubDev *vdev_blk;
+ uint8_t wce;
+ int fd;
+
+ /* don't support live migration */
+ if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
+ return -1;
+ }
+
+ gdev = container_of(vu_dev, VugDev, parent);
+ vdev_blk = container_of(gdev, VubDev, parent);
+
+ if (offset != offsetof(struct virtio_blk_config, wce) ||
+ size != 1) {
+ return -1;
+ }
+
+ wce = *data;
+ if (wce == vdev_blk->blkcfg.wce) {
+ /* Do nothing as same with old configuration */
+ return 0;
+ }
+
+ vdev_blk->blkcfg.wce = wce;
+ fprintf(stdout, "Write Cache Policy Changed\n");
+ if (vdev_blk->blk_fd >= 0) {
+ close(vdev_blk->blk_fd);
+ vdev_blk->blk_fd = -1;
+ }
+
+ fd = vub_open(vdev_blk->blk_name, wce);
+ if (fd < 0) {
+ fprintf(stderr, "Error to open block device %s\n", vdev_blk->blk_name);
+ vdev_blk->blk_fd = -1;
+ return -1;
+ }
+ vdev_blk->blk_fd = fd;
+
+ return 0;
+}
+
+static const VuDevIface vub_iface = {
+ .get_features = vub_get_features,
+ .queue_set_started = vub_queue_set_started,
+ .get_config = vub_get_config,
+ .set_config = vub_set_config,
+};
+
+static int unix_sock_new(char *unix_fn)
+{
+ int sock;
+ struct sockaddr_un un;
+ size_t len;
+
+ assert(unix_fn);
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock <= 0) {
+ perror("socket");
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
+ len = sizeof(un.sun_family) + strlen(un.sun_path);
+
+ (void)unlink(unix_fn);
+ if (bind(sock, (struct sockaddr *)&un, len) < 0) {
+ perror("bind");
+ goto fail;
+ }
+
+ if (listen(sock, 1) < 0) {
+ perror("listen");
+ goto fail;
+ }
+
+ return sock;
+
+fail:
+ (void)close(sock);
+
+ return -1;
+}
+
+static void vub_free(struct VubDev *vdev_blk)
+{
+ if (!vdev_blk) {
+ return;
+ }
+
+ g_main_loop_unref(vdev_blk->loop);
+ if (vdev_blk->blk_fd >= 0) {
+ close(vdev_blk->blk_fd);
+ }
+ g_free(vdev_blk);
+}
+
+static uint32_t
+vub_get_blocksize(int fd)
+{
+ uint32_t blocksize = 512;
+
+ #if defined(__linux__) && defined(BLKSSZGET)
+ if (ioctl(fd, BLKSSZGET, &blocksize) == 0) {
+ return blocklen;
+ }
+ #endif
+
+ return blocksize;
+}
+
+static void
+vub_initialize_config(int fd, struct virtio_blk_config *config)
+{
+ off64_t capacity;
+
+ capacity = lseek64(fd, 0, SEEK_END);
+ config->capacity = capacity >> 9;
+ config->blk_size = vub_get_blocksize(fd);
+ config->size_max = 65536;
+ config->seg_max = 128 - 2;
+ config->min_io_size = 1;
+ config->opt_io_size = 1;
+ config->num_queues = 1;
+}
+
+static VubDev *
+vub_new(char *blk_file)
+{
+ VubDev *vdev_blk;
+
+ vdev_blk = g_new0(VubDev, 1);
+ vdev_blk->loop = g_main_loop_new(NULL, FALSE);
+ vdev_blk->blk_fd = vub_open(blk_file, 0);
+ if (vdev_blk->blk_fd < 0) {
+ fprintf(stderr, "Error to open block device %s\n", blk_file);
+ return NULL;
+ }
+ vdev_blk->blkcfg.wce = 0;
+ vdev_blk->blk_name = blk_file;
+
+ /* fill virtio_blk_config with block parameters */
+ vub_initialize_config(vdev_blk->blk_fd, &vdev_blk->blkcfg);
+
+ return vdev_blk;
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ char *unix_socket = NULL;
+ char *blk_file = NULL;
+ int lsock = -1, csock = -1;
+ VubDev *vdev_blk = NULL;
+
+ while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
+ switch (opt) {
+ case 'b':
+ blk_file = g_strdup(optarg);
+ break;
+ case 's':
+ unix_socket = g_strdup(optarg);
+ break;
+ case 'h':
+ default:
+ printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
+ " | [ -h ]\n", argv[0]);
+ return 0;
+ }
+ }
+
+ if (!unix_socket || !blk_file) {
+ printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
+ " [ -h ]\n", argv[0]);
+ return -1;
+ }
+
+ lsock = unix_sock_new(unix_socket);
+ if (lsock < 0) {
+ goto err;
+ }
+
+ csock = accept(lsock, (void *)0, (void *)0);
+ if (csock < 0) {
+ fprintf(stderr, "Accept error %s\n", strerror(errno));
+ goto err;
+ }
+
+ vdev_blk = vub_new(blk_file);
+ if (!vdev_blk) {
+ goto err;
+ }
+
+ vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
+
+ g_main_loop_run(vdev_blk->loop);
+
+ vug_deinit(&vdev_blk->parent);
+
+err:
+ vub_free(vdev_blk);
+ if (csock >= 0) {
+ close(csock);
+ }
+ if (lsock >= 0) {
+ close(lsock);
+ }
+ g_free(unix_socket);
+ g_free(blk_file);
+
+ return 0;
+}
--
1.9.3
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application
2018-01-02 3:55 ` [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application Changpeng Liu
@ 2018-01-02 15:43 ` Marc-André Lureau
2018-01-03 2:06 ` Liu, Changpeng
2018-01-03 4:12 ` Liu, Changpeng
0 siblings, 2 replies; 13+ messages in thread
From: Marc-André Lureau @ 2018-01-02 15:43 UTC (permalink / raw)
To: Changpeng Liu
Cc: QEMU, james.r.harris, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> This commit introcudes a vhost-user-blk backend device, it uses UNIX'
introcudes -> introduces
> domain socket to communicate with QEMU. The vhost-user-blk sample
> application should be used with QEMU vhost-user-blk-pci device.
>
> To use it, complie with:
> make vhost-user-blk
>
> and start like this:
> vhost-user-blk -b /dev/sdb -s /path/vhost.socket
>
> Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> ---
> .gitignore | 1 +
> Makefile | 3 +
> Makefile.objs | 1 +
> contrib/vhost-user-blk/Makefile.objs | 1 +
> contrib/vhost-user-blk/vhost-user-blk.c | 543 ++++++++++++++++++++++++++++++++
> 5 files changed, 549 insertions(+)
> create mode 100644 contrib/vhost-user-blk/Makefile.objs
> create mode 100644 contrib/vhost-user-blk/vhost-user-blk.c
>
> diff --git a/.gitignore b/.gitignore
> index 433f64f..704b222 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -54,6 +54,7 @@
> /module_block.h
> /scsi/qemu-pr-helper
> /vhost-user-scsi
> +/vhost-user-blk
> /fsdev/virtfs-proxy-helper
> *.tmp
> *.[1-9]
> diff --git a/Makefile b/Makefile
> index d86ecd2..f021fc8 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -331,6 +331,7 @@ dummy := $(call unnest-vars,, \
> ivshmem-server-obj-y \
> libvhost-user-obj-y \
> vhost-user-scsi-obj-y \
> + vhost-user-blk-obj-y \
> qga-vss-dll-obj-y \
> block-obj-y \
> block-obj-m \
> @@ -562,6 +563,8 @@ ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS)
> endif
> vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
> $(call LINK, $^)
> +vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
> + $(call LINK, $^)
>
> module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
> $(call quiet-command,$(PYTHON) $< $@ \
> diff --git a/Makefile.objs b/Makefile.objs
> index 285c6f3..ae9aef7 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -115,6 +115,7 @@ libvhost-user-obj-y = contrib/libvhost-user/
> vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
> vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
> vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
> +vhost-user-blk-obj-y = contrib/vhost-user-blk/
>
> ######################################################################
> trace-events-subdirs =
> diff --git a/contrib/vhost-user-blk/Makefile.objs b/contrib/vhost-user-blk/Makefile.objs
> new file mode 100644
> index 0000000..72e2cdc
> --- /dev/null
> +++ b/contrib/vhost-user-blk/Makefile.objs
> @@ -0,0 +1 @@
> +vhost-user-blk-obj-y = vhost-user-blk.o
> diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c
> new file mode 100644
> index 0000000..4649435
> --- /dev/null
> +++ b/contrib/vhost-user-blk/vhost-user-blk.c
> @@ -0,0 +1,543 @@
> +/*
> + * vhost-user-blk sample application
> + *
> + * Copyright (c) 2017 Intel Corporation. All rights reserved.
> + *
> + * Author:
> + * Changpeng Liu <changpeng.liu@intel.com>
> + *
> + * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
> + * implemention by:
implemention -> implementation
> + * Felipe Franciosi <felipe@nutanix.com>
> + * Anthony Liguori <aliguori@us.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 only.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "standard-headers/linux/virtio_blk.h"
> +#include "contrib/libvhost-user/libvhost-user-glib.h"
> +#include "contrib/libvhost-user/libvhost-user.h"
> +
> +#include <glib.h>
> +
> +struct virtio_blk_inhdr {
> + unsigned char status;
> +};
> +
> +/* vhost user block device */
> +typedef struct VubDev {
> + VugDev parent;
> + int blk_fd;
> + struct virtio_blk_config blkcfg;
> + char *blk_name;
> + GMainLoop *loop;
> +} VubDev;
> +
> +typedef struct VubReq {
> + VuVirtqElement *elem;
> + int64_t sector_num;
> + size_t size;
> + struct virtio_blk_inhdr *in;
> + struct virtio_blk_outhdr *out;
> + VubDev *vdev_blk;
> + struct VuVirtq *vq;
> +} VubReq;
> +
> +/** refer util/iov.c **/
let's use regular /* comment */ (I know vhost-user-scsi uses both
style for some reason)
> +static size_t vub_iov_size(const struct iovec *iov,
> + const unsigned int iov_cnt)
> +{
> + size_t len;
> + unsigned int i;
> +
> + len = 0;
> + for (i = 0; i < iov_cnt; i++) {
> + len += iov[i].iov_len;
> + }
> + return len;
> +}
> +
> +static void vub_panic_cb(VuDev *vu_dev, const char *buf)
> +{
> + VugDev *gdev;
> + VubDev *vdev_blk;
> +
> + assert(vu_dev);
> +
> + gdev = container_of(vu_dev, VugDev, parent);
> + vdev_blk = container_of(gdev, VubDev, parent);
> + if (buf) {
> + g_warning("vu_panic: %s", buf);
> + }
> +
> + g_main_loop_quit(vdev_blk->loop);
> +}
> +
> +static void vub_req_complete(VubReq *req)
> +{
> + VugDev *gdev = &req->vdev_blk->parent;
> + VuDev *vu_dev = &gdev->parent;
> +
> + /* IO size with 1 extra status byte */
> + vu_queue_push(vu_dev, req->vq, req->elem,
> + req->size + 1);
> + vu_queue_notify(vu_dev, req->vq);
> +
> + if (req->elem) {
> + free(req->elem);
> + }
> + g_free(req);
> +}
> +
> +static int vub_open(const char *file_name, bool wce)
> +{
> + int fd;
> + int flags = O_RDWR;
> +
> + if (!wce) {
> + flags |= O_DIRECT;
> + }
> +
> + fd = open(file_name, flags);
> + if (fd < 0) {
> + fprintf(stderr, "Cannot open file %s, %s\n", file_name,
> + strerror(errno));
> + return -1;
> + }
> +
> + return fd;
> +}
> +
> +static ssize_t
> +vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> +{
> + VubDev *vdev_blk = req->vdev_blk;
> + ssize_t rc;
> +
> + if (!iovcnt) {
> + fprintf(stderr, "Invalid Read IOV count\n");
> + return -1;
> + }
> +
> + req->size = vub_iov_size(iov, iovcnt);
> + rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> + if (rc < 0) {
> + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> + vdev_blk->blk_name, req->sector_num, req->size,
> + strerror(errno));
> + return -1;
> + }
> +
> + return rc;
> +}
> +
> +static ssize_t
> +vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> +{
> + VubDev *vdev_blk = req->vdev_blk;
> + ssize_t rc;
> +
> + if (!iovcnt) {
> + fprintf(stderr, "Invalid Write IOV count\n");
> + return -1;
> + }
> +
> + req->size = vub_iov_size(iov, iovcnt);
> + rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> + if (rc < 0) {
> + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> + vdev_blk->blk_name, req->sector_num, req->size,
> + strerror(errno));
> + return -1;
> + }
> +
> + return rc;
> +}
> +
> +static void
> +vub_flush(VubReq *req)
> +{
> + VubDev *vdev_blk = req->vdev_blk;
> +
> + fdatasync(vdev_blk->blk_fd);
> +}
> +
> +static int vub_virtio_process_req(VubDev *vdev_blk,
> + VuVirtq *vq)
> +{
> + VugDev *gdev = &vdev_blk->parent;
> + VuDev *vu_dev = &gdev->parent;
> + VuVirtqElement *elem;
> + uint32_t type;
> + unsigned in_num;
> + unsigned out_num;
> + VubReq *req;
> +
> + elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
(there should be an optimization possible here, where you allocate
with a size > VuVirtqElement: this should avoid the need for second
allocation of VubReq)
> + if (!elem) {
> + return -1;
> + }
> +
> + /* refer to hw/block/virtio_blk.c */
> + if (elem->out_num < 1 || elem->in_num < 1) {
> + fprintf(stderr, "virtio-blk request missing headers\n");
> + free(elem);
> + return -1;
> + }
> +
> + req = g_new0(VubReq, 1);
> + req->vdev_blk = vdev_blk;
> + req->vq = vq;
> + req->elem = elem;
> +
> + in_num = elem->in_num;
> + out_num = elem->out_num;
> +
> + /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
> + if (elem->out_sg[0].iov_len < sizeof(struct virtio_blk_outhdr)) {
> + fprintf(stderr, "Invalid outhdr size\n");
> + goto err;
> + }
> + req->out = (struct virtio_blk_outhdr *)elem->out_sg[0].iov_base;
> + out_num--;
> +
> + if (elem->in_sg[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
> + fprintf(stderr, "Invalid inhdr size\n");
> + goto err;
> + }
> + req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base;
> + in_num--;
> +
> + type = le32toh(req->out->type);
> + switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
> + case VIRTIO_BLK_T_IN: {
> + ssize_t ret = 0;
> + bool is_write = type & VIRTIO_BLK_T_OUT;
> + req->sector_num = le64toh(req->out->sector);
> + if (is_write) {
> + ret = vub_writev(req, &elem->out_sg[1], out_num);
> + } else {
> + ret = vub_readv(req, &elem->in_sg[0], in_num);
> + }
> + if (ret >= 0) {
> + req->in->status = VIRTIO_BLK_S_OK;
> + } else {
> + req->in->status = VIRTIO_BLK_S_IOERR;
> + }
> + vub_req_complete(req);
> + break;
> + }
> + case VIRTIO_BLK_T_FLUSH: {
> + vub_flush(req);
> + req->in->status = VIRTIO_BLK_S_OK;
> + vub_req_complete(req);
> + break;
> + }
> + case VIRTIO_BLK_T_GET_ID: {
> + size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num),
> + VIRTIO_BLK_ID_BYTES);
> + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
> + req->in->status = VIRTIO_BLK_S_OK;
> + req->size = elem->in_sg[0].iov_len;
> + vub_req_complete(req);
> + break;
> + }
> + default: {
> + req->in->status = VIRTIO_BLK_S_UNSUPP;
> + vub_req_complete(req);
> + break;
> + }
> + }
> +
> + return 0;
> +
> +err:
> + free(elem);
> + g_free(req);
> + return -1;
> +}
> +
> +static void vub_process_vq(VuDev *vu_dev, int idx)
> +{
> + VugDev *gdev;
> + VubDev *vdev_blk;
> + VuVirtq *vq;
> + int ret;
> +
> + if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
> + fprintf(stderr, "VQ Index out of range: %d\n", idx);
> + vub_panic_cb(vu_dev, NULL);
> + return;
> + }
> +
> + gdev = container_of(vu_dev, VugDev, parent);
> + vdev_blk = container_of(gdev, VubDev, parent);
> + assert(vdev_blk);
> +
> + vq = vu_get_queue(vu_dev, idx);
> + assert(vq);
> +
> + while (1) {
> + ret = vub_virtio_process_req(vdev_blk, vq);
> + if (ret) {
> + break;
> + }
> + }
> +}
> +
> +static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
> +{
> + VuVirtq *vq;
> +
> + assert(vu_dev);
> +
> + vq = vu_get_queue(vu_dev, idx);
> + vu_set_queue_handler(vu_dev, vq, started ? vub_process_vq : NULL);
> +}
> +
> +static uint64_t
> +vub_get_features(VuDev *dev)
> +{
> + return 1ull << VIRTIO_BLK_F_SIZE_MAX |
> + 1ull << VIRTIO_BLK_F_SEG_MAX |
> + 1ull << VIRTIO_BLK_F_TOPOLOGY |
> + 1ull << VIRTIO_BLK_F_BLK_SIZE |
> + 1ull << VIRTIO_BLK_F_FLUSH |
> + 1ull << VIRTIO_BLK_F_CONFIG_WCE |
> + 1ull << VIRTIO_F_VERSION_1 |
> + 1ull << VHOST_USER_F_PROTOCOL_FEATURES;
> +}
> +
> +static int
> +vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
> +{
> + VugDev *gdev;
> + VubDev *vdev_blk;
> +
> + gdev = container_of(vu_dev, VugDev, parent);
> + vdev_blk = container_of(gdev, VubDev, parent);
> + memcpy(config, &vdev_blk->blkcfg, len);
> +
> + return 0;
> +}
> +
> +static int
> +vub_set_config(VuDev *vu_dev, const uint8_t *data,
> + uint32_t offset, uint32_t size, uint32_t flags)
> +{
> + VugDev *gdev;
> + VubDev *vdev_blk;
> + uint8_t wce;
> + int fd;
> +
> + /* don't support live migration */
> + if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
> + return -1;
> + }
> +
> + gdev = container_of(vu_dev, VugDev, parent);
> + vdev_blk = container_of(gdev, VubDev, parent);
> +
> + if (offset != offsetof(struct virtio_blk_config, wce) ||
> + size != 1) {
> + return -1;
> + }
> +
> + wce = *data;
> + if (wce == vdev_blk->blkcfg.wce) {
> + /* Do nothing as same with old configuration */
> + return 0;
> + }
> +
> + vdev_blk->blkcfg.wce = wce;
> + fprintf(stdout, "Write Cache Policy Changed\n");
> + if (vdev_blk->blk_fd >= 0) {
> + close(vdev_blk->blk_fd);
> + vdev_blk->blk_fd = -1;
> + }
> +
> + fd = vub_open(vdev_blk->blk_name, wce);
> + if (fd < 0) {
> + fprintf(stderr, "Error to open block device %s\n", vdev_blk->blk_name);
> + vdev_blk->blk_fd = -1;
> + return -1;
> + }
> + vdev_blk->blk_fd = fd;
> +
> + return 0;
> +}
> +
> +static const VuDevIface vub_iface = {
> + .get_features = vub_get_features,
> + .queue_set_started = vub_queue_set_started,
> + .get_config = vub_get_config,
> + .set_config = vub_set_config,
> +};
> +
> +static int unix_sock_new(char *unix_fn)
> +{
> + int sock;
> + struct sockaddr_un un;
> + size_t len;
> +
> + assert(unix_fn);
> +
> + sock = socket(AF_UNIX, SOCK_STREAM, 0);
> + if (sock <= 0) {
> + perror("socket");
> + return -1;
> + }
> +
> + un.sun_family = AF_UNIX;
> + (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
> + len = sizeof(un.sun_family) + strlen(un.sun_path);
> +
> + (void)unlink(unix_fn);
> + if (bind(sock, (struct sockaddr *)&un, len) < 0) {
> + perror("bind");
> + goto fail;
> + }
> +
> + if (listen(sock, 1) < 0) {
> + perror("listen");
> + goto fail;
> + }
> +
> + return sock;
> +
> +fail:
> + (void)close(sock);
> +
> + return -1;
> +}
> +
> +static void vub_free(struct VubDev *vdev_blk)
> +{
> + if (!vdev_blk) {
> + return;
> + }
> +
> + g_main_loop_unref(vdev_blk->loop);
> + if (vdev_blk->blk_fd >= 0) {
> + close(vdev_blk->blk_fd);
> + }
> + g_free(vdev_blk);
> +}
> +
> +static uint32_t
> +vub_get_blocksize(int fd)
> +{
> + uint32_t blocksize = 512;
> +
> + #if defined(__linux__) && defined(BLKSSZGET)
Weird indentation, please keep preprocessor lines at first column.
> + if (ioctl(fd, BLKSSZGET, &blocksize) == 0) {
> + return blocklen;
> + }
> + #endif
> +
> + return blocksize;
> +}
> +
> +static void
> +vub_initialize_config(int fd, struct virtio_blk_config *config)
> +{
> + off64_t capacity;
> +
> + capacity = lseek64(fd, 0, SEEK_END);
> + config->capacity = capacity >> 9;
> + config->blk_size = vub_get_blocksize(fd);
> + config->size_max = 65536;
> + config->seg_max = 128 - 2;
> + config->min_io_size = 1;
> + config->opt_io_size = 1;
> + config->num_queues = 1;
> +}
> +
> +static VubDev *
> +vub_new(char *blk_file)
> +{
> + VubDev *vdev_blk;
> +
> + vdev_blk = g_new0(VubDev, 1);
> + vdev_blk->loop = g_main_loop_new(NULL, FALSE);
> + vdev_blk->blk_fd = vub_open(blk_file, 0);
> + if (vdev_blk->blk_fd < 0) {
> + fprintf(stderr, "Error to open block device %s\n", blk_file);
> + return NULL;
You leak vdev_blk here.
> + }
> + vdev_blk->blkcfg.wce = 0;
> + vdev_blk->blk_name = blk_file;
> +
> + /* fill virtio_blk_config with block parameters */
> + vub_initialize_config(vdev_blk->blk_fd, &vdev_blk->blkcfg);
> +
> + return vdev_blk;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + int opt;
> + char *unix_socket = NULL;
> + char *blk_file = NULL;
> + int lsock = -1, csock = -1;
> + VubDev *vdev_blk = NULL;
> +
> + while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
> + switch (opt) {
> + case 'b':
> + blk_file = g_strdup(optarg);
> + break;
> + case 's':
> + unix_socket = g_strdup(optarg);
> + break;
> + case 'h':
> + default:
> + printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
> + " | [ -h ]\n", argv[0]);
> + return 0;
> + }
> + }
> +
> + if (!unix_socket || !blk_file) {
> + printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
> + " [ -h ]\n", argv[0]);
> + return -1;
> + }
> +
> + lsock = unix_sock_new(unix_socket);
> + if (lsock < 0) {
> + goto err;
> + }
> +
> + csock = accept(lsock, (void *)0, (void *)0);
> + if (csock < 0) {
> + fprintf(stderr, "Accept error %s\n", strerror(errno));
> + goto err;
> + }
> +
> + vdev_blk = vub_new(blk_file);
> + if (!vdev_blk) {
> + goto err;
> + }
> +
> + vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
> +
> + g_main_loop_run(vdev_blk->loop);
> +
> + vug_deinit(&vdev_blk->parent);
> +
> +err:
> + vub_free(vdev_blk);
> + if (csock >= 0) {
> + close(csock);
> + }
> + if (lsock >= 0) {
> + close(lsock);
> + }
> + g_free(unix_socket);
> + g_free(blk_file);
> +
> + return 0;
> +}
> --
> 1.9.3
>
>
thanks
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application
2018-01-02 15:43 ` Marc-André Lureau
@ 2018-01-03 2:06 ` Liu, Changpeng
2018-01-03 4:12 ` Liu, Changpeng
1 sibling, 0 replies; 13+ messages in thread
From: Liu, Changpeng @ 2018-01-03 2:06 UTC (permalink / raw)
To: Marc-André Lureau
Cc: QEMU, Harris, James R, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
> -----Original Message-----
> From: Marc-André Lureau [mailto:marcandre.lureau@gmail.com]
> Sent: Tuesday, January 2, 2018 11:44 PM
> To: Liu, Changpeng <changpeng.liu@intel.com>
> Cc: QEMU <qemu-devel@nongnu.org>; Harris, James R <james.r.harris@intel.com>;
> Michael S. Tsirkin <mst@redhat.com>; Stefan Hajnoczi <stefanha@gmail.com>;
> Paolo Bonzini <pbonzini@redhat.com>; Felipe Franciosi <felipe@nutanix.com>
> Subject: Re: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a
> vhost-user-blk sample application
>
> On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com> wrote:
> > This commit introcudes a vhost-user-blk backend device, it uses UNIX'
>
> introcudes -> introduces
Thanks.
>
> > domain socket to communicate with QEMU. The vhost-user-blk sample
> > application should be used with QEMU vhost-user-blk-pci device.
> >
> > To use it, complie with:
> > make vhost-user-blk
> >
> > and start like this:
> > vhost-user-blk -b /dev/sdb -s /path/vhost.socket
> >
> > Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> > ---
> > .gitignore | 1 +
> > Makefile | 3 +
> > Makefile.objs | 1 +
> > contrib/vhost-user-blk/Makefile.objs | 1 +
> > contrib/vhost-user-blk/vhost-user-blk.c | 543
> ++++++++++++++++++++++++++++++++
> > 5 files changed, 549 insertions(+)
> > create mode 100644 contrib/vhost-user-blk/Makefile.objs
> > create mode 100644 contrib/vhost-user-blk/vhost-user-blk.c
> >
> > diff --git a/.gitignore b/.gitignore
> > index 433f64f..704b222 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -54,6 +54,7 @@
> > /module_block.h
> > /scsi/qemu-pr-helper
> > /vhost-user-scsi
> > +/vhost-user-blk
> > /fsdev/virtfs-proxy-helper
> > *.tmp
> > *.[1-9]
> > diff --git a/Makefile b/Makefile
> > index d86ecd2..f021fc8 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -331,6 +331,7 @@ dummy := $(call unnest-vars,, \
> > ivshmem-server-obj-y \
> > libvhost-user-obj-y \
> > vhost-user-scsi-obj-y \
> > + vhost-user-blk-obj-y \
> > qga-vss-dll-obj-y \
> > block-obj-y \
> > block-obj-m \
> > @@ -562,6 +563,8 @@ ivshmem-server$(EXESUF): $(ivshmem-server-obj-y)
> $(COMMON_LDADDS)
> > endif
> > vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
> > $(call LINK, $^)
> > +vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
> > + $(call LINK, $^)
> >
> > module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-
> host.mak
> > $(call quiet-command,$(PYTHON) $< $@ \
> > diff --git a/Makefile.objs b/Makefile.objs
> > index 285c6f3..ae9aef7 100644
> > --- a/Makefile.objs
> > +++ b/Makefile.objs
> > @@ -115,6 +115,7 @@ libvhost-user-obj-y = contrib/libvhost-user/
> > vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
> > vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
> > vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
> > +vhost-user-blk-obj-y = contrib/vhost-user-blk/
> >
> >
> ####################################################################
> ##
> > trace-events-subdirs =
> > diff --git a/contrib/vhost-user-blk/Makefile.objs b/contrib/vhost-user-
> blk/Makefile.objs
> > new file mode 100644
> > index 0000000..72e2cdc
> > --- /dev/null
> > +++ b/contrib/vhost-user-blk/Makefile.objs
> > @@ -0,0 +1 @@
> > +vhost-user-blk-obj-y = vhost-user-blk.o
> > diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-
> blk/vhost-user-blk.c
> > new file mode 100644
> > index 0000000..4649435
> > --- /dev/null
> > +++ b/contrib/vhost-user-blk/vhost-user-blk.c
> > @@ -0,0 +1,543 @@
> > +/*
> > + * vhost-user-blk sample application
> > + *
> > + * Copyright (c) 2017 Intel Corporation. All rights reserved.
> > + *
> > + * Author:
> > + * Changpeng Liu <changpeng.liu@intel.com>
> > + *
> > + * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
> > + * implemention by:
>
> implemention -> implementation
Thanks.
>
> > + * Felipe Franciosi <felipe@nutanix.com>
> > + * Anthony Liguori <aliguori@us.ibm.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPL, version 2 only.
> > + * See the COPYING file in the top-level directory.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "standard-headers/linux/virtio_blk.h"
> > +#include "contrib/libvhost-user/libvhost-user-glib.h"
> > +#include "contrib/libvhost-user/libvhost-user.h"
> > +
> > +#include <glib.h>
> > +
> > +struct virtio_blk_inhdr {
> > + unsigned char status;
> > +};
> > +
> > +/* vhost user block device */
> > +typedef struct VubDev {
> > + VugDev parent;
> > + int blk_fd;
> > + struct virtio_blk_config blkcfg;
> > + char *blk_name;
> > + GMainLoop *loop;
> > +} VubDev;
> > +
> > +typedef struct VubReq {
> > + VuVirtqElement *elem;
> > + int64_t sector_num;
> > + size_t size;
> > + struct virtio_blk_inhdr *in;
> > + struct virtio_blk_outhdr *out;
> > + VubDev *vdev_blk;
> > + struct VuVirtq *vq;
> > +} VubReq;
> > +
> > +/** refer util/iov.c **/
>
> let's use regular /* comment */ (I know vhost-user-scsi uses both
> style for some reason)
Ok.
>
> > +static size_t vub_iov_size(const struct iovec *iov,
> > + const unsigned int iov_cnt)
> > +{
> > + size_t len;
> > + unsigned int i;
> > +
> > + len = 0;
> > + for (i = 0; i < iov_cnt; i++) {
> > + len += iov[i].iov_len;
> > + }
> > + return len;
> > +}
> > +
> > +static void vub_panic_cb(VuDev *vu_dev, const char *buf)
> > +{
> > + VugDev *gdev;
> > + VubDev *vdev_blk;
> > +
> > + assert(vu_dev);
> > +
> > + gdev = container_of(vu_dev, VugDev, parent);
> > + vdev_blk = container_of(gdev, VubDev, parent);
> > + if (buf) {
> > + g_warning("vu_panic: %s", buf);
> > + }
> > +
> > + g_main_loop_quit(vdev_blk->loop);
> > +}
> > +
> > +static void vub_req_complete(VubReq *req)
> > +{
> > + VugDev *gdev = &req->vdev_blk->parent;
> > + VuDev *vu_dev = &gdev->parent;
> > +
> > + /* IO size with 1 extra status byte */
> > + vu_queue_push(vu_dev, req->vq, req->elem,
> > + req->size + 1);
> > + vu_queue_notify(vu_dev, req->vq);
> > +
> > + if (req->elem) {
> > + free(req->elem);
> > + }
> > + g_free(req);
> > +}
> > +
> > +static int vub_open(const char *file_name, bool wce)
> > +{
> > + int fd;
> > + int flags = O_RDWR;
> > +
> > + if (!wce) {
> > + flags |= O_DIRECT;
> > + }
> > +
> > + fd = open(file_name, flags);
> > + if (fd < 0) {
> > + fprintf(stderr, "Cannot open file %s, %s\n", file_name,
> > + strerror(errno));
> > + return -1;
> > + }
> > +
> > + return fd;
> > +}
> > +
> > +static ssize_t
> > +vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> > +{
> > + VubDev *vdev_blk = req->vdev_blk;
> > + ssize_t rc;
> > +
> > + if (!iovcnt) {
> > + fprintf(stderr, "Invalid Read IOV count\n");
> > + return -1;
> > + }
> > +
> > + req->size = vub_iov_size(iov, iovcnt);
> > + rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> > + if (rc < 0) {
> > + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> > + vdev_blk->blk_name, req->sector_num, req->size,
> > + strerror(errno));
> > + return -1;
> > + }
> > +
> > + return rc;
> > +}
> > +
> > +static ssize_t
> > +vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> > +{
> > + VubDev *vdev_blk = req->vdev_blk;
> > + ssize_t rc;
> > +
> > + if (!iovcnt) {
> > + fprintf(stderr, "Invalid Write IOV count\n");
> > + return -1;
> > + }
> > +
> > + req->size = vub_iov_size(iov, iovcnt);
> > + rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> > + if (rc < 0) {
> > + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> > + vdev_blk->blk_name, req->sector_num, req->size,
> > + strerror(errno));
> > + return -1;
> > + }
> > +
> > + return rc;
> > +}
> > +
> > +static void
> > +vub_flush(VubReq *req)
> > +{
> > + VubDev *vdev_blk = req->vdev_blk;
> > +
> > + fdatasync(vdev_blk->blk_fd);
> > +}
> > +
> > +static int vub_virtio_process_req(VubDev *vdev_blk,
> > + VuVirtq *vq)
> > +{
> > + VugDev *gdev = &vdev_blk->parent;
> > + VuDev *vu_dev = &gdev->parent;
> > + VuVirtqElement *elem;
> > + uint32_t type;
> > + unsigned in_num;
> > + unsigned out_num;
> > + VubReq *req;
> > +
> > + elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
>
> (there should be an optimization possible here, where you allocate
> with a size > VuVirtqElement: this should avoid the need for second
> allocation of VubReq)
Ok, will remove the following allocation for VubReq.
>
> > + if (!elem) {
> > + return -1;
> > + }
> > +
> > + /* refer to hw/block/virtio_blk.c */
> > + if (elem->out_num < 1 || elem->in_num < 1) {
> > + fprintf(stderr, "virtio-blk request missing headers\n");
> > + free(elem);
> > + return -1;
> > + }
> > +
> > + req = g_new0(VubReq, 1);
> > + req->vdev_blk = vdev_blk;
> > + req->vq = vq;
> > + req->elem = elem;
> > +
> > + in_num = elem->in_num;
> > + out_num = elem->out_num;
> > +
> > + /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
> > + if (elem->out_sg[0].iov_len < sizeof(struct virtio_blk_outhdr)) {
> > + fprintf(stderr, "Invalid outhdr size\n");
> > + goto err;
> > + }
> > + req->out = (struct virtio_blk_outhdr *)elem->out_sg[0].iov_base;
> > + out_num--;
> > +
> > + if (elem->in_sg[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
> > + fprintf(stderr, "Invalid inhdr size\n");
> > + goto err;
> > + }
> > + req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base;
> > + in_num--;
> > +
> > + type = le32toh(req->out->type);
> > + switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
> > + case VIRTIO_BLK_T_IN: {
> > + ssize_t ret = 0;
> > + bool is_write = type & VIRTIO_BLK_T_OUT;
> > + req->sector_num = le64toh(req->out->sector);
> > + if (is_write) {
> > + ret = vub_writev(req, &elem->out_sg[1], out_num);
> > + } else {
> > + ret = vub_readv(req, &elem->in_sg[0], in_num);
> > + }
> > + if (ret >= 0) {
> > + req->in->status = VIRTIO_BLK_S_OK;
> > + } else {
> > + req->in->status = VIRTIO_BLK_S_IOERR;
> > + }
> > + vub_req_complete(req);
> > + break;
> > + }
> > + case VIRTIO_BLK_T_FLUSH: {
> > + vub_flush(req);
> > + req->in->status = VIRTIO_BLK_S_OK;
> > + vub_req_complete(req);
> > + break;
> > + }
> > + case VIRTIO_BLK_T_GET_ID: {
> > + size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num),
> > + VIRTIO_BLK_ID_BYTES);
> > + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
> > + req->in->status = VIRTIO_BLK_S_OK;
> > + req->size = elem->in_sg[0].iov_len;
> > + vub_req_complete(req);
> > + break;
> > + }
> > + default: {
> > + req->in->status = VIRTIO_BLK_S_UNSUPP;
> > + vub_req_complete(req);
> > + break;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err:
> > + free(elem);
> > + g_free(req);
> > + return -1;
> > +}
> > +
> > +static void vub_process_vq(VuDev *vu_dev, int idx)
> > +{
> > + VugDev *gdev;
> > + VubDev *vdev_blk;
> > + VuVirtq *vq;
> > + int ret;
> > +
> > + if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
> > + fprintf(stderr, "VQ Index out of range: %d\n", idx);
> > + vub_panic_cb(vu_dev, NULL);
> > + return;
> > + }
> > +
> > + gdev = container_of(vu_dev, VugDev, parent);
> > + vdev_blk = container_of(gdev, VubDev, parent);
> > + assert(vdev_blk);
> > +
> > + vq = vu_get_queue(vu_dev, idx);
> > + assert(vq);
> > +
> > + while (1) {
> > + ret = vub_virtio_process_req(vdev_blk, vq);
> > + if (ret) {
> > + break;
> > + }
> > + }
> > +}
> > +
> > +static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
> > +{
> > + VuVirtq *vq;
> > +
> > + assert(vu_dev);
> > +
> > + vq = vu_get_queue(vu_dev, idx);
> > + vu_set_queue_handler(vu_dev, vq, started ? vub_process_vq : NULL);
> > +}
> > +
> > +static uint64_t
> > +vub_get_features(VuDev *dev)
> > +{
> > + return 1ull << VIRTIO_BLK_F_SIZE_MAX |
> > + 1ull << VIRTIO_BLK_F_SEG_MAX |
> > + 1ull << VIRTIO_BLK_F_TOPOLOGY |
> > + 1ull << VIRTIO_BLK_F_BLK_SIZE |
> > + 1ull << VIRTIO_BLK_F_FLUSH |
> > + 1ull << VIRTIO_BLK_F_CONFIG_WCE |
> > + 1ull << VIRTIO_F_VERSION_1 |
> > + 1ull << VHOST_USER_F_PROTOCOL_FEATURES;
> > +}
> > +
> > +static int
> > +vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
> > +{
> > + VugDev *gdev;
> > + VubDev *vdev_blk;
> > +
> > + gdev = container_of(vu_dev, VugDev, parent);
> > + vdev_blk = container_of(gdev, VubDev, parent);
> > + memcpy(config, &vdev_blk->blkcfg, len);
> > +
> > + return 0;
> > +}
> > +
> > +static int
> > +vub_set_config(VuDev *vu_dev, const uint8_t *data,
> > + uint32_t offset, uint32_t size, uint32_t flags)
> > +{
> > + VugDev *gdev;
> > + VubDev *vdev_blk;
> > + uint8_t wce;
> > + int fd;
> > +
> > + /* don't support live migration */
> > + if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
> > + return -1;
> > + }
> > +
> > + gdev = container_of(vu_dev, VugDev, parent);
> > + vdev_blk = container_of(gdev, VubDev, parent);
> > +
> > + if (offset != offsetof(struct virtio_blk_config, wce) ||
> > + size != 1) {
> > + return -1;
> > + }
> > +
> > + wce = *data;
> > + if (wce == vdev_blk->blkcfg.wce) {
> > + /* Do nothing as same with old configuration */
> > + return 0;
> > + }
> > +
> > + vdev_blk->blkcfg.wce = wce;
> > + fprintf(stdout, "Write Cache Policy Changed\n");
> > + if (vdev_blk->blk_fd >= 0) {
> > + close(vdev_blk->blk_fd);
> > + vdev_blk->blk_fd = -1;
> > + }
> > +
> > + fd = vub_open(vdev_blk->blk_name, wce);
> > + if (fd < 0) {
> > + fprintf(stderr, "Error to open block device %s\n", vdev_blk->blk_name);
> > + vdev_blk->blk_fd = -1;
> > + return -1;
> > + }
> > + vdev_blk->blk_fd = fd;
> > +
> > + return 0;
> > +}
> > +
> > +static const VuDevIface vub_iface = {
> > + .get_features = vub_get_features,
> > + .queue_set_started = vub_queue_set_started,
> > + .get_config = vub_get_config,
> > + .set_config = vub_set_config,
> > +};
> > +
> > +static int unix_sock_new(char *unix_fn)
> > +{
> > + int sock;
> > + struct sockaddr_un un;
> > + size_t len;
> > +
> > + assert(unix_fn);
> > +
> > + sock = socket(AF_UNIX, SOCK_STREAM, 0);
> > + if (sock <= 0) {
> > + perror("socket");
> > + return -1;
> > + }
> > +
> > + un.sun_family = AF_UNIX;
> > + (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
> > + len = sizeof(un.sun_family) + strlen(un.sun_path);
> > +
> > + (void)unlink(unix_fn);
> > + if (bind(sock, (struct sockaddr *)&un, len) < 0) {
> > + perror("bind");
> > + goto fail;
> > + }
> > +
> > + if (listen(sock, 1) < 0) {
> > + perror("listen");
> > + goto fail;
> > + }
> > +
> > + return sock;
> > +
> > +fail:
> > + (void)close(sock);
> > +
> > + return -1;
> > +}
> > +
> > +static void vub_free(struct VubDev *vdev_blk)
> > +{
> > + if (!vdev_blk) {
> > + return;
> > + }
> > +
> > + g_main_loop_unref(vdev_blk->loop);
> > + if (vdev_blk->blk_fd >= 0) {
> > + close(vdev_blk->blk_fd);
> > + }
> > + g_free(vdev_blk);
> > +}
> > +
> > +static uint32_t
> > +vub_get_blocksize(int fd)
> > +{
> > + uint32_t blocksize = 512;
> > +
> > + #if defined(__linux__) && defined(BLKSSZGET)
>
> Weird indentation, please keep preprocessor lines at first column.
Ok.
>
> > + if (ioctl(fd, BLKSSZGET, &blocksize) == 0) {
> > + return blocklen;
> > + }
> > + #endif
> > +
> > + return blocksize;
> > +}
> > +
> > +static void
> > +vub_initialize_config(int fd, struct virtio_blk_config *config)
> > +{
> > + off64_t capacity;
> > +
> > + capacity = lseek64(fd, 0, SEEK_END);
> > + config->capacity = capacity >> 9;
> > + config->blk_size = vub_get_blocksize(fd);
> > + config->size_max = 65536;
> > + config->seg_max = 128 - 2;
> > + config->min_io_size = 1;
> > + config->opt_io_size = 1;
> > + config->num_queues = 1;
> > +}
> > +
> > +static VubDev *
> > +vub_new(char *blk_file)
> > +{
> > + VubDev *vdev_blk;
> > +
> > + vdev_blk = g_new0(VubDev, 1);
> > + vdev_blk->loop = g_main_loop_new(NULL, FALSE);
> > + vdev_blk->blk_fd = vub_open(blk_file, 0);
> > + if (vdev_blk->blk_fd < 0) {
> > + fprintf(stderr, "Error to open block device %s\n", blk_file);
> > + return NULL;
>
> You leak vdev_blk here.
Will fix here.
>
> > + }
> > + vdev_blk->blkcfg.wce = 0;
> > + vdev_blk->blk_name = blk_file;
> > +
> > + /* fill virtio_blk_config with block parameters */
> > + vub_initialize_config(vdev_blk->blk_fd, &vdev_blk->blkcfg);
> > +
> > + return vdev_blk;
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > + int opt;
> > + char *unix_socket = NULL;
> > + char *blk_file = NULL;
> > + int lsock = -1, csock = -1;
> > + VubDev *vdev_blk = NULL;
> > +
> > + while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
> > + switch (opt) {
> > + case 'b':
> > + blk_file = g_strdup(optarg);
> > + break;
> > + case 's':
> > + unix_socket = g_strdup(optarg);
> > + break;
> > + case 'h':
> > + default:
> > + printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
> > + " | [ -h ]\n", argv[0]);
> > + return 0;
> > + }
> > + }
> > +
> > + if (!unix_socket || !blk_file) {
> > + printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
> > + " [ -h ]\n", argv[0]);
> > + return -1;
> > + }
> > +
> > + lsock = unix_sock_new(unix_socket);
> > + if (lsock < 0) {
> > + goto err;
> > + }
> > +
> > + csock = accept(lsock, (void *)0, (void *)0);
> > + if (csock < 0) {
> > + fprintf(stderr, "Accept error %s\n", strerror(errno));
> > + goto err;
> > + }
> > +
> > + vdev_blk = vub_new(blk_file);
> > + if (!vdev_blk) {
> > + goto err;
> > + }
> > +
> > + vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
> > +
> > + g_main_loop_run(vdev_blk->loop);
> > +
> > + vug_deinit(&vdev_blk->parent);
> > +
> > +err:
> > + vub_free(vdev_blk);
> > + if (csock >= 0) {
> > + close(csock);
> > + }
> > + if (lsock >= 0) {
> > + close(lsock);
> > + }
> > + g_free(unix_socket);
> > + g_free(blk_file);
> > +
> > + return 0;
> > +}
> > --
> > 1.9.3
> >
> >
>
> thanks
>
> --
> Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a vhost-user-blk sample application
2018-01-02 15:43 ` Marc-André Lureau
2018-01-03 2:06 ` Liu, Changpeng
@ 2018-01-03 4:12 ` Liu, Changpeng
1 sibling, 0 replies; 13+ messages in thread
From: Liu, Changpeng @ 2018-01-03 4:12 UTC (permalink / raw)
To: Marc-André Lureau
Cc: QEMU, Harris, James R, Michael S. Tsirkin, Stefan Hajnoczi,
Paolo Bonzini, Felipe Franciosi
> -----Original Message-----
> From: Liu, Changpeng
> Sent: Wednesday, January 3, 2018 10:07 AM
> To: 'Marc-André Lureau' <marcandre.lureau@gmail.com>
> Cc: QEMU <qemu-devel@nongnu.org>; Harris, James R <james.r.harris@intel.com>;
> Michael S. Tsirkin <mst@redhat.com>; Stefan Hajnoczi <stefanha@gmail.com>;
> Paolo Bonzini <pbonzini@redhat.com>; Felipe Franciosi <felipe@nutanix.com>
> Subject: RE: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a
> vhost-user-blk sample application
>
>
>
> > -----Original Message-----
> > From: Marc-André Lureau [mailto:marcandre.lureau@gmail.com]
> > Sent: Tuesday, January 2, 2018 11:44 PM
> > To: Liu, Changpeng <changpeng.liu@intel.com>
> > Cc: QEMU <qemu-devel@nongnu.org>; Harris, James R
> <james.r.harris@intel.com>;
> > Michael S. Tsirkin <mst@redhat.com>; Stefan Hajnoczi <stefanha@gmail.com>;
> > Paolo Bonzini <pbonzini@redhat.com>; Felipe Franciosi <felipe@nutanix.com>
> > Subject: Re: [Qemu-devel] [PATCH v8 4/4] contrib/vhost-user-blk: introduce a
> > vhost-user-blk sample application
> >
> > On Tue, Jan 2, 2018 at 4:55 AM, Changpeng Liu <changpeng.liu@intel.com>
> wrote:
> > > This commit introcudes a vhost-user-blk backend device, it uses UNIX'
> >
> > introcudes -> introduces
> Thanks.
> >
> > > domain socket to communicate with QEMU. The vhost-user-blk sample
> > > application should be used with QEMU vhost-user-blk-pci device.
> > >
> > > To use it, complie with:
> > > make vhost-user-blk
> > >
> > > and start like this:
> > > vhost-user-blk -b /dev/sdb -s /path/vhost.socket
> > >
> > > Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
> > > ---
> > > .gitignore | 1 +
> > > Makefile | 3 +
> > > Makefile.objs | 1 +
> > > contrib/vhost-user-blk/Makefile.objs | 1 +
> > > contrib/vhost-user-blk/vhost-user-blk.c | 543
> > ++++++++++++++++++++++++++++++++
> > > 5 files changed, 549 insertions(+)
> > > create mode 100644 contrib/vhost-user-blk/Makefile.objs
> > > create mode 100644 contrib/vhost-user-blk/vhost-user-blk.c
> > >
> > > diff --git a/.gitignore b/.gitignore
> > > index 433f64f..704b222 100644
> > > --- a/.gitignore
> > > +++ b/.gitignore
> > > @@ -54,6 +54,7 @@
> > > /module_block.h
> > > /scsi/qemu-pr-helper
> > > /vhost-user-scsi
> > > +/vhost-user-blk
> > > /fsdev/virtfs-proxy-helper
> > > *.tmp
> > > *.[1-9]
> > > diff --git a/Makefile b/Makefile
> > > index d86ecd2..f021fc8 100644
> > > --- a/Makefile
> > > +++ b/Makefile
> > > @@ -331,6 +331,7 @@ dummy := $(call unnest-vars,, \
> > > ivshmem-server-obj-y \
> > > libvhost-user-obj-y \
> > > vhost-user-scsi-obj-y \
> > > + vhost-user-blk-obj-y \
> > > qga-vss-dll-obj-y \
> > > block-obj-y \
> > > block-obj-m \
> > > @@ -562,6 +563,8 @@ ivshmem-server$(EXESUF): $(ivshmem-server-obj-y)
> > $(COMMON_LDADDS)
> > > endif
> > > vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a
> > > $(call LINK, $^)
> > > +vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a
> > > + $(call LINK, $^)
> > >
> > > module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-
> > host.mak
> > > $(call quiet-command,$(PYTHON) $< $@ \
> > > diff --git a/Makefile.objs b/Makefile.objs
> > > index 285c6f3..ae9aef7 100644
> > > --- a/Makefile.objs
> > > +++ b/Makefile.objs
> > > @@ -115,6 +115,7 @@ libvhost-user-obj-y = contrib/libvhost-user/
> > > vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
> > > vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
> > > vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
> > > +vhost-user-blk-obj-y = contrib/vhost-user-blk/
> > >
> > >
> >
> ####################################################################
> > ##
> > > trace-events-subdirs =
> > > diff --git a/contrib/vhost-user-blk/Makefile.objs b/contrib/vhost-user-
> > blk/Makefile.objs
> > > new file mode 100644
> > > index 0000000..72e2cdc
> > > --- /dev/null
> > > +++ b/contrib/vhost-user-blk/Makefile.objs
> > > @@ -0,0 +1 @@
> > > +vhost-user-blk-obj-y = vhost-user-blk.o
> > > diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-
> > blk/vhost-user-blk.c
> > > new file mode 100644
> > > index 0000000..4649435
> > > --- /dev/null
> > > +++ b/contrib/vhost-user-blk/vhost-user-blk.c
> > > @@ -0,0 +1,543 @@
> > > +/*
> > > + * vhost-user-blk sample application
> > > + *
> > > + * Copyright (c) 2017 Intel Corporation. All rights reserved.
> > > + *
> > > + * Author:
> > > + * Changpeng Liu <changpeng.liu@intel.com>
> > > + *
> > > + * This work is based on the "vhost-user-scsi" sample and "virtio-blk" driver
> > > + * implemention by:
> >
> > implemention -> implementation
> Thanks.
> >
> > > + * Felipe Franciosi <felipe@nutanix.com>
> > > + * Anthony Liguori <aliguori@us.ibm.com>
> > > + *
> > > + * This work is licensed under the terms of the GNU GPL, version 2 only.
> > > + * See the COPYING file in the top-level directory.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "standard-headers/linux/virtio_blk.h"
> > > +#include "contrib/libvhost-user/libvhost-user-glib.h"
> > > +#include "contrib/libvhost-user/libvhost-user.h"
> > > +
> > > +#include <glib.h>
> > > +
> > > +struct virtio_blk_inhdr {
> > > + unsigned char status;
> > > +};
> > > +
> > > +/* vhost user block device */
> > > +typedef struct VubDev {
> > > + VugDev parent;
> > > + int blk_fd;
> > > + struct virtio_blk_config blkcfg;
> > > + char *blk_name;
> > > + GMainLoop *loop;
> > > +} VubDev;
> > > +
> > > +typedef struct VubReq {
> > > + VuVirtqElement *elem;
> > > + int64_t sector_num;
> > > + size_t size;
> > > + struct virtio_blk_inhdr *in;
> > > + struct virtio_blk_outhdr *out;
> > > + VubDev *vdev_blk;
> > > + struct VuVirtq *vq;
> > > +} VubReq;
> > > +
> > > +/** refer util/iov.c **/
> >
> > let's use regular /* comment */ (I know vhost-user-scsi uses both
> > style for some reason)
> Ok.
> >
> > > +static size_t vub_iov_size(const struct iovec *iov,
> > > + const unsigned int iov_cnt)
> > > +{
> > > + size_t len;
> > > + unsigned int i;
> > > +
> > > + len = 0;
> > > + for (i = 0; i < iov_cnt; i++) {
> > > + len += iov[i].iov_len;
> > > + }
> > > + return len;
> > > +}
> > > +
> > > +static void vub_panic_cb(VuDev *vu_dev, const char *buf)
> > > +{
> > > + VugDev *gdev;
> > > + VubDev *vdev_blk;
> > > +
> > > + assert(vu_dev);
> > > +
> > > + gdev = container_of(vu_dev, VugDev, parent);
> > > + vdev_blk = container_of(gdev, VubDev, parent);
> > > + if (buf) {
> > > + g_warning("vu_panic: %s", buf);
> > > + }
> > > +
> > > + g_main_loop_quit(vdev_blk->loop);
> > > +}
> > > +
> > > +static void vub_req_complete(VubReq *req)
> > > +{
> > > + VugDev *gdev = &req->vdev_blk->parent;
> > > + VuDev *vu_dev = &gdev->parent;
> > > +
> > > + /* IO size with 1 extra status byte */
> > > + vu_queue_push(vu_dev, req->vq, req->elem,
> > > + req->size + 1);
> > > + vu_queue_notify(vu_dev, req->vq);
> > > +
> > > + if (req->elem) {
> > > + free(req->elem);
> > > + }
> > > + g_free(req);
> > > +}
> > > +
> > > +static int vub_open(const char *file_name, bool wce)
> > > +{
> > > + int fd;
> > > + int flags = O_RDWR;
> > > +
> > > + if (!wce) {
> > > + flags |= O_DIRECT;
> > > + }
> > > +
> > > + fd = open(file_name, flags);
> > > + if (fd < 0) {
> > > + fprintf(stderr, "Cannot open file %s, %s\n", file_name,
> > > + strerror(errno));
> > > + return -1;
> > > + }
> > > +
> > > + return fd;
> > > +}
> > > +
> > > +static ssize_t
> > > +vub_readv(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> > > +{
> > > + VubDev *vdev_blk = req->vdev_blk;
> > > + ssize_t rc;
> > > +
> > > + if (!iovcnt) {
> > > + fprintf(stderr, "Invalid Read IOV count\n");
> > > + return -1;
> > > + }
> > > +
> > > + req->size = vub_iov_size(iov, iovcnt);
> > > + rc = preadv(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> > > + if (rc < 0) {
> > > + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> > > + vdev_blk->blk_name, req->sector_num, req->size,
> > > + strerror(errno));
> > > + return -1;
> > > + }
> > > +
> > > + return rc;
> > > +}
> > > +
> > > +static ssize_t
> > > +vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt)
> > > +{
> > > + VubDev *vdev_blk = req->vdev_blk;
> > > + ssize_t rc;
> > > +
> > > + if (!iovcnt) {
> > > + fprintf(stderr, "Invalid Write IOV count\n");
> > > + return -1;
> > > + }
> > > +
> > > + req->size = vub_iov_size(iov, iovcnt);
> > > + rc = pwritev(vdev_blk->blk_fd, iov, iovcnt, req->sector_num * 512);
> > > + if (rc < 0) {
> > > + fprintf(stderr, "%s, Sector %"PRIu64", Size %lu failed with %s\n",
> > > + vdev_blk->blk_name, req->sector_num, req->size,
> > > + strerror(errno));
> > > + return -1;
> > > + }
> > > +
> > > + return rc;
> > > +}
> > > +
> > > +static void
> > > +vub_flush(VubReq *req)
> > > +{
> > > + VubDev *vdev_blk = req->vdev_blk;
> > > +
> > > + fdatasync(vdev_blk->blk_fd);
> > > +}
> > > +
> > > +static int vub_virtio_process_req(VubDev *vdev_blk,
> > > + VuVirtq *vq)
> > > +{
> > > + VugDev *gdev = &vdev_blk->parent;
> > > + VuDev *vu_dev = &gdev->parent;
> > > + VuVirtqElement *elem;
> > > + uint32_t type;
> > > + unsigned in_num;
> > > + unsigned out_num;
> > > + VubReq *req;
> > > +
> > > + elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
> >
> > (there should be an optimization possible here, where you allocate
> > with a size > VuVirtqElement: this should avoid the need for second
> > allocation of VubReq)
> Ok, will remove the following allocation for VubReq.
Hi Marc-André, your idea works here, but after I implemented the
optimization, I think it's better to keep the original code, because the
size of VuVirtqElement is a variable, it depends of the iov size for each
Guest request, I should almost copy all the logic of function
virtqueue_alloc_element to calculate the size of each VuVirtqElement.
> >
> > > + if (!elem) {
> > > + return -1;
> > > + }
> > > +
> > > + /* refer to hw/block/virtio_blk.c */
> > > + if (elem->out_num < 1 || elem->in_num < 1) {
> > > + fprintf(stderr, "virtio-blk request missing headers\n");
> > > + free(elem);
> > > + return -1;
> > > + }
> > > +
> > > + req = g_new0(VubReq, 1);
> > > + req->vdev_blk = vdev_blk;
> > > + req->vq = vq;
> > > + req->elem = elem;
> > > +
> > > + in_num = elem->in_num;
> > > + out_num = elem->out_num;
> > > +
> > > + /* don't support VIRTIO_F_ANY_LAYOUT and virtio 1.0 only */
> > > + if (elem->out_sg[0].iov_len < sizeof(struct virtio_blk_outhdr)) {
> > > + fprintf(stderr, "Invalid outhdr size\n");
> > > + goto err;
> > > + }
> > > + req->out = (struct virtio_blk_outhdr *)elem->out_sg[0].iov_base;
> > > + out_num--;
> > > +
> > > + if (elem->in_sg[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
> > > + fprintf(stderr, "Invalid inhdr size\n");
> > > + goto err;
> > > + }
> > > + req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base;
> > > + in_num--;
> > > +
> > > + type = le32toh(req->out->type);
> > > + switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) {
> > > + case VIRTIO_BLK_T_IN: {
> > > + ssize_t ret = 0;
> > > + bool is_write = type & VIRTIO_BLK_T_OUT;
> > > + req->sector_num = le64toh(req->out->sector);
> > > + if (is_write) {
> > > + ret = vub_writev(req, &elem->out_sg[1], out_num);
> > > + } else {
> > > + ret = vub_readv(req, &elem->in_sg[0], in_num);
> > > + }
> > > + if (ret >= 0) {
> > > + req->in->status = VIRTIO_BLK_S_OK;
> > > + } else {
> > > + req->in->status = VIRTIO_BLK_S_IOERR;
> > > + }
> > > + vub_req_complete(req);
> > > + break;
> > > + }
> > > + case VIRTIO_BLK_T_FLUSH: {
> > > + vub_flush(req);
> > > + req->in->status = VIRTIO_BLK_S_OK;
> > > + vub_req_complete(req);
> > > + break;
> > > + }
> > > + case VIRTIO_BLK_T_GET_ID: {
> > > + size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num),
> > > + VIRTIO_BLK_ID_BYTES);
> > > + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk");
> > > + req->in->status = VIRTIO_BLK_S_OK;
> > > + req->size = elem->in_sg[0].iov_len;
> > > + vub_req_complete(req);
> > > + break;
> > > + }
> > > + default: {
> > > + req->in->status = VIRTIO_BLK_S_UNSUPP;
> > > + vub_req_complete(req);
> > > + break;
> > > + }
> > > + }
> > > +
> > > + return 0;
> > > +
> > > +err:
> > > + free(elem);
> > > + g_free(req);
> > > + return -1;
> > > +}
> > > +
> > > +static void vub_process_vq(VuDev *vu_dev, int idx)
> > > +{
> > > + VugDev *gdev;
> > > + VubDev *vdev_blk;
> > > + VuVirtq *vq;
> > > + int ret;
> > > +
> > > + if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
> > > + fprintf(stderr, "VQ Index out of range: %d\n", idx);
> > > + vub_panic_cb(vu_dev, NULL);
> > > + return;
> > > + }
> > > +
> > > + gdev = container_of(vu_dev, VugDev, parent);
> > > + vdev_blk = container_of(gdev, VubDev, parent);
> > > + assert(vdev_blk);
> > > +
> > > + vq = vu_get_queue(vu_dev, idx);
> > > + assert(vq);
> > > +
> > > + while (1) {
> > > + ret = vub_virtio_process_req(vdev_blk, vq);
> > > + if (ret) {
> > > + break;
> > > + }
> > > + }
> > > +}
> > > +
> > > +static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
> > > +{
> > > + VuVirtq *vq;
> > > +
> > > + assert(vu_dev);
> > > +
> > > + vq = vu_get_queue(vu_dev, idx);
> > > + vu_set_queue_handler(vu_dev, vq, started ? vub_process_vq : NULL);
> > > +}
> > > +
> > > +static uint64_t
> > > +vub_get_features(VuDev *dev)
> > > +{
> > > + return 1ull << VIRTIO_BLK_F_SIZE_MAX |
> > > + 1ull << VIRTIO_BLK_F_SEG_MAX |
> > > + 1ull << VIRTIO_BLK_F_TOPOLOGY |
> > > + 1ull << VIRTIO_BLK_F_BLK_SIZE |
> > > + 1ull << VIRTIO_BLK_F_FLUSH |
> > > + 1ull << VIRTIO_BLK_F_CONFIG_WCE |
> > > + 1ull << VIRTIO_F_VERSION_1 |
> > > + 1ull << VHOST_USER_F_PROTOCOL_FEATURES;
> > > +}
> > > +
> > > +static int
> > > +vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
> > > +{
> > > + VugDev *gdev;
> > > + VubDev *vdev_blk;
> > > +
> > > + gdev = container_of(vu_dev, VugDev, parent);
> > > + vdev_blk = container_of(gdev, VubDev, parent);
> > > + memcpy(config, &vdev_blk->blkcfg, len);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static int
> > > +vub_set_config(VuDev *vu_dev, const uint8_t *data,
> > > + uint32_t offset, uint32_t size, uint32_t flags)
> > > +{
> > > + VugDev *gdev;
> > > + VubDev *vdev_blk;
> > > + uint8_t wce;
> > > + int fd;
> > > +
> > > + /* don't support live migration */
> > > + if (flags != VHOST_SET_CONFIG_TYPE_MASTER) {
> > > + return -1;
> > > + }
> > > +
> > > + gdev = container_of(vu_dev, VugDev, parent);
> > > + vdev_blk = container_of(gdev, VubDev, parent);
> > > +
> > > + if (offset != offsetof(struct virtio_blk_config, wce) ||
> > > + size != 1) {
> > > + return -1;
> > > + }
> > > +
> > > + wce = *data;
> > > + if (wce == vdev_blk->blkcfg.wce) {
> > > + /* Do nothing as same with old configuration */
> > > + return 0;
> > > + }
> > > +
> > > + vdev_blk->blkcfg.wce = wce;
> > > + fprintf(stdout, "Write Cache Policy Changed\n");
> > > + if (vdev_blk->blk_fd >= 0) {
> > > + close(vdev_blk->blk_fd);
> > > + vdev_blk->blk_fd = -1;
> > > + }
> > > +
> > > + fd = vub_open(vdev_blk->blk_name, wce);
> > > + if (fd < 0) {
> > > + fprintf(stderr, "Error to open block device %s\n", vdev_blk->blk_name);
> > > + vdev_blk->blk_fd = -1;
> > > + return -1;
> > > + }
> > > + vdev_blk->blk_fd = fd;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +static const VuDevIface vub_iface = {
> > > + .get_features = vub_get_features,
> > > + .queue_set_started = vub_queue_set_started,
> > > + .get_config = vub_get_config,
> > > + .set_config = vub_set_config,
> > > +};
> > > +
> > > +static int unix_sock_new(char *unix_fn)
> > > +{
> > > + int sock;
> > > + struct sockaddr_un un;
> > > + size_t len;
> > > +
> > > + assert(unix_fn);
> > > +
> > > + sock = socket(AF_UNIX, SOCK_STREAM, 0);
> > > + if (sock <= 0) {
> > > + perror("socket");
> > > + return -1;
> > > + }
> > > +
> > > + un.sun_family = AF_UNIX;
> > > + (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
> > > + len = sizeof(un.sun_family) + strlen(un.sun_path);
> > > +
> > > + (void)unlink(unix_fn);
> > > + if (bind(sock, (struct sockaddr *)&un, len) < 0) {
> > > + perror("bind");
> > > + goto fail;
> > > + }
> > > +
> > > + if (listen(sock, 1) < 0) {
> > > + perror("listen");
> > > + goto fail;
> > > + }
> > > +
> > > + return sock;
> > > +
> > > +fail:
> > > + (void)close(sock);
> > > +
> > > + return -1;
> > > +}
> > > +
> > > +static void vub_free(struct VubDev *vdev_blk)
> > > +{
> > > + if (!vdev_blk) {
> > > + return;
> > > + }
> > > +
> > > + g_main_loop_unref(vdev_blk->loop);
> > > + if (vdev_blk->blk_fd >= 0) {
> > > + close(vdev_blk->blk_fd);
> > > + }
> > > + g_free(vdev_blk);
> > > +}
> > > +
> > > +static uint32_t
> > > +vub_get_blocksize(int fd)
> > > +{
> > > + uint32_t blocksize = 512;
> > > +
> > > + #if defined(__linux__) && defined(BLKSSZGET)
> >
> > Weird indentation, please keep preprocessor lines at first column.
> Ok.
> >
> > > + if (ioctl(fd, BLKSSZGET, &blocksize) == 0) {
> > > + return blocklen;
> > > + }
> > > + #endif
> > > +
> > > + return blocksize;
> > > +}
> > > +
> > > +static void
> > > +vub_initialize_config(int fd, struct virtio_blk_config *config)
> > > +{
> > > + off64_t capacity;
> > > +
> > > + capacity = lseek64(fd, 0, SEEK_END);
> > > + config->capacity = capacity >> 9;
> > > + config->blk_size = vub_get_blocksize(fd);
> > > + config->size_max = 65536;
> > > + config->seg_max = 128 - 2;
> > > + config->min_io_size = 1;
> > > + config->opt_io_size = 1;
> > > + config->num_queues = 1;
> > > +}
> > > +
> > > +static VubDev *
> > > +vub_new(char *blk_file)
> > > +{
> > > + VubDev *vdev_blk;
> > > +
> > > + vdev_blk = g_new0(VubDev, 1);
> > > + vdev_blk->loop = g_main_loop_new(NULL, FALSE);
> > > + vdev_blk->blk_fd = vub_open(blk_file, 0);
> > > + if (vdev_blk->blk_fd < 0) {
> > > + fprintf(stderr, "Error to open block device %s\n", blk_file);
> > > + return NULL;
> >
> > You leak vdev_blk here.
> Will fix here.
> >
> > > + }
> > > + vdev_blk->blkcfg.wce = 0;
> > > + vdev_blk->blk_name = blk_file;
> > > +
> > > + /* fill virtio_blk_config with block parameters */
> > > + vub_initialize_config(vdev_blk->blk_fd, &vdev_blk->blkcfg);
> > > +
> > > + return vdev_blk;
> > > +}
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > + int opt;
> > > + char *unix_socket = NULL;
> > > + char *blk_file = NULL;
> > > + int lsock = -1, csock = -1;
> > > + VubDev *vdev_blk = NULL;
> > > +
> > > + while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
> > > + switch (opt) {
> > > + case 'b':
> > > + blk_file = g_strdup(optarg);
> > > + break;
> > > + case 's':
> > > + unix_socket = g_strdup(optarg);
> > > + break;
> > > + case 'h':
> > > + default:
> > > + printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
> > > + " | [ -h ]\n", argv[0]);
> > > + return 0;
> > > + }
> > > + }
> > > +
> > > + if (!unix_socket || !blk_file) {
> > > + printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
> > > + " [ -h ]\n", argv[0]);
> > > + return -1;
> > > + }
> > > +
> > > + lsock = unix_sock_new(unix_socket);
> > > + if (lsock < 0) {
> > > + goto err;
> > > + }
> > > +
> > > + csock = accept(lsock, (void *)0, (void *)0);
> > > + if (csock < 0) {
> > > + fprintf(stderr, "Accept error %s\n", strerror(errno));
> > > + goto err;
> > > + }
> > > +
> > > + vdev_blk = vub_new(blk_file);
> > > + if (!vdev_blk) {
> > > + goto err;
> > > + }
> > > +
> > > + vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);
> > > +
> > > + g_main_loop_run(vdev_blk->loop);
> > > +
> > > + vug_deinit(&vdev_blk->parent);
> > > +
> > > +err:
> > > + vub_free(vdev_blk);
> > > + if (csock >= 0) {
> > > + close(csock);
> > > + }
> > > + if (lsock >= 0) {
> > > + close(lsock);
> > > + }
> > > + g_free(unix_socket);
> > > + g_free(blk_file);
> > > +
> > > + return 0;
> > > +}
> > > --
> > > 1.9.3
> > >
> > >
> >
> > thanks
> >
> > --
> > Marc-André Lureau
^ permalink raw reply [flat|nested] 13+ messages in thread