xref: /openbmc/qemu/hw/vfio/migration-multifd.c (revision 74b3445378f7d9be35b7a77757bb568080a6f929)
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