Skip to content

Commit cda83ad

Browse files
XanClicmstsirkin
authored andcommitted
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 <[email protected]> Signed-off-by: Hanna Czenczek <[email protected]> Message-Id: <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent 0192330 commit cda83ad

File tree

5 files changed

+286
-0
lines changed

5 files changed

+286
-0
lines changed

hw/virtio/vhost-user.c

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ typedef enum VhostUserRequest {
103103
VHOST_USER_SET_STATUS = 39,
104104
VHOST_USER_GET_STATUS = 40,
105105
VHOST_USER_GET_SHARED_OBJECT = 41,
106+
VHOST_USER_SET_DEVICE_STATE_FD = 42,
107+
VHOST_USER_CHECK_DEVICE_STATE = 43,
106108
VHOST_USER_MAX
107109
} VhostUserRequest;
108110

@@ -201,6 +203,12 @@ typedef struct {
201203
uint32_t size; /* the following payload size */
202204
} QEMU_PACKED VhostUserHeader;
203205

206+
/* Request payload of VHOST_USER_SET_DEVICE_STATE_FD */
207+
typedef struct VhostUserTransferDeviceState {
208+
uint32_t direction;
209+
uint32_t phase;
210+
} VhostUserTransferDeviceState;
211+
204212
typedef union {
205213
#define VHOST_USER_VRING_IDX_MASK (0xff)
206214
#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
@@ -216,6 +224,7 @@ typedef union {
216224
VhostUserVringArea area;
217225
VhostUserInflight inflight;
218226
VhostUserShared object;
227+
VhostUserTransferDeviceState transfer_state;
219228
} VhostUserPayload;
220229

221230
typedef struct VhostUserMsg {
@@ -2855,6 +2864,140 @@ static void vhost_user_reset_status(struct vhost_dev *dev)
28552864
}
28562865
}
28572866

