196116512SMaciej S. Szmigiero /*
296116512SMaciej S. Szmigiero * Multifd VFIO migration
396116512SMaciej S. Szmigiero *
496116512SMaciej S. Szmigiero * Copyright (C) 2024,2025 Oracle and/or its affiliates.
596116512SMaciej S. Szmigiero *
696116512SMaciej S. Szmigiero * This work is licensed under the terms of the GNU GPL, version 2 or later.
796116512SMaciej S. Szmigiero * See the COPYING file in the top-level directory.
896116512SMaciej S. Szmigiero *
996116512SMaciej S. Szmigiero * SPDX-License-Identifier: GPL-2.0-or-later
1096116512SMaciej S. Szmigiero */
1196116512SMaciej S. Szmigiero
1296116512SMaciej S. Szmigiero #include "qemu/osdep.h"
1396116512SMaciej S. Szmigiero #include "hw/vfio/vfio-common.h"
1496116512SMaciej S. Szmigiero #include "migration/misc.h"
1596116512SMaciej S. Szmigiero #include "qapi/error.h"
16*cbb2e105SMaciej S. Szmigiero #include "qemu/bswap.h"
1796116512SMaciej S. Szmigiero #include "qemu/error-report.h"
1896116512SMaciej S. Szmigiero #include "qemu/lockable.h"
1996116512SMaciej S. Szmigiero #include "qemu/main-loop.h"
2096116512SMaciej S. Szmigiero #include "qemu/thread.h"
21b659c07cSMaciej S. Szmigiero #include "io/channel-buffer.h"
2296116512SMaciej S. Szmigiero #include "migration/qemu-file.h"
2396116512SMaciej S. Szmigiero #include "migration-multifd.h"
2496116512SMaciej S. Szmigiero #include "trace.h"
2596116512SMaciej S. Szmigiero
2696116512SMaciej S. Szmigiero #define VFIO_DEVICE_STATE_CONFIG_STATE (1)
2796116512SMaciej S. Szmigiero
2896116512SMaciej S. Szmigiero #define VFIO_DEVICE_STATE_PACKET_VER_CURRENT (0)
2996116512SMaciej S. Szmigiero
3096116512SMaciej S. Szmigiero typedef struct VFIODeviceStatePacket {
3196116512SMaciej S. Szmigiero uint32_t version;
3296116512SMaciej S. Szmigiero uint32_t idx;
3396116512SMaciej S. Szmigiero uint32_t flags;
3496116512SMaciej S. Szmigiero uint8_t data[0];
3596116512SMaciej S. Szmigiero } QEMU_PACKED VFIODeviceStatePacket;
362efa35d3SMaciej S. Szmigiero
373228d311SMaciej S. Szmigiero /* type safety */
383228d311SMaciej S. Szmigiero typedef struct VFIOStateBuffers {
393228d311SMaciej S. Szmigiero GArray *array;
403228d311SMaciej S. Szmigiero } VFIOStateBuffers;
413228d311SMaciej S. Szmigiero
423228d311SMaciej S. Szmigiero typedef struct VFIOStateBuffer {
433228d311SMaciej S. Szmigiero bool is_present;
443228d311SMaciej S. Szmigiero char *data;
453228d311SMaciej S. Szmigiero size_t len;
463228d311SMaciej S. Szmigiero } VFIOStateBuffer;
473228d311SMaciej S. Szmigiero
48ff2fd1f7SMaciej S. Szmigiero typedef struct VFIOMultifd {
49c59748c1SMaciej S. Szmigiero bool load_bufs_thread_running;
50c59748c1SMaciej S. Szmigiero bool load_bufs_thread_want_exit;
51c59748c1SMaciej S. Szmigiero
523228d311SMaciej S. Szmigiero VFIOStateBuffers load_bufs;
533228d311SMaciej S. Szmigiero QemuCond load_bufs_buffer_ready_cond;
54c59748c1SMaciej S. Szmigiero QemuCond load_bufs_thread_finished_cond;
553228d311SMaciej S. Szmigiero QemuMutex load_bufs_mutex; /* Lock order: this lock -> BQL */
563228d311SMaciej S. Szmigiero uint32_t load_buf_idx;
573228d311SMaciej S. Szmigiero uint32_t load_buf_idx_last;
58ff2fd1f7SMaciej S. Szmigiero } VFIOMultifd;
59ff2fd1f7SMaciej S. Szmigiero
vfio_state_buffer_clear(gpointer data)603228d311SMaciej S. Szmigiero static void vfio_state_buffer_clear(gpointer data)
613228d311SMaciej S. Szmigiero {
623228d311SMaciej S. Szmigiero VFIOStateBuffer *lb = data;
633228d311SMaciej S. Szmigiero
643228d311SMaciej S. Szmigiero if (!lb->is_present) {
653228d311SMaciej S. Szmigiero return;
663228d311SMaciej S. Szmigiero }
673228d311SMaciej S. Szmigiero
683228d311SMaciej S. Szmigiero g_clear_pointer(&lb->data, g_free);
693228d311SMaciej S. Szmigiero lb->is_present = false;
703228d311SMaciej S. Szmigiero }
713228d311SMaciej S. Szmigiero
vfio_state_buffers_init(VFIOStateBuffers * bufs)723228d311SMaciej S. Szmigiero static void vfio_state_buffers_init(VFIOStateBuffers *bufs)
733228d311SMaciej S. Szmigiero {
743228d311SMaciej S. Szmigiero bufs->array = g_array_new(FALSE, TRUE, sizeof(VFIOStateBuffer));
753228d311SMaciej S. Szmigiero g_array_set_clear_func(bufs->array, vfio_state_buffer_clear);
763228d311SMaciej S. Szmigiero }
773228d311SMaciej S. Szmigiero
vfio_state_buffers_destroy(VFIOStateBuffers * bufs)783228d311SMaciej S. Szmigiero static void vfio_state_buffers_destroy(VFIOStateBuffers *bufs)
793228d311SMaciej S. Szmigiero {
803228d311SMaciej S. Szmigiero g_clear_pointer(&bufs->array, g_array_unref);
813228d311SMaciej S. Szmigiero }
823228d311SMaciej S. Szmigiero
vfio_state_buffers_assert_init(VFIOStateBuffers * bufs)833228d311SMaciej S. Szmigiero static void vfio_state_buffers_assert_init(VFIOStateBuffers *bufs)
843228d311SMaciej S. Szmigiero {
853228d311SMaciej S. Szmigiero assert(bufs->array);
863228d311SMaciej S. Szmigiero }
873228d311SMaciej S. Szmigiero
vfio_state_buffers_size_get(VFIOStateBuffers * bufs)883228d311SMaciej S. Szmigiero static unsigned int vfio_state_buffers_size_get(VFIOStateBuffers *bufs)
893228d311SMaciej S. Szmigiero {
903228d311SMaciej S. Szmigiero return bufs->array->len;
913228d311SMaciej S. Szmigiero }
923228d311SMaciej S. Szmigiero
vfio_state_buffers_size_set(VFIOStateBuffers * bufs,unsigned int size)933228d311SMaciej S. Szmigiero static void vfio_state_buffers_size_set(VFIOStateBuffers *bufs,
943228d311SMaciej S. Szmigiero unsigned int size)
953228d311SMaciej S. Szmigiero {
963228d311SMaciej S. Szmigiero g_array_set_size(bufs->array, size);
973228d311SMaciej S. Szmigiero }
983228d311SMaciej S. Szmigiero
vfio_state_buffers_at(VFIOStateBuffers * bufs,unsigned int idx)993228d311SMaciej S. Szmigiero static VFIOStateBuffer *vfio_state_buffers_at(VFIOStateBuffers *bufs,
1003228d311SMaciej S. Szmigiero unsigned int idx)
1013228d311SMaciej S. Szmigiero {
1023228d311SMaciej S. Szmigiero return &g_array_index(bufs->array, VFIOStateBuffer, idx);
1033228d311SMaciej S. Szmigiero }
1043228d311SMaciej S. Szmigiero
1053228d311SMaciej S. Szmigiero /* called with load_bufs_mutex locked */
vfio_load_state_buffer_insert(VFIODevice * vbasedev,VFIODeviceStatePacket * packet,size_t packet_total_size,Error ** errp)1063228d311SMaciej S. Szmigiero static bool vfio_load_state_buffer_insert(VFIODevice *vbasedev,
1073228d311SMaciej S. Szmigiero VFIODeviceStatePacket *packet,
1083228d311SMaciej S. Szmigiero size_t packet_total_size,
1093228d311SMaciej S. Szmigiero Error **errp)
1103228d311SMaciej S. Szmigiero {
1113228d311SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
1123228d311SMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
1133228d311SMaciej S. Szmigiero VFIOStateBuffer *lb;
1143228d311SMaciej S. Szmigiero
1153228d311SMaciej S. Szmigiero vfio_state_buffers_assert_init(&multifd->load_bufs);
1163228d311SMaciej S. Szmigiero if (packet->idx >= vfio_state_buffers_size_get(&multifd->load_bufs)) {
1173228d311SMaciej S. Szmigiero vfio_state_buffers_size_set(&multifd->load_bufs, packet->idx + 1);
1183228d311SMaciej S. Szmigiero }
1193228d311SMaciej S. Szmigiero
1203228d311SMaciej S. Szmigiero lb = vfio_state_buffers_at(&multifd->load_bufs, packet->idx);
1213228d311SMaciej S. Szmigiero if (lb->is_present) {
1223228d311SMaciej S. Szmigiero error_setg(errp, "%s: state buffer %" PRIu32 " already filled",
1233228d311SMaciej S. Szmigiero vbasedev->name, packet->idx);
1243228d311SMaciej S. Szmigiero return false;
1253228d311SMaciej S. Szmigiero }
1263228d311SMaciej S. Szmigiero
1273228d311SMaciej S. Szmigiero assert(packet->idx >= multifd->load_buf_idx);
1283228d311SMaciej S. Szmigiero
1293228d311SMaciej S. Szmigiero lb->data = g_memdup2(&packet->data, packet_total_size - sizeof(*packet));
1303228d311SMaciej S. Szmigiero lb->len = packet_total_size - sizeof(*packet);
1313228d311SMaciej S. Szmigiero lb->is_present = true;
1323228d311SMaciej S. Szmigiero
1333228d311SMaciej S. Szmigiero return true;
1343228d311SMaciej S. Szmigiero }
1353228d311SMaciej S. Szmigiero
vfio_multifd_load_state_buffer(void * opaque,char * data,size_t data_size,Error ** errp)1363228d311SMaciej S. Szmigiero bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size,
1373228d311SMaciej S. Szmigiero Error **errp)
1383228d311SMaciej S. Szmigiero {
1393228d311SMaciej S. Szmigiero VFIODevice *vbasedev = opaque;
1403228d311SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
1413228d311SMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
1423228d311SMaciej S. Szmigiero VFIODeviceStatePacket *packet = (VFIODeviceStatePacket *)data;
1433228d311SMaciej S. Szmigiero
1443228d311SMaciej S. Szmigiero if (!vfio_multifd_transfer_enabled(vbasedev)) {
1453228d311SMaciej S. Szmigiero error_setg(errp,
1463228d311SMaciej S. Szmigiero "%s: got device state packet but not doing multifd transfer",
1473228d311SMaciej S. Szmigiero vbasedev->name);
1483228d311SMaciej S. Szmigiero return false;
1493228d311SMaciej S. Szmigiero }
1503228d311SMaciej S. Szmigiero
1513228d311SMaciej S. Szmigiero assert(multifd);
1523228d311SMaciej S. Szmigiero
1533228d311SMaciej S. Szmigiero if (data_size < sizeof(*packet)) {
1543228d311SMaciej S. Szmigiero error_setg(errp, "%s: packet too short at %zu (min is %zu)",
1553228d311SMaciej S. Szmigiero vbasedev->name, data_size, sizeof(*packet));
1563228d311SMaciej S. Szmigiero return false;
1573228d311SMaciej S. Szmigiero }
1583228d311SMaciej S. Szmigiero
159*cbb2e105SMaciej S. Szmigiero packet->version = be32_to_cpu(packet->version);
1603228d311SMaciej S. Szmigiero if (packet->version != VFIO_DEVICE_STATE_PACKET_VER_CURRENT) {
1613228d311SMaciej S. Szmigiero error_setg(errp, "%s: packet has unknown version %" PRIu32,
1623228d311SMaciej S. Szmigiero vbasedev->name, packet->version);
1633228d311SMaciej S. Szmigiero return false;
1643228d311SMaciej S. Szmigiero }
1653228d311SMaciej S. Szmigiero
166*cbb2e105SMaciej S. Szmigiero packet->idx = be32_to_cpu(packet->idx);
167*cbb2e105SMaciej S. Szmigiero packet->flags = be32_to_cpu(packet->flags);
168*cbb2e105SMaciej S. Szmigiero
1693228d311SMaciej S. Szmigiero if (packet->idx == UINT32_MAX) {
1703228d311SMaciej S. Szmigiero error_setg(errp, "%s: packet index is invalid", vbasedev->name);
1713228d311SMaciej S. Szmigiero return false;
1723228d311SMaciej S. Szmigiero }
1733228d311SMaciej S. Szmigiero
1743228d311SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_incoming(vbasedev->name, packet->idx);
1753228d311SMaciej S. Szmigiero
1763228d311SMaciej S. Szmigiero /*
1773228d311SMaciej S. Szmigiero * Holding BQL here would violate the lock order and can cause
1783228d311SMaciej S. Szmigiero * a deadlock once we attempt to lock load_bufs_mutex below.
1793228d311SMaciej S. Szmigiero */
1803228d311SMaciej S. Szmigiero assert(!bql_locked());
1813228d311SMaciej S. Szmigiero
1823228d311SMaciej S. Szmigiero WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) {
1833228d311SMaciej S. Szmigiero /* config state packet should be the last one in the stream */
1843228d311SMaciej S. Szmigiero if (packet->flags & VFIO_DEVICE_STATE_CONFIG_STATE) {
1853228d311SMaciej S. Szmigiero multifd->load_buf_idx_last = packet->idx;
1863228d311SMaciej S. Szmigiero }
1873228d311SMaciej S. Szmigiero
1883228d311SMaciej S. Szmigiero if (!vfio_load_state_buffer_insert(vbasedev, packet, data_size,
1893228d311SMaciej S. Szmigiero errp)) {
1903228d311SMaciej S. Szmigiero return false;
1913228d311SMaciej S. Szmigiero }
1923228d311SMaciej S. Szmigiero
1933228d311SMaciej S. Szmigiero qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond);
1943228d311SMaciej S. Szmigiero }
1953228d311SMaciej S. Szmigiero
1963228d311SMaciej S. Szmigiero return true;
1973228d311SMaciej S. Szmigiero }
1983228d311SMaciej S. Szmigiero
vfio_load_bufs_thread_load_config(VFIODevice * vbasedev,Error ** errp)199c59748c1SMaciej S. Szmigiero static bool vfio_load_bufs_thread_load_config(VFIODevice *vbasedev,
200c59748c1SMaciej S. Szmigiero Error **errp)
201c59748c1SMaciej S. Szmigiero {
202b659c07cSMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
203b659c07cSMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
204b659c07cSMaciej S. Szmigiero VFIOStateBuffer *lb;
205b659c07cSMaciej S. Szmigiero g_autoptr(QIOChannelBuffer) bioc = NULL;
206b659c07cSMaciej S. Szmigiero g_autoptr(QEMUFile) f_out = NULL, f_in = NULL;
207b659c07cSMaciej S. Szmigiero uint64_t mig_header;
208b659c07cSMaciej S. Szmigiero int ret;
209b659c07cSMaciej S. Szmigiero
210b659c07cSMaciej S. Szmigiero assert(multifd->load_buf_idx == multifd->load_buf_idx_last);
211b659c07cSMaciej S. Szmigiero lb = vfio_state_buffers_at(&multifd->load_bufs, multifd->load_buf_idx);
212b659c07cSMaciej S. Szmigiero assert(lb->is_present);
213b659c07cSMaciej S. Szmigiero
214b659c07cSMaciej S. Szmigiero bioc = qio_channel_buffer_new(lb->len);
215b659c07cSMaciej S. Szmigiero qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-load");
216b659c07cSMaciej S. Szmigiero
217b659c07cSMaciej S. Szmigiero f_out = qemu_file_new_output(QIO_CHANNEL(bioc));
218b659c07cSMaciej S. Szmigiero qemu_put_buffer(f_out, (uint8_t *)lb->data, lb->len);
219b659c07cSMaciej S. Szmigiero
220b659c07cSMaciej S. Szmigiero ret = qemu_fflush(f_out);
221b659c07cSMaciej S. Szmigiero if (ret) {
222b659c07cSMaciej S. Szmigiero error_setg(errp, "%s: load config state flush failed: %d",
223b659c07cSMaciej S. Szmigiero vbasedev->name, ret);
224c59748c1SMaciej S. Szmigiero return false;
225c59748c1SMaciej S. Szmigiero }
226c59748c1SMaciej S. Szmigiero
227b659c07cSMaciej S. Szmigiero qio_channel_io_seek(QIO_CHANNEL(bioc), 0, 0, NULL);
228b659c07cSMaciej S. Szmigiero f_in = qemu_file_new_input(QIO_CHANNEL(bioc));
229b659c07cSMaciej S. Szmigiero
230b659c07cSMaciej S. Szmigiero mig_header = qemu_get_be64(f_in);
231b659c07cSMaciej S. Szmigiero if (mig_header != VFIO_MIG_FLAG_DEV_CONFIG_STATE) {
232b659c07cSMaciej S. Szmigiero error_setg(errp, "%s: expected FLAG_DEV_CONFIG_STATE but got %" PRIx64,
233b659c07cSMaciej S. Szmigiero vbasedev->name, mig_header);
234b659c07cSMaciej S. Szmigiero return false;
235b659c07cSMaciej S. Szmigiero }
236b659c07cSMaciej S. Szmigiero
237b659c07cSMaciej S. Szmigiero bql_lock();
238b659c07cSMaciej S. Szmigiero ret = vfio_load_device_config_state(f_in, vbasedev);
239b659c07cSMaciej S. Szmigiero bql_unlock();
240b659c07cSMaciej S. Szmigiero
241b659c07cSMaciej S. Szmigiero if (ret < 0) {
242b659c07cSMaciej S. Szmigiero error_setg(errp, "%s: vfio_load_device_config_state() failed: %d",
243b659c07cSMaciej S. Szmigiero vbasedev->name, ret);
244b659c07cSMaciej S. Szmigiero return false;
245b659c07cSMaciej S. Szmigiero }
246b659c07cSMaciej S. Szmigiero
247b659c07cSMaciej S. Szmigiero return true;
248b659c07cSMaciej S. Szmigiero }
249b659c07cSMaciej S. Szmigiero
vfio_load_state_buffer_get(VFIOMultifd * multifd)250c59748c1SMaciej S. Szmigiero static VFIOStateBuffer *vfio_load_state_buffer_get(VFIOMultifd *multifd)
251c59748c1SMaciej S. Szmigiero {
252c59748c1SMaciej S. Szmigiero VFIOStateBuffer *lb;
253c59748c1SMaciej S. Szmigiero unsigned int bufs_len;
254c59748c1SMaciej S. Szmigiero
255c59748c1SMaciej S. Szmigiero bufs_len = vfio_state_buffers_size_get(&multifd->load_bufs);
256c59748c1SMaciej S. Szmigiero if (multifd->load_buf_idx >= bufs_len) {
257c59748c1SMaciej S. Szmigiero assert(multifd->load_buf_idx == bufs_len);
258c59748c1SMaciej S. Szmigiero return NULL;
259c59748c1SMaciej S. Szmigiero }
260c59748c1SMaciej S. Szmigiero
261c59748c1SMaciej S. Szmigiero lb = vfio_state_buffers_at(&multifd->load_bufs,
262c59748c1SMaciej S. Szmigiero multifd->load_buf_idx);
263c59748c1SMaciej S. Szmigiero if (!lb->is_present) {
264c59748c1SMaciej S. Szmigiero return NULL;
265c59748c1SMaciej S. Szmigiero }
266c59748c1SMaciej S. Szmigiero
267c59748c1SMaciej S. Szmigiero return lb;
268c59748c1SMaciej S. Szmigiero }
269c59748c1SMaciej S. Szmigiero
vfio_load_state_buffer_write(VFIODevice * vbasedev,VFIOStateBuffer * lb,Error ** errp)270c59748c1SMaciej S. Szmigiero static bool vfio_load_state_buffer_write(VFIODevice *vbasedev,
271c59748c1SMaciej S. Szmigiero VFIOStateBuffer *lb,
272c59748c1SMaciej S. Szmigiero Error **errp)
273c59748c1SMaciej S. Szmigiero {
274c59748c1SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
275c59748c1SMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
276c59748c1SMaciej S. Szmigiero g_autofree char *buf = NULL;
277c59748c1SMaciej S. Szmigiero char *buf_cur;
278c59748c1SMaciej S. Szmigiero size_t buf_len;
279c59748c1SMaciej S. Szmigiero
280c59748c1SMaciej S. Szmigiero if (!lb->len) {
281c59748c1SMaciej S. Szmigiero return true;
282c59748c1SMaciej S. Szmigiero }
283c59748c1SMaciej S. Szmigiero
284c59748c1SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_load_start(vbasedev->name,
285c59748c1SMaciej S. Szmigiero multifd->load_buf_idx);
286c59748c1SMaciej S. Szmigiero
287c59748c1SMaciej S. Szmigiero /* lb might become re-allocated when we drop the lock */
288c59748c1SMaciej S. Szmigiero buf = g_steal_pointer(&lb->data);
289c59748c1SMaciej S. Szmigiero buf_cur = buf;
290c59748c1SMaciej S. Szmigiero buf_len = lb->len;
291c59748c1SMaciej S. Szmigiero while (buf_len > 0) {
292c59748c1SMaciej S. Szmigiero ssize_t wr_ret;
293c59748c1SMaciej S. Szmigiero int errno_save;
294c59748c1SMaciej S. Szmigiero
295c59748c1SMaciej S. Szmigiero /*
296c59748c1SMaciej S. Szmigiero * Loading data to the device takes a while,
297c59748c1SMaciej S. Szmigiero * drop the lock during this process.
298c59748c1SMaciej S. Szmigiero */
299c59748c1SMaciej S. Szmigiero qemu_mutex_unlock(&multifd->load_bufs_mutex);
300c59748c1SMaciej S. Szmigiero wr_ret = write(migration->data_fd, buf_cur, buf_len);
301c59748c1SMaciej S. Szmigiero errno_save = errno;
302c59748c1SMaciej S. Szmigiero qemu_mutex_lock(&multifd->load_bufs_mutex);
303c59748c1SMaciej S. Szmigiero
304c59748c1SMaciej S. Szmigiero if (wr_ret < 0) {
305c59748c1SMaciej S. Szmigiero error_setg(errp,
306c59748c1SMaciej S. Szmigiero "%s: writing state buffer %" PRIu32 " failed: %d",
307c59748c1SMaciej S. Szmigiero vbasedev->name, multifd->load_buf_idx, errno_save);
308c59748c1SMaciej S. Szmigiero return false;
309c59748c1SMaciej S. Szmigiero }
310c59748c1SMaciej S. Szmigiero
311c59748c1SMaciej S. Szmigiero assert(wr_ret <= buf_len);
312c59748c1SMaciej S. Szmigiero buf_len -= wr_ret;
313c59748c1SMaciej S. Szmigiero buf_cur += wr_ret;
314c59748c1SMaciej S. Szmigiero }
315c59748c1SMaciej S. Szmigiero
316c59748c1SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_load_end(vbasedev->name,
317c59748c1SMaciej S. Szmigiero multifd->load_buf_idx);
318c59748c1SMaciej S. Szmigiero
319c59748c1SMaciej S. Szmigiero return true;
320c59748c1SMaciej S. Szmigiero }
321c59748c1SMaciej S. Szmigiero
vfio_load_bufs_thread_want_exit(VFIOMultifd * multifd,bool * should_quit)322c59748c1SMaciej S. Szmigiero static bool vfio_load_bufs_thread_want_exit(VFIOMultifd *multifd,
323c59748c1SMaciej S. Szmigiero bool *should_quit)
324c59748c1SMaciej S. Szmigiero {
325c59748c1SMaciej S. Szmigiero return multifd->load_bufs_thread_want_exit || qatomic_read(should_quit);
326c59748c1SMaciej S. Szmigiero }
327c59748c1SMaciej S. Szmigiero
328c59748c1SMaciej S. Szmigiero /*
329c59748c1SMaciej S. Szmigiero * This thread is spawned by vfio_multifd_switchover_start() which gets
330c59748c1SMaciej S. Szmigiero * called upon encountering the switchover point marker in main migration
331c59748c1SMaciej S. Szmigiero * stream.
332c59748c1SMaciej S. Szmigiero *
333c59748c1SMaciej S. Szmigiero * It exits after either:
334c59748c1SMaciej S. Szmigiero * * completing loading the remaining device state and device config, OR:
335c59748c1SMaciej S. Szmigiero * * encountering some error while doing the above, OR:
336c59748c1SMaciej S. Szmigiero * * being forcefully aborted by the migration core by it setting should_quit
337c59748c1SMaciej S. Szmigiero * or by vfio_load_cleanup_load_bufs_thread() setting
338c59748c1SMaciej S. Szmigiero * multifd->load_bufs_thread_want_exit.
339c59748c1SMaciej S. Szmigiero */
vfio_load_bufs_thread(void * opaque,bool * should_quit,Error ** errp)340c59748c1SMaciej S. Szmigiero static bool vfio_load_bufs_thread(void *opaque, bool *should_quit, Error **errp)
341c59748c1SMaciej S. Szmigiero {
342c59748c1SMaciej S. Szmigiero VFIODevice *vbasedev = opaque;
343c59748c1SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
344c59748c1SMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
345c59748c1SMaciej S. Szmigiero bool ret = false;
346c59748c1SMaciej S. Szmigiero
347c59748c1SMaciej S. Szmigiero trace_vfio_load_bufs_thread_start(vbasedev->name);
348c59748c1SMaciej S. Szmigiero
349c59748c1SMaciej S. Szmigiero assert(multifd);
350c59748c1SMaciej S. Szmigiero QEMU_LOCK_GUARD(&multifd->load_bufs_mutex);
351c59748c1SMaciej S. Szmigiero
352c59748c1SMaciej S. Szmigiero assert(multifd->load_bufs_thread_running);
353c59748c1SMaciej S. Szmigiero
354c59748c1SMaciej S. Szmigiero while (true) {
355c59748c1SMaciej S. Szmigiero VFIOStateBuffer *lb;
356c59748c1SMaciej S. Szmigiero
357c59748c1SMaciej S. Szmigiero /*
358c59748c1SMaciej S. Szmigiero * Always check cancellation first after the buffer_ready wait below in
359c59748c1SMaciej S. Szmigiero * case that cond was signalled by vfio_load_cleanup_load_bufs_thread().
360c59748c1SMaciej S. Szmigiero */
361c59748c1SMaciej S. Szmigiero if (vfio_load_bufs_thread_want_exit(multifd, should_quit)) {
362c59748c1SMaciej S. Szmigiero error_setg(errp, "operation cancelled");
363c59748c1SMaciej S. Szmigiero goto thread_exit;
364c59748c1SMaciej S. Szmigiero }
365c59748c1SMaciej S. Szmigiero
366c59748c1SMaciej S. Szmigiero assert(multifd->load_buf_idx <= multifd->load_buf_idx_last);
367c59748c1SMaciej S. Szmigiero
368c59748c1SMaciej S. Szmigiero lb = vfio_load_state_buffer_get(multifd);
369c59748c1SMaciej S. Szmigiero if (!lb) {
370c59748c1SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_starved(vbasedev->name,
371c59748c1SMaciej S. Szmigiero multifd->load_buf_idx);
372c59748c1SMaciej S. Szmigiero qemu_cond_wait(&multifd->load_bufs_buffer_ready_cond,
373c59748c1SMaciej S. Szmigiero &multifd->load_bufs_mutex);
374c59748c1SMaciej S. Szmigiero continue;
375c59748c1SMaciej S. Szmigiero }
376c59748c1SMaciej S. Szmigiero
377c59748c1SMaciej S. Szmigiero if (multifd->load_buf_idx == multifd->load_buf_idx_last) {
378c59748c1SMaciej S. Szmigiero break;
379c59748c1SMaciej S. Szmigiero }
380c59748c1SMaciej S. Szmigiero
381c59748c1SMaciej S. Szmigiero if (multifd->load_buf_idx == 0) {
382c59748c1SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_start(vbasedev->name);
383c59748c1SMaciej S. Szmigiero }
384c59748c1SMaciej S. Szmigiero
385c59748c1SMaciej S. Szmigiero if (!vfio_load_state_buffer_write(vbasedev, lb, errp)) {
386c59748c1SMaciej S. Szmigiero goto thread_exit;
387c59748c1SMaciej S. Szmigiero }
388c59748c1SMaciej S. Szmigiero
389c59748c1SMaciej S. Szmigiero if (multifd->load_buf_idx == multifd->load_buf_idx_last - 1) {
390c59748c1SMaciej S. Szmigiero trace_vfio_load_state_device_buffer_end(vbasedev->name);
391c59748c1SMaciej S. Szmigiero }
392c59748c1SMaciej S. Szmigiero
393c59748c1SMaciej S. Szmigiero multifd->load_buf_idx++;
394c59748c1SMaciej S. Szmigiero }
395c59748c1SMaciej S. Szmigiero
396c59748c1SMaciej S. Szmigiero if (!vfio_load_bufs_thread_load_config(vbasedev, errp)) {
397c59748c1SMaciej S. Szmigiero goto thread_exit;
398c59748c1SMaciej S. Szmigiero }
399c59748c1SMaciej S. Szmigiero
400c59748c1SMaciej S. Szmigiero ret = true;
401c59748c1SMaciej S. Szmigiero
402c59748c1SMaciej S. Szmigiero thread_exit:
403c59748c1SMaciej S. Szmigiero /*
404c59748c1SMaciej S. Szmigiero * Notify possibly waiting vfio_load_cleanup_load_bufs_thread() that
405c59748c1SMaciej S. Szmigiero * this thread is exiting.
406c59748c1SMaciej S. Szmigiero */
407c59748c1SMaciej S. Szmigiero multifd->load_bufs_thread_running = false;
408c59748c1SMaciej S. Szmigiero qemu_cond_signal(&multifd->load_bufs_thread_finished_cond);
409c59748c1SMaciej S. Szmigiero
410c59748c1SMaciej S. Szmigiero trace_vfio_load_bufs_thread_end(vbasedev->name);
411c59748c1SMaciej S. Szmigiero
412c59748c1SMaciej S. Szmigiero return ret;
413c59748c1SMaciej S. Szmigiero }
414c59748c1SMaciej S. Szmigiero
vfio_multifd_new(void)415ff2fd1f7SMaciej S. Szmigiero static VFIOMultifd *vfio_multifd_new(void)
416ff2fd1f7SMaciej S. Szmigiero {
417ff2fd1f7SMaciej S. Szmigiero VFIOMultifd *multifd = g_new(VFIOMultifd, 1);
418ff2fd1f7SMaciej S. Szmigiero
4193228d311SMaciej S. Szmigiero vfio_state_buffers_init(&multifd->load_bufs);
4203228d311SMaciej S. Szmigiero
4213228d311SMaciej S. Szmigiero qemu_mutex_init(&multifd->load_bufs_mutex);
4223228d311SMaciej S. Szmigiero
4233228d311SMaciej S. Szmigiero multifd->load_buf_idx = 0;
4243228d311SMaciej S. Szmigiero multifd->load_buf_idx_last = UINT32_MAX;
4253228d311SMaciej S. Szmigiero qemu_cond_init(&multifd->load_bufs_buffer_ready_cond);
4263228d311SMaciej S. Szmigiero
427c59748c1SMaciej S. Szmigiero multifd->load_bufs_thread_running = false;
428c59748c1SMaciej S. Szmigiero multifd->load_bufs_thread_want_exit = false;
429c59748c1SMaciej S. Szmigiero qemu_cond_init(&multifd->load_bufs_thread_finished_cond);
430c59748c1SMaciej S. Szmigiero
431ff2fd1f7SMaciej S. Szmigiero return multifd;
432ff2fd1f7SMaciej S. Szmigiero }
433ff2fd1f7SMaciej S. Szmigiero
434c59748c1SMaciej S. Szmigiero /*
435c59748c1SMaciej S. Szmigiero * Terminates vfio_load_bufs_thread by setting
436c59748c1SMaciej S. Szmigiero * multifd->load_bufs_thread_want_exit and signalling all the conditions
437c59748c1SMaciej S. Szmigiero * the thread could be blocked on.
438c59748c1SMaciej S. Szmigiero *
439c59748c1SMaciej S. Szmigiero * Waits for the thread to signal that it had finished.
440c59748c1SMaciej S. Szmigiero */
vfio_load_cleanup_load_bufs_thread(VFIOMultifd * multifd)441c59748c1SMaciej S. Szmigiero static void vfio_load_cleanup_load_bufs_thread(VFIOMultifd *multifd)
442c59748c1SMaciej S. Szmigiero {
443c59748c1SMaciej S. Szmigiero /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */
444c59748c1SMaciej S. Szmigiero bql_unlock();
445c59748c1SMaciej S. Szmigiero WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) {
446c59748c1SMaciej S. Szmigiero while (multifd->load_bufs_thread_running) {
447c59748c1SMaciej S. Szmigiero multifd->load_bufs_thread_want_exit = true;
448c59748c1SMaciej S. Szmigiero
449c59748c1SMaciej S. Szmigiero qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond);
450c59748c1SMaciej S. Szmigiero qemu_cond_wait(&multifd->load_bufs_thread_finished_cond,
451c59748c1SMaciej S. Szmigiero &multifd->load_bufs_mutex);
452c59748c1SMaciej S. Szmigiero }
453c59748c1SMaciej S. Szmigiero }
454c59748c1SMaciej S. Szmigiero bql_lock();
455c59748c1SMaciej S. Szmigiero }
456c59748c1SMaciej S. Szmigiero
vfio_multifd_free(VFIOMultifd * multifd)457ff2fd1f7SMaciej S. Szmigiero static void vfio_multifd_free(VFIOMultifd *multifd)
458ff2fd1f7SMaciej S. Szmigiero {
459c59748c1SMaciej S. Szmigiero vfio_load_cleanup_load_bufs_thread(multifd);
460c59748c1SMaciej S. Szmigiero
461c59748c1SMaciej S. Szmigiero qemu_cond_destroy(&multifd->load_bufs_thread_finished_cond);
4623228d311SMaciej S. Szmigiero vfio_state_buffers_destroy(&multifd->load_bufs);
4633228d311SMaciej S. Szmigiero qemu_cond_destroy(&multifd->load_bufs_buffer_ready_cond);
4643228d311SMaciej S. Szmigiero qemu_mutex_destroy(&multifd->load_bufs_mutex);
4653228d311SMaciej S. Szmigiero
466ff2fd1f7SMaciej S. Szmigiero g_free(multifd);
467ff2fd1f7SMaciej S. Szmigiero }
468ff2fd1f7SMaciej S. Szmigiero
vfio_multifd_cleanup(VFIODevice * vbasedev)469ff2fd1f7SMaciej S. Szmigiero void vfio_multifd_cleanup(VFIODevice *vbasedev)
470ff2fd1f7SMaciej S. Szmigiero {
471ff2fd1f7SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
472ff2fd1f7SMaciej S. Szmigiero
473ff2fd1f7SMaciej S. Szmigiero g_clear_pointer(&migration->multifd, vfio_multifd_free);
474ff2fd1f7SMaciej S. Szmigiero }
475ff2fd1f7SMaciej S. Szmigiero
vfio_multifd_transfer_supported(void)4762efa35d3SMaciej S. Szmigiero bool vfio_multifd_transfer_supported(void)
4772efa35d3SMaciej S. Szmigiero {
4782efa35d3SMaciej S. Szmigiero return multifd_device_state_supported() &&
4792efa35d3SMaciej S. Szmigiero migrate_send_switchover_start();
4802efa35d3SMaciej S. Szmigiero }
481ff2fd1f7SMaciej S. Szmigiero
vfio_multifd_transfer_enabled(VFIODevice * vbasedev)482ff2fd1f7SMaciej S. Szmigiero bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev)
483ff2fd1f7SMaciej S. Szmigiero {
484623af41dSMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
485623af41dSMaciej S. Szmigiero
486623af41dSMaciej S. Szmigiero return migration->multifd_transfer;
487ff2fd1f7SMaciej S. Szmigiero }
488ff2fd1f7SMaciej S. Szmigiero
vfio_multifd_setup(VFIODevice * vbasedev,bool alloc_multifd,Error ** errp)489ff2fd1f7SMaciej S. Szmigiero bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp)
490ff2fd1f7SMaciej S. Szmigiero {
491ff2fd1f7SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
492ff2fd1f7SMaciej S. Szmigiero
4934c765ceaSMaciej S. Szmigiero /*
4944c765ceaSMaciej S. Szmigiero * Make a copy of this setting at the start in case it is changed
4954c765ceaSMaciej S. Szmigiero * mid-migration.
4964c765ceaSMaciej S. Szmigiero */
497623af41dSMaciej S. Szmigiero if (vbasedev->migration_multifd_transfer == ON_OFF_AUTO_AUTO) {
498623af41dSMaciej S. Szmigiero migration->multifd_transfer = vfio_multifd_transfer_supported();
499623af41dSMaciej S. Szmigiero } else {
500623af41dSMaciej S. Szmigiero migration->multifd_transfer =
501623af41dSMaciej S. Szmigiero vbasedev->migration_multifd_transfer == ON_OFF_AUTO_ON;
502623af41dSMaciej S. Szmigiero }
503623af41dSMaciej S. Szmigiero
504ff2fd1f7SMaciej S. Szmigiero if (!vfio_multifd_transfer_enabled(vbasedev)) {
505ff2fd1f7SMaciej S. Szmigiero /* Nothing further to check or do */
506ff2fd1f7SMaciej S. Szmigiero return true;
507ff2fd1f7SMaciej S. Szmigiero }
508ff2fd1f7SMaciej S. Szmigiero
509623af41dSMaciej S. Szmigiero if (!vfio_multifd_transfer_supported()) {
510623af41dSMaciej S. Szmigiero error_setg(errp,
511623af41dSMaciej S. Szmigiero "%s: Multifd device transfer requested but unsupported in the current config",
512623af41dSMaciej S. Szmigiero vbasedev->name);
513623af41dSMaciej S. Szmigiero return false;
514623af41dSMaciej S. Szmigiero }
515623af41dSMaciej S. Szmigiero
516ff2fd1f7SMaciej S. Szmigiero if (alloc_multifd) {
517ff2fd1f7SMaciej S. Szmigiero assert(!migration->multifd);
518ff2fd1f7SMaciej S. Szmigiero migration->multifd = vfio_multifd_new();
519ff2fd1f7SMaciej S. Szmigiero }
520ff2fd1f7SMaciej S. Szmigiero
521ff2fd1f7SMaciej S. Szmigiero return true;
522ff2fd1f7SMaciej S. Szmigiero }
523c59748c1SMaciej S. Szmigiero
vfio_multifd_emit_dummy_eos(VFIODevice * vbasedev,QEMUFile * f)5246d644baeSMaciej S. Szmigiero void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f)
5256d644baeSMaciej S. Szmigiero {
5266d644baeSMaciej S. Szmigiero assert(vfio_multifd_transfer_enabled(vbasedev));
5276d644baeSMaciej S. Szmigiero
5286d644baeSMaciej S. Szmigiero /*
5296d644baeSMaciej S. Szmigiero * Emit dummy NOP data on the main migration channel since the actual
5306d644baeSMaciej S. Szmigiero * device state transfer is done via multifd channels.
5316d644baeSMaciej S. Szmigiero */
5326d644baeSMaciej S. Szmigiero qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
5336d644baeSMaciej S. Szmigiero }
5346d644baeSMaciej S. Szmigiero
5356d644baeSMaciej S. Szmigiero static bool
vfio_save_complete_precopy_thread_config_state(VFIODevice * vbasedev,char * idstr,uint32_t instance_id,uint32_t idx,Error ** errp)5366d644baeSMaciej S. Szmigiero vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev,
5376d644baeSMaciej S. Szmigiero char *idstr,
5386d644baeSMaciej S. Szmigiero uint32_t instance_id,
5396d644baeSMaciej S. Szmigiero uint32_t idx,
5406d644baeSMaciej S. Szmigiero Error **errp)
5416d644baeSMaciej S. Szmigiero {
5426d644baeSMaciej S. Szmigiero g_autoptr(QIOChannelBuffer) bioc = NULL;
5436d644baeSMaciej S. Szmigiero g_autoptr(QEMUFile) f = NULL;
5446d644baeSMaciej S. Szmigiero int ret;
5456d644baeSMaciej S. Szmigiero g_autofree VFIODeviceStatePacket *packet = NULL;
5466d644baeSMaciej S. Szmigiero size_t packet_len;
5476d644baeSMaciej S. Szmigiero
5486d644baeSMaciej S. Szmigiero bioc = qio_channel_buffer_new(0);
5496d644baeSMaciej S. Szmigiero qio_channel_set_name(QIO_CHANNEL(bioc), "vfio-device-config-save");
5506d644baeSMaciej S. Szmigiero
5516d644baeSMaciej S. Szmigiero f = qemu_file_new_output(QIO_CHANNEL(bioc));
5526d644baeSMaciej S. Szmigiero
5536d644baeSMaciej S. Szmigiero if (vfio_save_device_config_state(f, vbasedev, errp)) {
5546d644baeSMaciej S. Szmigiero return false;
5556d644baeSMaciej S. Szmigiero }
5566d644baeSMaciej S. Szmigiero
5576d644baeSMaciej S. Szmigiero ret = qemu_fflush(f);
5586d644baeSMaciej S. Szmigiero if (ret) {
5596d644baeSMaciej S. Szmigiero error_setg(errp, "%s: save config state flush failed: %d",
5606d644baeSMaciej S. Szmigiero vbasedev->name, ret);
5616d644baeSMaciej S. Szmigiero return false;
5626d644baeSMaciej S. Szmigiero }
5636d644baeSMaciej S. Szmigiero
5646d644baeSMaciej S. Szmigiero packet_len = sizeof(*packet) + bioc->usage;
5656d644baeSMaciej S. Szmigiero packet = g_malloc0(packet_len);
566*cbb2e105SMaciej S. Szmigiero packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT);
567*cbb2e105SMaciej S. Szmigiero packet->idx = cpu_to_be32(idx);
568*cbb2e105SMaciej S. Szmigiero packet->flags = cpu_to_be32(VFIO_DEVICE_STATE_CONFIG_STATE);
5696d644baeSMaciej S. Szmigiero memcpy(&packet->data, bioc->data, bioc->usage);
5706d644baeSMaciej S. Szmigiero
5716d644baeSMaciej S. Szmigiero if (!multifd_queue_device_state(idstr, instance_id,
5726d644baeSMaciej S. Szmigiero (char *)packet, packet_len)) {
5736d644baeSMaciej S. Szmigiero error_setg(errp, "%s: multifd config data queuing failed",
5746d644baeSMaciej S. Szmigiero vbasedev->name);
5756d644baeSMaciej S. Szmigiero return false;
5766d644baeSMaciej S. Szmigiero }
5776d644baeSMaciej S. Szmigiero
5786d644baeSMaciej S. Szmigiero vfio_mig_add_bytes_transferred(packet_len);
5796d644baeSMaciej S. Szmigiero
5806d644baeSMaciej S. Szmigiero return true;
5816d644baeSMaciej S. Szmigiero }
5826d644baeSMaciej S. Szmigiero
5836d644baeSMaciej S. Szmigiero /*
5846d644baeSMaciej S. Szmigiero * This thread is spawned by the migration core directly via
5856d644baeSMaciej S. Szmigiero * .save_live_complete_precopy_thread SaveVMHandler.
5866d644baeSMaciej S. Szmigiero *
5876d644baeSMaciej S. Szmigiero * It exits after either:
5886d644baeSMaciej S. Szmigiero * * completing saving the remaining device state and device config, OR:
5896d644baeSMaciej S. Szmigiero * * encountering some error while doing the above, OR:
5906d644baeSMaciej S. Szmigiero * * being forcefully aborted by the migration core by
5916d644baeSMaciej S. Szmigiero * multifd_device_state_save_thread_should_exit() returning true.
5926d644baeSMaciej S. Szmigiero */
5936d644baeSMaciej S. Szmigiero bool
vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData * d,Error ** errp)5946d644baeSMaciej S. Szmigiero vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d,
5956d644baeSMaciej S. Szmigiero Error **errp)
5966d644baeSMaciej S. Szmigiero {
5976d644baeSMaciej S. Szmigiero VFIODevice *vbasedev = d->handler_opaque;
5986d644baeSMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
5996d644baeSMaciej S. Szmigiero bool ret = false;
6006d644baeSMaciej S. Szmigiero g_autofree VFIODeviceStatePacket *packet = NULL;
6016d644baeSMaciej S. Szmigiero uint32_t idx;
6026d644baeSMaciej S. Szmigiero
6036d644baeSMaciej S. Szmigiero if (!vfio_multifd_transfer_enabled(vbasedev)) {
6046d644baeSMaciej S. Szmigiero /* Nothing to do, vfio_save_complete_precopy() does the transfer. */
6056d644baeSMaciej S. Szmigiero return true;
6066d644baeSMaciej S. Szmigiero }
6076d644baeSMaciej S. Szmigiero
6086d644baeSMaciej S. Szmigiero trace_vfio_save_complete_precopy_thread_start(vbasedev->name,
6096d644baeSMaciej S. Szmigiero d->idstr, d->instance_id);
6106d644baeSMaciej S. Szmigiero
6116d644baeSMaciej S. Szmigiero /* We reach here with device state STOP or STOP_COPY only */
6126d644baeSMaciej S. Szmigiero if (vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_STOP_COPY,
6136d644baeSMaciej S. Szmigiero VFIO_DEVICE_STATE_STOP, errp)) {
6146d644baeSMaciej S. Szmigiero goto thread_exit;
6156d644baeSMaciej S. Szmigiero }
6166d644baeSMaciej S. Szmigiero
6176d644baeSMaciej S. Szmigiero packet = g_malloc0(sizeof(*packet) + migration->data_buffer_size);
618*cbb2e105SMaciej S. Szmigiero packet->version = cpu_to_be32(VFIO_DEVICE_STATE_PACKET_VER_CURRENT);
6196d644baeSMaciej S. Szmigiero
6206d644baeSMaciej S. Szmigiero for (idx = 0; ; idx++) {
6216d644baeSMaciej S. Szmigiero ssize_t data_size;
6226d644baeSMaciej S. Szmigiero size_t packet_size;
6236d644baeSMaciej S. Szmigiero
6246d644baeSMaciej S. Szmigiero if (multifd_device_state_save_thread_should_exit()) {
6256d644baeSMaciej S. Szmigiero error_setg(errp, "operation cancelled");
6266d644baeSMaciej S. Szmigiero goto thread_exit;
6276d644baeSMaciej S. Szmigiero }
6286d644baeSMaciej S. Szmigiero
6296d644baeSMaciej S. Szmigiero data_size = read(migration->data_fd, &packet->data,
6306d644baeSMaciej S. Szmigiero migration->data_buffer_size);
6316d644baeSMaciej S. Szmigiero if (data_size < 0) {
6326d644baeSMaciej S. Szmigiero error_setg(errp, "%s: reading state buffer %" PRIu32 " failed: %d",
6336d644baeSMaciej S. Szmigiero vbasedev->name, idx, errno);
6346d644baeSMaciej S. Szmigiero goto thread_exit;
6356d644baeSMaciej S. Szmigiero } else if (data_size == 0) {
6366d644baeSMaciej S. Szmigiero break;
6376d644baeSMaciej S. Szmigiero }
6386d644baeSMaciej S. Szmigiero
639*cbb2e105SMaciej S. Szmigiero packet->idx = cpu_to_be32(idx);
6406d644baeSMaciej S. Szmigiero packet_size = sizeof(*packet) + data_size;
6416d644baeSMaciej S. Szmigiero
6426d644baeSMaciej S. Szmigiero if (!multifd_queue_device_state(d->idstr, d->instance_id,
6436d644baeSMaciej S. Szmigiero (char *)packet, packet_size)) {
6446d644baeSMaciej S. Szmigiero error_setg(errp, "%s: multifd data queuing failed", vbasedev->name);
6456d644baeSMaciej S. Szmigiero goto thread_exit;
6466d644baeSMaciej S. Szmigiero }
6476d644baeSMaciej S. Szmigiero
6486d644baeSMaciej S. Szmigiero vfio_mig_add_bytes_transferred(packet_size);
6496d644baeSMaciej S. Szmigiero }
6506d644baeSMaciej S. Szmigiero
6516d644baeSMaciej S. Szmigiero if (!vfio_save_complete_precopy_thread_config_state(vbasedev,
6526d644baeSMaciej S. Szmigiero d->idstr,
6536d644baeSMaciej S. Szmigiero d->instance_id,
6546d644baeSMaciej S. Szmigiero idx, errp)) {
6556d644baeSMaciej S. Szmigiero goto thread_exit;
6566d644baeSMaciej S. Szmigiero }
6576d644baeSMaciej S. Szmigiero
6586d644baeSMaciej S. Szmigiero ret = true;
6596d644baeSMaciej S. Szmigiero
6606d644baeSMaciej S. Szmigiero thread_exit:
6616d644baeSMaciej S. Szmigiero trace_vfio_save_complete_precopy_thread_end(vbasedev->name, ret);
6626d644baeSMaciej S. Szmigiero
6636d644baeSMaciej S. Szmigiero return ret;
6646d644baeSMaciej S. Szmigiero }
6656d644baeSMaciej S. Szmigiero
vfio_multifd_switchover_start(VFIODevice * vbasedev)666c59748c1SMaciej S. Szmigiero int vfio_multifd_switchover_start(VFIODevice *vbasedev)
667c59748c1SMaciej S. Szmigiero {
668c59748c1SMaciej S. Szmigiero VFIOMigration *migration = vbasedev->migration;
669c59748c1SMaciej S. Szmigiero VFIOMultifd *multifd = migration->multifd;
670c59748c1SMaciej S. Szmigiero
671c59748c1SMaciej S. Szmigiero assert(multifd);
672c59748c1SMaciej S. Szmigiero
673c59748c1SMaciej S. Szmigiero /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */
674c59748c1SMaciej S. Szmigiero bql_unlock();
675c59748c1SMaciej S. Szmigiero WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) {
676c59748c1SMaciej S. Szmigiero assert(!multifd->load_bufs_thread_running);
677c59748c1SMaciej S. Szmigiero multifd->load_bufs_thread_running = true;
678c59748c1SMaciej S. Szmigiero }
679c59748c1SMaciej S. Szmigiero bql_lock();
680c59748c1SMaciej S. Szmigiero
681c59748c1SMaciej S. Szmigiero qemu_loadvm_start_load_thread(vfio_load_bufs_thread, vbasedev);
682c59748c1SMaciej S. Szmigiero
683c59748c1SMaciej S. Szmigiero return 0;
684c59748c1SMaciej S. Szmigiero }
685