mirror of
https://github.com/mii443/qemu.git
synced 2025-12-03 11:08:25 +00:00
vhost-user: Interface for migration state transfer
Add the interface for transferring the back-end's state during migration as defined previously in vhost-user.rst. Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Hanna Czenczek <hreitz@redhat.com> Message-Id: <20231016134243.68248-6-hreitz@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
committed by
Michael S. Tsirkin
parent
019233096c
commit
cda83adc62
@@ -103,6 +103,8 @@ typedef enum VhostUserRequest {
|
||||
VHOST_USER_SET_STATUS = 39,
|
||||
VHOST_USER_GET_STATUS = 40,
|
||||
VHOST_USER_GET_SHARED_OBJECT = 41,
|
||||
VHOST_USER_SET_DEVICE_STATE_FD = 42,
|
||||
VHOST_USER_CHECK_DEVICE_STATE = 43,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
@@ -201,6 +203,12 @@ typedef struct {
|
||||
uint32_t size; /* the following payload size */
|
||||
} QEMU_PACKED VhostUserHeader;
|
||||
|
||||
/* Request payload of VHOST_USER_SET_DEVICE_STATE_FD */
|
||||
typedef struct VhostUserTransferDeviceState {
|
||||
uint32_t direction;
|
||||
uint32_t phase;
|
||||
} VhostUserTransferDeviceState;
|
||||
|
||||
typedef union {
|
||||
#define VHOST_USER_VRING_IDX_MASK (0xff)
|
||||
#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
|
||||
@@ -216,6 +224,7 @@ typedef union {
|
||||
VhostUserVringArea area;
|
||||
VhostUserInflight inflight;
|
||||
VhostUserShared object;
|
||||
VhostUserTransferDeviceState transfer_state;
|
||||
} VhostUserPayload;
|
||||
|
||||
typedef struct VhostUserMsg {
|
||||
@@ -2855,6 +2864,140 @@ static void vhost_user_reset_status(struct vhost_dev *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static bool vhost_user_supports_device_state(struct vhost_dev *dev)
|
||||
{
|
||||
return virtio_has_feature(dev->protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_DEVICE_STATE);
|
||||
}
|
||||
|
||||
static int vhost_user_set_device_state_fd(struct vhost_dev *dev,
|
||||
VhostDeviceStateDirection direction,
|
||||
VhostDeviceStatePhase phase,
|
||||
int fd,
|
||||
int *reply_fd,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
struct vhost_user *vu = dev->opaque;
|
||||
VhostUserMsg msg = {
|
||||
.hdr = {
|
||||
.request = VHOST_USER_SET_DEVICE_STATE_FD,
|
||||
.flags = VHOST_USER_VERSION,
|
||||
.size = sizeof(msg.payload.transfer_state),
|
||||
},
|
||||
.payload.transfer_state = {
|
||||
.direction = direction,
|
||||
.phase = phase,
|
||||
},
|
||||
};
|
||||
|
||||
*reply_fd = -1;
|
||||
|
||||
if (!vhost_user_supports_device_state(dev)) {
|
||||
close(fd);
|
||||
error_setg(errp, "Back-end does not support migration state transfer");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = vhost_user_write(dev, &msg, &fd, 1);
|
||||
close(fd);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to send SET_DEVICE_STATE_FD message");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vhost_user_read(dev, &msg);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to receive SET_DEVICE_STATE_FD reply");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (msg.hdr.request != VHOST_USER_SET_DEVICE_STATE_FD) {
|
||||
error_setg(errp,
|
||||
"Received unexpected message type, expected %d, received %d",
|
||||
VHOST_USER_SET_DEVICE_STATE_FD, msg.hdr.request);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (msg.hdr.size != sizeof(msg.payload.u64)) {
|
||||
error_setg(errp,
|
||||
"Received bad message size, expected %zu, received %" PRIu32,
|
||||
sizeof(msg.payload.u64), msg.hdr.size);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if ((msg.payload.u64 & 0xff) != 0) {
|
||||
error_setg(errp, "Back-end did not accept migration state transfer");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
|
||||
*reply_fd = qemu_chr_fe_get_msgfd(vu->user->chr);
|
||||
if (*reply_fd < 0) {
|
||||
error_setg(errp,
|
||||
"Failed to get back-end-provided transfer pipe FD");
|
||||
*reply_fd = -1;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_user_check_device_state(struct vhost_dev *dev, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
VhostUserMsg msg = {
|
||||
.hdr = {
|
||||
.request = VHOST_USER_CHECK_DEVICE_STATE,
|
||||
.flags = VHOST_USER_VERSION,
|
||||
.size = 0,
|
||||
},
|
||||
};
|
||||
|
||||
if (!vhost_user_supports_device_state(dev)) {
|
||||
error_setg(errp, "Back-end does not support migration state transfer");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = vhost_user_write(dev, &msg, NULL, 0);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to send CHECK_DEVICE_STATE message");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vhost_user_read(dev, &msg);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Failed to receive CHECK_DEVICE_STATE reply");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (msg.hdr.request != VHOST_USER_CHECK_DEVICE_STATE) {
|
||||
error_setg(errp,
|
||||
"Received unexpected message type, expected %d, received %d",
|
||||
VHOST_USER_CHECK_DEVICE_STATE, msg.hdr.request);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (msg.hdr.size != sizeof(msg.payload.u64)) {
|
||||
error_setg(errp,
|
||||
"Received bad message size, expected %zu, received %" PRIu32,
|
||||
sizeof(msg.payload.u64), msg.hdr.size);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (msg.payload.u64 != 0) {
|
||||
error_setg(errp, "Back-end failed to process its internal state");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VhostOps user_ops = {
|
||||
.backend_type = VHOST_BACKEND_TYPE_USER,
|
||||
.vhost_backend_init = vhost_user_backend_init,
|
||||
@@ -2890,4 +3033,7 @@ const VhostOps user_ops = {
|
||||
.vhost_set_inflight_fd = vhost_user_set_inflight_fd,
|
||||
.vhost_dev_start = vhost_user_dev_start,
|
||||
.vhost_reset_status = vhost_user_reset_status,
|
||||
.vhost_supports_device_state = vhost_user_supports_device_state,
|
||||
.vhost_set_device_state_fd = vhost_user_set_device_state_fd,
|
||||
.vhost_check_device_state = vhost_user_check_device_state,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user