2867+
static bool vhost_user_supports_device_state(struct vhost_dev *dev)
2868+
{
2869+
return virtio_has_feature(dev->protocol_features,
2870+
VHOST_USER_PROTOCOL_F_DEVICE_STATE);
2871+
}
2872+
2873+
static int vhost_user_set_device_state_fd(struct vhost_dev *dev,
2874+
VhostDeviceStateDirection direction,
2875+
VhostDeviceStatePhase phase,
2876+
int fd,
2877+
int *reply_fd,
2878+
Error **errp)
2879+
{
2880+
int ret;
2881+
struct vhost_user *vu = dev->opaque;
2882+
VhostUserMsg msg = {
2883+
.hdr = {
2884+
.request = VHOST_USER_SET_DEVICE_STATE_FD,
2885+
.flags = VHOST_USER_VERSION,
2886+
.size = sizeof(msg.payload.transfer_state),
2887+
},
2888+
.payload.transfer_state = {
2889+
.direction = direction,
2890+
.phase = phase,
2891+
},
2892+
};
2893+
2894+
*reply_fd = -1;
2895+
2896+
if (!vhost_user_supports_device_state(dev)) {
2897+
close(fd);
2898+
error_setg(errp, "Back-end does not support migration state transfer");
2899+
return -ENOTSUP;
2900+
}
2901+
2902+
ret = vhost_user_write(dev, &msg, &fd, 1);
2903+
close(fd);
2904+
if (ret < 0) {
2905+
error_setg_errno(errp, -ret,
2906+
"Failed to send SET_DEVICE_STATE_FD message");
2907+
return ret;
2908+
}
2909+
2910+
ret = vhost_user_read(dev, &msg);
2911+
if (ret < 0) {
2912+
error_setg_errno(errp, -ret,
2913+
"Failed to receive SET_DEVICE_STATE_FD reply");
2914+
return ret;
2915+
}
2916+
2917+
if (msg.hdr.request != VHOST_USER_SET_DEVICE_STATE_FD) {
2918+
error_setg(errp,
2919+
"Received unexpected message type, expected %d, received %d",
2920+
VHOST_USER_SET_DEVICE_STATE_FD, msg.hdr.request);
2921+
return -EPROTO;
2922+
}
2923+
2924+
if (msg.hdr.size != sizeof(msg.payload.u64)) {
2925+
error_setg(errp,
2926+
"Received bad message size, expected %zu, received %" PRIu32,
2927+
sizeof(msg.payload.u64), msg.hdr.size);
2928+
return -EPROTO;
2929+
}
2930+
2931+
if ((msg.payload.u64 & 0xff) != 0) {
2932+
error_setg(errp, "Back-end did not accept migration state transfer");
2933+
return -EIO;
2934+
}
2935+
2936+
if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
2937+
*reply_fd = qemu_chr_fe_get_msgfd(vu->user->chr);
2938+
if (*reply_fd < 0) {
2939+
error_setg(errp,
2940+
"Failed to get back-end-provided transfer pipe FD");
2941+
*reply_fd = -1;
2942+
return -EIO;
2943+
}
2944+
}
2945+
2946+
return 0;
2947+
}
2948+
2949+
static int vhost_user_check_device_state(struct vhost_dev *dev, Error **errp)
2950+
{
2951+
int ret;
2952+
VhostUserMsg msg = {
2953+
.hdr = {
2954+
.request = VHOST_USER_CHECK_DEVICE_STATE,
2955+
.flags = VHOST_USER_VERSION,
2956+
.size = 0,
2957+
},
2958+
};
2959+
2960+
if (!vhost_user_supports_device_state(dev)) {
2961+
error_setg(errp, "Back-end does not support migration state transfer");
2962+
return -ENOTSUP;
2963+
}
2964+
2965+
ret = vhost_user_write(dev, &msg, NULL, 0);
2966+
if (ret < 0) {
2967+
error_setg_errno(errp, -ret,
2968+
"Failed to send CHECK_DEVICE_STATE message");
2969+
return ret;
2970+
}
2971+
2972+
ret = vhost_user_read(dev, &msg);
2973+
if (ret < 0) {
2974+
error_setg_errno(errp, -ret,
2975+
"Failed to receive CHECK_DEVICE_STATE reply");
2976+
return ret;
2977+
}
2978+
2979+
if (msg.hdr.request != VHOST_USER_CHECK_DEVICE_STATE) {
2980+
error_setg(errp,
2981+
"Received unexpected message type, expected %d, received %d",
2982+
VHOST_USER_CHECK_DEVICE_STATE, msg.hdr.request);
2983+
return -EPROTO;
2984+
}
2985+
2986+
if (msg.hdr.size != sizeof(msg.payload.u64)) {
2987+
error_setg(errp,
2988+
"Received bad message size, expected %zu, received %" PRIu32,
2989+
sizeof(msg.payload.u64), msg.hdr.size);
2990+
return -EPROTO;
2991+
}
2992+
2993+
if (msg.payload.u64 != 0) {
2994+
error_setg(errp, "Back-end failed to process its internal state");
2995+
return -EIO;
2996+
}
2997+
2998+
return 0;
2999+
}
3000+
28583001
const VhostOps user_ops = {
28593002
.backend_type = VHOST_BACKEND_TYPE_USER,
28603003
.vhost_backend_init = vhost_user_backend_init,
@@ -2890,4 +3033,7 @@ const VhostOps user_ops = {
28903033
.vhost_set_inflight_fd = vhost_user_set_inflight_fd,
28913034
.vhost_dev_start = vhost_user_dev_start,
28923035
.vhost_reset_status = vhost_user_reset_status,
3036+
.vhost_supports_device_state = vhost_user_supports_device_state,
3037+
.vhost_set_device_state_fd = vhost_user_set_device_state_fd,
3038+
.vhost_check_device_state = vhost_user_check_device_state,
28933039
};

hw/virtio/vhost.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,3 +2159,40 @@ int vhost_reset_device(struct vhost_dev *hdev)
21592159

21602160
return -ENOSYS;
21612161
}
2162+
2163+
bool vhost_supports_device_state(struct vhost_dev *dev)
2164+
{
2165+
if (dev->vhost_ops->vhost_supports_device_state) {
2166+
return dev->vhost_ops->vhost_supports_device_state(dev);
2167+
}
2168+
2169+
return false;
2170+
}
2171+
2172+
int vhost_set_device_state_fd(struct vhost_dev *dev,
2173+
VhostDeviceStateDirection direction,
2174+
VhostDeviceStatePhase phase,
2175+
int fd,
2176+
int *reply_fd,
2177+
Error **errp)
2178+
{
2179+
if (dev->vhost_ops->vhost_set_device_state_fd) {
2180+
return dev->vhost_ops->vhost_set_device_state_fd(dev, direction, phase,
2181+
fd, reply_fd, errp);
2182+
}
2183+
2184+
error_setg(errp,
2185+
"vhost transport does not support migration state transfer");
2186+
return -ENOSYS;
2187+
}
2188+
2189+
int vhost_check_device_state(struct vhost_dev *dev, Error **errp)
2190+
{
2191+
if (dev->vhost_ops->vhost_check_device_state) {
2192+
return dev->vhost_ops->vhost_check_device_state(dev, errp);
2193+
}
2194+
2195+
error_setg(errp,
2196+
"vhost transport does not support migration state transfer");
2197+
return -ENOSYS;
2198+
}

