vhost-user: add new vhost user messages to support virtio config space
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> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
1bcf209154
commit
4c3e257b5e
@ -119,6 +119,19 @@ Depending on the request type, payload can be:
|
|||||||
- 3: IOTLB invalidate
|
- 3: IOTLB invalidate
|
||||||
- 4: IOTLB access fail
|
- 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:
|
In QEMU the vhost-user message is implemented with the following struct:
|
||||||
|
|
||||||
typedef struct VhostUserMsg {
|
typedef struct VhostUserMsg {
|
||||||
@ -132,6 +145,7 @@ typedef struct VhostUserMsg {
|
|||||||
VhostUserMemory memory;
|
VhostUserMemory memory;
|
||||||
VhostUserLog log;
|
VhostUserLog log;
|
||||||
struct vhost_iotlb_msg iotlb;
|
struct vhost_iotlb_msg iotlb;
|
||||||
|
VhostUserConfig config;
|
||||||
};
|
};
|
||||||
} QEMU_PACKED VhostUserMsg;
|
} QEMU_PACKED VhostUserMsg;
|
||||||
|
|
||||||
@ -623,6 +637,32 @@ Master message types
|
|||||||
and expect this message once (per VQ) during device configuration
|
and expect this message once (per VQ) during device configuration
|
||||||
(ie. before the master starts the VQ).
|
(ie. before the master starts the VQ).
|
||||||
|
|
||||||
|
* VHOST_USER_GET_CONFIG
|
||||||
|
|
||||||
|
Id: 24
|
||||||
|
Equivalent ioctl: N/A
|
||||||
|
Master payload: virtio device config space
|
||||||
|
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
|
||||||
|
Slave payload: N/A
|
||||||
|
|
||||||
|
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
|
Slave message types
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@ -641,6 +681,21 @@ Slave message types
|
|||||||
This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature
|
This request should be send only when VIRTIO_F_IOMMU_PLATFORM feature
|
||||||
has been successfully negotiated.
|
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:
|
VHOST_USER_PROTOCOL_F_REPLY_ACK:
|
||||||
-------------------------------
|
-------------------------------
|
||||||
The original vhost-user specification only demands replies for certain
|
The original vhost-user specification only demands replies for certain
|
||||||
|
@ -26,6 +26,11 @@
|
|||||||
#define VHOST_MEMORY_MAX_NREGIONS 8
|
#define VHOST_MEMORY_MAX_NREGIONS 8
|
||||||
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum size of virtio device config space
|
||||||
|
*/
|
||||||
|
#define VHOST_USER_MAX_CONFIG_SIZE 256
|
||||||
|
|
||||||
enum VhostUserProtocolFeature {
|
enum VhostUserProtocolFeature {
|
||||||
VHOST_USER_PROTOCOL_F_MQ = 0,
|
VHOST_USER_PROTOCOL_F_MQ = 0,
|
||||||
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
|
VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1,
|
||||||
@ -65,12 +70,15 @@ typedef enum VhostUserRequest {
|
|||||||
VHOST_USER_SET_SLAVE_REQ_FD = 21,
|
VHOST_USER_SET_SLAVE_REQ_FD = 21,
|
||||||
VHOST_USER_IOTLB_MSG = 22,
|
VHOST_USER_IOTLB_MSG = 22,
|
||||||
VHOST_USER_SET_VRING_ENDIAN = 23,
|
VHOST_USER_SET_VRING_ENDIAN = 23,
|
||||||
|
VHOST_USER_GET_CONFIG = 24,
|
||||||
|
VHOST_USER_SET_CONFIG = 25,
|
||||||
VHOST_USER_MAX
|
VHOST_USER_MAX
|
||||||
} VhostUserRequest;
|
} VhostUserRequest;
|
||||||
|
|
||||||
typedef enum VhostUserSlaveRequest {
|
typedef enum VhostUserSlaveRequest {
|
||||||
VHOST_USER_SLAVE_NONE = 0,
|
VHOST_USER_SLAVE_NONE = 0,
|
||||||
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
VHOST_USER_SLAVE_IOTLB_MSG = 1,
|
||||||
|
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
|
||||||
VHOST_USER_SLAVE_MAX
|
VHOST_USER_SLAVE_MAX
|
||||||
} VhostUserSlaveRequest;
|
} VhostUserSlaveRequest;
|
||||||
|
|
||||||
@ -92,6 +100,18 @@ typedef struct VhostUserLog {
|
|||||||
uint64_t mmap_offset;
|
uint64_t mmap_offset;
|
||||||
} VhostUserLog;
|
} 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 {
|
typedef struct VhostUserMsg {
|
||||||
VhostUserRequest request;
|
VhostUserRequest request;
|
||||||
|
|
||||||
@ -109,6 +129,7 @@ typedef struct VhostUserMsg {
|
|||||||
VhostUserMemory memory;
|
VhostUserMemory memory;
|
||||||
VhostUserLog log;
|
VhostUserLog log;
|
||||||
struct vhost_iotlb_msg iotlb;
|
struct vhost_iotlb_msg iotlb;
|
||||||
|
VhostUserConfig config;
|
||||||
} payload;
|
} payload;
|
||||||
} QEMU_PACKED VhostUserMsg;
|
} QEMU_PACKED VhostUserMsg;
|
||||||
|
|
||||||
@ -608,6 +629,21 @@ static int vhost_user_reset_device(struct vhost_dev *dev)
|
|||||||
return 0;
|
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)
|
static void slave_read(void *opaque)
|
||||||
{
|
{
|
||||||
struct vhost_dev *dev = opaque;
|
struct vhost_dev *dev = opaque;
|
||||||
@ -640,6 +676,9 @@ static void slave_read(void *opaque)
|
|||||||
case VHOST_USER_SLAVE_IOTLB_MSG:
|
case VHOST_USER_SLAVE_IOTLB_MSG:
|
||||||
ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb);
|
ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb);
|
||||||
break;
|
break;
|
||||||
|
case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG :
|
||||||
|
ret = vhost_user_slave_handle_config_change(dev);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error_report("Received unexpected msg type.");
|
error_report("Received unexpected msg type.");
|
||||||
ret = -EINVAL;
|
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. */
|
/* 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 = {
|
const VhostOps user_ops = {
|
||||||
.backend_type = VHOST_BACKEND_TYPE_USER,
|
.backend_type = VHOST_BACKEND_TYPE_USER,
|
||||||
.vhost_backend_init = vhost_user_init,
|
.vhost_backend_init = vhost_user_init,
|
||||||
@ -948,4 +1064,6 @@ const VhostOps user_ops = {
|
|||||||
.vhost_net_set_mtu = vhost_user_net_set_mtu,
|
.vhost_net_set_mtu = vhost_user_net_set_mtu,
|
||||||
.vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
|
.vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
|
||||||
.vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg,
|
.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,
|
||||||
};
|
};
|
||||||
|
@ -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. */
|
/* Host notifiers must be enabled at this point. */
|
||||||
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,11 @@ typedef enum VhostBackendType {
|
|||||||
VHOST_BACKEND_TYPE_MAX = 3,
|
VHOST_BACKEND_TYPE_MAX = 3,
|
||||||
} VhostBackendType;
|
} VhostBackendType;
|
||||||
|
|
||||||
|
typedef enum VhostSetConfigType {
|
||||||
|
VHOST_SET_CONFIG_TYPE_MASTER = 0,
|
||||||
|
VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
|
||||||
|
} VhostSetConfigType;
|
||||||
|
|
||||||
struct vhost_dev;
|
struct vhost_dev;
|
||||||
struct vhost_log;
|
struct vhost_log;
|
||||||
struct vhost_memory;
|
struct vhost_memory;
|
||||||
@ -84,6 +89,11 @@ typedef void (*vhost_set_iotlb_callback_op)(struct vhost_dev *dev,
|
|||||||
int enabled);
|
int enabled);
|
||||||
typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev,
|
typedef int (*vhost_send_device_iotlb_msg_op)(struct vhost_dev *dev,
|
||||||
struct vhost_iotlb_msg *imsg);
|
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 {
|
typedef struct VhostOps {
|
||||||
VhostBackendType backend_type;
|
VhostBackendType backend_type;
|
||||||
@ -118,6 +128,8 @@ typedef struct VhostOps {
|
|||||||
vhost_vsock_set_running_op vhost_vsock_set_running;
|
vhost_vsock_set_running_op vhost_vsock_set_running;
|
||||||
vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
|
vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
|
||||||
vhost_send_device_iotlb_msg_op vhost_send_device_iotlb_msg;
|
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;
|
} VhostOps;
|
||||||
|
|
||||||
extern const VhostOps user_ops;
|
extern const VhostOps user_ops;
|
||||||
|
@ -46,6 +46,12 @@ struct vhost_iommu {
|
|||||||
QLIST_ENTRY(vhost_iommu) iommu_next;
|
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_memory;
|
||||||
struct vhost_dev {
|
struct vhost_dev {
|
||||||
VirtIODevice *vdev;
|
VirtIODevice *vdev;
|
||||||
@ -76,6 +82,7 @@ struct vhost_dev {
|
|||||||
QLIST_ENTRY(vhost_dev) entry;
|
QLIST_ENTRY(vhost_dev) entry;
|
||||||
QLIST_HEAD(, vhost_iommu) iommu_list;
|
QLIST_HEAD(, vhost_iommu) iommu_list;
|
||||||
IOMMUNotifier n;
|
IOMMUNotifier n;
|
||||||
|
const VhostDevConfigOps *config_ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
|
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);
|
struct vhost_vring_file *file);
|
||||||
|
|
||||||
int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
|
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
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user