include/hw/virtio/vhost-backend.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ typedef enum VhostSetConfigType {
2626
VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
2727
} VhostSetConfigType;
2828

29+
typedef enum VhostDeviceStateDirection {
30+
/* Transfer state from back-end (device) to front-end */
31+
VHOST_TRANSFER_STATE_DIRECTION_SAVE = 0,
32+
/* Transfer state from front-end to back-end (device) */
33+
VHOST_TRANSFER_STATE_DIRECTION_LOAD = 1,
34+
} VhostDeviceStateDirection;
35+
36+
typedef enum VhostDeviceStatePhase {
37+
/* The device (and all its vrings) is stopped */
38+
VHOST_TRANSFER_STATE_PHASE_STOPPED = 0,
39+
} VhostDeviceStatePhase;
40+
2941
struct vhost_inflight;
3042
struct vhost_dev;
3143
struct vhost_log;
@@ -129,6 +141,15 @@ typedef int (*vhost_set_config_call_op)(struct vhost_dev *dev,
129141

130142
typedef void (*vhost_reset_status_op)(struct vhost_dev *dev);
131143

144+
typedef bool (*vhost_supports_device_state_op)(struct vhost_dev *dev);
145+
typedef int (*vhost_set_device_state_fd_op)(struct vhost_dev *dev,
146+
VhostDeviceStateDirection direction,
147+
VhostDeviceStatePhase phase,
148+
int fd,
149+
int *reply_fd,
150+
Error **errp);
151+
typedef int (*vhost_check_device_state_op)(struct vhost_dev *dev, Error **errp);
152+
132153
typedef struct VhostOps {
133154
VhostBackendType backend_type;
134155
vhost_backend_init vhost_backend_init;
@@ -176,6 +197,9 @@ typedef struct VhostOps {
176197
vhost_force_iommu_op vhost_force_iommu;
177198
vhost_set_config_call_op vhost_set_config_call;
178199
vhost_reset_status_op vhost_reset_status;
200+
vhost_supports_device_state_op vhost_supports_device_state;
201+
vhost_set_device_state_fd_op vhost_set_device_state_fd;
202+
vhost_check_device_state_op vhost_check_device_state;
179203
} VhostOps;
180204

181205
int vhost_backend_update_device_iotlb(struct vhost_dev *dev,

include/hw/virtio/vhost-user.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum VhostUserProtocolFeature {
3131
VHOST_USER_PROTOCOL_F_STATUS = 16,
3232
/* Feature 17 reserved for VHOST_USER_PROTOCOL_F_XEN_MMAP. */
3333
VHOST_USER_PROTOCOL_F_SHARED_OBJECT = 18,
34+
VHOST_USER_PROTOCOL_F_DEVICE_STATE = 19,
3435
VHOST_USER_PROTOCOL_F_MAX
3536
};
3637

include/hw/virtio/vhost.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,4 +351,82 @@ static inline int vhost_reset_device(struct vhost_dev *hdev)
351351
}
352352
#endif /* CONFIG_VHOST */
353353

354+
/**
355+
* vhost_supports_device_state(): Checks whether the back-end supports
356+
* transferring internal device state for the purpose of migration.
357+
* Support for this feature is required for vhost_set_device_state_fd()
358+
* and vhost_check_device_state().
359+
*
360+
* @dev: The vhost device
361+
*
362+
* Returns true if the device supports these commands, and false if it
363+
* does not.
364+
*/
365+
bool vhost_supports_device_state(struct vhost_dev *dev);
366+
367+
/**
368+
* vhost_set_device_state_fd(): Begin transfer of internal state from/to
369+
* the back-end for the purpose of migration. Data is to be transferred
370+
* over a pipe according to @direction and @phase. The sending end must
371+
* only write to the pipe, and the receiving end must only read from it.
372+
* Once the sending end is done, it closes its FD. The receiving end
373+
* must take this as the end-of-transfer signal and close its FD, too.
374+
*
375+
* @fd is the back-end's end of the pipe: The write FD for SAVE, and the
376+
* read FD for LOAD. This function transfers ownership of @fd to the
377+
* back-end, i.e. closes it in the front-end.
378+
*
379+
* The back-end may optionally reply with an FD of its own, if this
380+
* improves efficiency on its end. In this case, the returned FD is
381+
* stored in *reply_fd. The back-end will discard the FD sent to it,
382+
* and the front-end must use *reply_fd for transferring state to/from
383+
* the back-end.
384+
*
385+
* @dev: The vhost device
386+
* @direction: The direction in which the state is to be transferred.
387+
* For outgoing migrations, this is SAVE, and data is read
388+
* from the back-end and stored by the front-end in the
389+
* migration stream.
390+
* For incoming migrations, this is LOAD, and data is read
391+
* by the front-end from the migration stream and sent to
392+
* the back-end to restore the saved state.
393+
* @phase: Which migration phase we are in. Currently, there is only
394+
* STOPPED (device and all vrings are stopped), in the future,
395+
* more phases such as PRE_COPY or POST_COPY may be added.
396+
* @fd: Back-end's end of the pipe through which to transfer state; note
397+
* that ownership is transferred to the back-end, so this function
398+
* closes @fd in the front-end.
399+
* @reply_fd: If the back-end wishes to use a different pipe for state
400+
* transfer, this will contain an FD for the front-end to
401+
* use. Otherwise, -1 is stored here.
402+
* @errp: Potential error description
403+
*
404+
* Returns 0 on success, and -errno on failure.
405+
*/
406+
int vhost_set_device_state_fd(struct vhost_dev *dev,
407+
VhostDeviceStateDirection direction,
408+
VhostDeviceStatePhase phase,
409+
int fd,
410+
int *reply_fd,
411+
Error **errp);
412+
413+
/**
414+
* vhost_set_device_state_fd(): After transferring state from/to the
415+
* back-end via vhost_set_device_state_fd(), i.e. once the sending end
416+
* has closed the pipe, inquire the back-end to report any potential
417+
* errors that have occurred on its side. This allows to sense errors
418+
* like:
419+
* - During outgoing migration, when the source side had already started
420+
* to produce its state, something went wrong and it failed to finish
421+
* - During incoming migration, when the received state is somehow
422+
* invalid and cannot be processed by the back-end
423+
*
424+
* @dev: The vhost device
425+
* @errp: Potential error description
426+
*
427+
* Returns 0 when the back-end reports successful state transfer and
428+
* processing, and -errno when an error occurred somewhere.
429+
*/
430+
int vhost_check_device_state(struct vhost_dev *dev, Error **errp);
431+
354432
#endif

0 commit comments

Comments
 (0)