10525b91aSMaciej S. Szmigiero /*
20525b91aSMaciej S. Szmigiero * Multifd device state migration
30525b91aSMaciej S. Szmigiero *
40525b91aSMaciej S. Szmigiero * Copyright (C) 2024,2025 Oracle and/or its affiliates.
50525b91aSMaciej S. Szmigiero *
60525b91aSMaciej S. Szmigiero * This work is licensed under the terms of the GNU GPL, version 2 or later.
70525b91aSMaciej S. Szmigiero * See the COPYING file in the top-level directory.
80525b91aSMaciej S. Szmigiero *
90525b91aSMaciej S. Szmigiero * SPDX-License-Identifier: GPL-2.0-or-later
100525b91aSMaciej S. Szmigiero */
110525b91aSMaciej S. Szmigiero
120525b91aSMaciej S. Szmigiero #include "qemu/osdep.h"
13*8305921aSMaciej S. Szmigiero #include "qapi/error.h"
140525b91aSMaciej S. Szmigiero #include "qemu/lockable.h"
15*8305921aSMaciej S. Szmigiero #include "block/thread-pool.h"
16*8305921aSMaciej S. Szmigiero #include "migration.h"
170525b91aSMaciej S. Szmigiero #include "migration/misc.h"
180525b91aSMaciej S. Szmigiero #include "multifd.h"
19a1131aa9SMaciej S. Szmigiero #include "options.h"
200525b91aSMaciej S. Szmigiero
210525b91aSMaciej S. Szmigiero static struct {
220525b91aSMaciej S. Szmigiero QemuMutex queue_job_mutex;
230525b91aSMaciej S. Szmigiero
240525b91aSMaciej S. Szmigiero MultiFDSendData *send_data;
25*8305921aSMaciej S. Szmigiero
26*8305921aSMaciej S. Szmigiero ThreadPool *threads;
27*8305921aSMaciej S. Szmigiero bool threads_abort;
280525b91aSMaciej S. Szmigiero } *multifd_send_device_state;
290525b91aSMaciej S. Szmigiero
multifd_device_state_send_setup(void)300525b91aSMaciej S. Szmigiero void multifd_device_state_send_setup(void)
310525b91aSMaciej S. Szmigiero {
320525b91aSMaciej S. Szmigiero assert(!multifd_send_device_state);
330525b91aSMaciej S. Szmigiero multifd_send_device_state = g_malloc(sizeof(*multifd_send_device_state));
340525b91aSMaciej S. Szmigiero
350525b91aSMaciej S. Szmigiero qemu_mutex_init(&multifd_send_device_state->queue_job_mutex);
360525b91aSMaciej S. Szmigiero
370525b91aSMaciej S. Szmigiero multifd_send_device_state->send_data = multifd_send_data_alloc();
38*8305921aSMaciej S. Szmigiero
39*8305921aSMaciej S. Szmigiero multifd_send_device_state->threads = thread_pool_new();
40*8305921aSMaciej S. Szmigiero multifd_send_device_state->threads_abort = false;
410525b91aSMaciej S. Szmigiero }
420525b91aSMaciej S. Szmigiero
multifd_device_state_send_cleanup(void)430525b91aSMaciej S. Szmigiero void multifd_device_state_send_cleanup(void)
440525b91aSMaciej S. Szmigiero {
45*8305921aSMaciej S. Szmigiero g_clear_pointer(&multifd_send_device_state->threads, thread_pool_free);
460525b91aSMaciej S. Szmigiero g_clear_pointer(&multifd_send_device_state->send_data,
470525b91aSMaciej S. Szmigiero multifd_send_data_free);
480525b91aSMaciej S. Szmigiero
490525b91aSMaciej S. Szmigiero qemu_mutex_destroy(&multifd_send_device_state->queue_job_mutex);
500525b91aSMaciej S. Szmigiero
510525b91aSMaciej S. Szmigiero g_clear_pointer(&multifd_send_device_state, g_free);
520525b91aSMaciej S. Szmigiero }
530525b91aSMaciej S. Szmigiero
multifd_send_data_clear_device_state(MultiFDDeviceState_t * device_state)540525b91aSMaciej S. Szmigiero void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state)
550525b91aSMaciej S. Szmigiero {
560525b91aSMaciej S. Szmigiero g_clear_pointer(&device_state->idstr, g_free);
570525b91aSMaciej S. Szmigiero g_clear_pointer(&device_state->buf, g_free);
580525b91aSMaciej S. Szmigiero }
590525b91aSMaciej S. Szmigiero
multifd_device_state_fill_packet(MultiFDSendParams * p)600525b91aSMaciej S. Szmigiero static void multifd_device_state_fill_packet(MultiFDSendParams *p)
610525b91aSMaciej S. Szmigiero {
620525b91aSMaciej S. Szmigiero MultiFDDeviceState_t *device_state = &p->data->u.device_state;
630525b91aSMaciej S. Szmigiero MultiFDPacketDeviceState_t *packet = p->packet_device_state;
640525b91aSMaciej S. Szmigiero
650525b91aSMaciej S. Szmigiero packet->hdr.flags = cpu_to_be32(p->flags);
660525b91aSMaciej S. Szmigiero strncpy(packet->idstr, device_state->idstr, sizeof(packet->idstr) - 1);
670525b91aSMaciej S. Szmigiero packet->idstr[sizeof(packet->idstr) - 1] = 0;
680525b91aSMaciej S. Szmigiero packet->instance_id = cpu_to_be32(device_state->instance_id);
690525b91aSMaciej S. Szmigiero packet->next_packet_size = cpu_to_be32(p->next_packet_size);
700525b91aSMaciej S. Szmigiero }
710525b91aSMaciej S. Szmigiero
multifd_prepare_header_device_state(MultiFDSendParams * p)720525b91aSMaciej S. Szmigiero static void multifd_prepare_header_device_state(MultiFDSendParams *p)
730525b91aSMaciej S. Szmigiero {
740525b91aSMaciej S. Szmigiero p->iov[0].iov_len = sizeof(*p->packet_device_state);
750525b91aSMaciej S. Szmigiero p->iov[0].iov_base = p->packet_device_state;
760525b91aSMaciej S. Szmigiero p->iovs_num++;
770525b91aSMaciej S. Szmigiero }
780525b91aSMaciej S. Szmigiero
multifd_device_state_send_prepare(MultiFDSendParams * p)790525b91aSMaciej S. Szmigiero void multifd_device_state_send_prepare(MultiFDSendParams *p)
800525b91aSMaciej S. Szmigiero {
810525b91aSMaciej S. Szmigiero MultiFDDeviceState_t *device_state = &p->data->u.device_state;
820525b91aSMaciej S. Szmigiero
830525b91aSMaciej S. Szmigiero assert(multifd_payload_device_state(p->data));
840525b91aSMaciej S. Szmigiero
850525b91aSMaciej S. Szmigiero multifd_prepare_header_device_state(p);
860525b91aSMaciej S. Szmigiero
870525b91aSMaciej S. Szmigiero assert(!(p->flags & MULTIFD_FLAG_SYNC));
880525b91aSMaciej S. Szmigiero
890525b91aSMaciej S. Szmigiero p->next_packet_size = device_state->buf_len;
900525b91aSMaciej S. Szmigiero if (p->next_packet_size > 0) {
910525b91aSMaciej S. Szmigiero p->iov[p->iovs_num].iov_base = device_state->buf;
920525b91aSMaciej S. Szmigiero p->iov[p->iovs_num].iov_len = p->next_packet_size;
930525b91aSMaciej S. Szmigiero p->iovs_num++;
940525b91aSMaciej S. Szmigiero }
950525b91aSMaciej S. Szmigiero
960525b91aSMaciej S. Szmigiero p->flags |= MULTIFD_FLAG_NOCOMP | MULTIFD_FLAG_DEVICE_STATE;
970525b91aSMaciej S. Szmigiero
980525b91aSMaciej S. Szmigiero multifd_device_state_fill_packet(p);
990525b91aSMaciej S. Szmigiero }
1000525b91aSMaciej S. Szmigiero
multifd_queue_device_state(char * idstr,uint32_t instance_id,char * data,size_t len)1010525b91aSMaciej S. Szmigiero bool multifd_queue_device_state(char *idstr, uint32_t instance_id,
1020525b91aSMaciej S. Szmigiero char *data, size_t len)
1030525b91aSMaciej S. Szmigiero {
1040525b91aSMaciej S. Szmigiero /* Device state submissions can come from multiple threads */
1050525b91aSMaciej S. Szmigiero QEMU_LOCK_GUARD(&multifd_send_device_state->queue_job_mutex);
1060525b91aSMaciej S. Szmigiero MultiFDDeviceState_t *device_state;
1070525b91aSMaciej S. Szmigiero
1080525b91aSMaciej S. Szmigiero assert(multifd_payload_empty(multifd_send_device_state->send_data));
1090525b91aSMaciej S. Szmigiero
1100525b91aSMaciej S. Szmigiero multifd_set_payload_type(multifd_send_device_state->send_data,
1110525b91aSMaciej S. Szmigiero MULTIFD_PAYLOAD_DEVICE_STATE);
1120525b91aSMaciej S. Szmigiero device_state = &multifd_send_device_state->send_data->u.device_state;
1130525b91aSMaciej S. Szmigiero device_state->idstr = g_strdup(idstr);
1140525b91aSMaciej S. Szmigiero device_state->instance_id = instance_id;
1150525b91aSMaciej S. Szmigiero device_state->buf = g_memdup2(data, len);
1160525b91aSMaciej S. Szmigiero device_state->buf_len = len;
1170525b91aSMaciej S. Szmigiero
1180525b91aSMaciej S. Szmigiero if (!multifd_send(&multifd_send_device_state->send_data)) {
1190525b91aSMaciej S. Szmigiero multifd_send_data_clear(multifd_send_device_state->send_data);
1200525b91aSMaciej S. Szmigiero return false;
1210525b91aSMaciej S. Szmigiero }
1220525b91aSMaciej S. Szmigiero
1230525b91aSMaciej S. Szmigiero return true;
1240525b91aSMaciej S. Szmigiero }
125a1131aa9SMaciej S. Szmigiero
multifd_device_state_supported(void)126a1131aa9SMaciej S. Szmigiero bool multifd_device_state_supported(void)
127a1131aa9SMaciej S. Szmigiero {
128a1131aa9SMaciej S. Szmigiero return migrate_multifd() && !migrate_mapped_ram() &&
129a1131aa9SMaciej S. Szmigiero migrate_multifd_compression() == MULTIFD_COMPRESSION_NONE;
130a1131aa9SMaciej S. Szmigiero }
131*8305921aSMaciej S. Szmigiero
multifd_device_state_save_thread_data_free(void * opaque)132*8305921aSMaciej S. Szmigiero static void multifd_device_state_save_thread_data_free(void *opaque)
133*8305921aSMaciej S. Szmigiero {
134*8305921aSMaciej S. Szmigiero SaveLiveCompletePrecopyThreadData *data = opaque;
135*8305921aSMaciej S. Szmigiero
136*8305921aSMaciej S. Szmigiero g_clear_pointer(&data->idstr, g_free);
137*8305921aSMaciej S. Szmigiero g_free(data);
138*8305921aSMaciej S. Szmigiero }
139*8305921aSMaciej S. Szmigiero
multifd_device_state_save_thread(void * opaque)140*8305921aSMaciej S. Szmigiero static int multifd_device_state_save_thread(void *opaque)
141*8305921aSMaciej S. Szmigiero {
142*8305921aSMaciej S. Szmigiero SaveLiveCompletePrecopyThreadData *data = opaque;
143*8305921aSMaciej S. Szmigiero g_autoptr(Error) local_err = NULL;
144*8305921aSMaciej S. Szmigiero
145*8305921aSMaciej S. Szmigiero if (!data->hdlr(data, &local_err)) {
146*8305921aSMaciej S. Szmigiero MigrationState *s = migrate_get_current();
147*8305921aSMaciej S. Szmigiero
148*8305921aSMaciej S. Szmigiero /*
149*8305921aSMaciej S. Szmigiero * Can't call abort_device_state_save_threads() here since new
150*8305921aSMaciej S. Szmigiero * save threads could still be in process of being launched
151*8305921aSMaciej S. Szmigiero * (if, for example, the very first save thread launched exited
152*8305921aSMaciej S. Szmigiero * with an error very quickly).
153*8305921aSMaciej S. Szmigiero */
154*8305921aSMaciej S. Szmigiero
155*8305921aSMaciej S. Szmigiero assert(local_err);
156*8305921aSMaciej S. Szmigiero
157*8305921aSMaciej S. Szmigiero /*
158*8305921aSMaciej S. Szmigiero * In case of multiple save threads failing which thread error
159*8305921aSMaciej S. Szmigiero * return we end setting is purely arbitrary.
160*8305921aSMaciej S. Szmigiero */
161*8305921aSMaciej S. Szmigiero migrate_set_error(s, local_err);
162*8305921aSMaciej S. Szmigiero }
163*8305921aSMaciej S. Szmigiero
164*8305921aSMaciej S. Szmigiero return 0;
165*8305921aSMaciej S. Szmigiero }
166*8305921aSMaciej S. Szmigiero
multifd_device_state_save_thread_should_exit(void)167*8305921aSMaciej S. Szmigiero bool multifd_device_state_save_thread_should_exit(void)
168*8305921aSMaciej S. Szmigiero {
169*8305921aSMaciej S. Szmigiero return qatomic_read(&multifd_send_device_state->threads_abort);
170*8305921aSMaciej S. Szmigiero }
171*8305921aSMaciej S. Szmigiero
172*8305921aSMaciej S. Szmigiero void
multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr,char * idstr,uint32_t instance_id,void * opaque)173*8305921aSMaciej S. Szmigiero multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr,
174*8305921aSMaciej S. Szmigiero char *idstr, uint32_t instance_id,
175*8305921aSMaciej S. Szmigiero void *opaque)
176*8305921aSMaciej S. Szmigiero {
177*8305921aSMaciej S. Szmigiero SaveLiveCompletePrecopyThreadData *data;
178*8305921aSMaciej S. Szmigiero
179*8305921aSMaciej S. Szmigiero assert(multifd_device_state_supported());
180*8305921aSMaciej S. Szmigiero assert(multifd_send_device_state);
181*8305921aSMaciej S. Szmigiero
182*8305921aSMaciej S. Szmigiero assert(!qatomic_read(&multifd_send_device_state->threads_abort));
183*8305921aSMaciej S. Szmigiero
184*8305921aSMaciej S. Szmigiero data = g_new(SaveLiveCompletePrecopyThreadData, 1);
185*8305921aSMaciej S. Szmigiero data->hdlr = hdlr;
186*8305921aSMaciej S. Szmigiero data->idstr = g_strdup(idstr);
187*8305921aSMaciej S. Szmigiero data->instance_id = instance_id;
188*8305921aSMaciej S. Szmigiero data->handler_opaque = opaque;
189*8305921aSMaciej S. Szmigiero
190*8305921aSMaciej S. Szmigiero thread_pool_submit_immediate(multifd_send_device_state->threads,
191*8305921aSMaciej S. Szmigiero multifd_device_state_save_thread,
192*8305921aSMaciej S. Szmigiero data,
193*8305921aSMaciej S. Szmigiero multifd_device_state_save_thread_data_free);
194*8305921aSMaciej S. Szmigiero }
195*8305921aSMaciej S. Szmigiero
multifd_abort_device_state_save_threads(void)196*8305921aSMaciej S. Szmigiero void multifd_abort_device_state_save_threads(void)
197*8305921aSMaciej S. Szmigiero {
198*8305921aSMaciej S. Szmigiero assert(multifd_device_state_supported());
199*8305921aSMaciej S. Szmigiero
200*8305921aSMaciej S. Szmigiero qatomic_set(&multifd_send_device_state->threads_abort, true);
201*8305921aSMaciej S. Szmigiero }
202*8305921aSMaciej S. Szmigiero
multifd_join_device_state_save_threads(void)203*8305921aSMaciej S. Szmigiero bool multifd_join_device_state_save_threads(void)
204*8305921aSMaciej S. Szmigiero {
205*8305921aSMaciej S. Szmigiero MigrationState *s = migrate_get_current();
206*8305921aSMaciej S. Szmigiero
207*8305921aSMaciej S. Szmigiero assert(multifd_device_state_supported());
208*8305921aSMaciej S. Szmigiero
209*8305921aSMaciej S. Szmigiero thread_pool_wait(multifd_send_device_state->threads);
210*8305921aSMaciej S. Szmigiero
211*8305921aSMaciej S. Szmigiero return !migrate_has_error(s);
212*8305921aSMaciej S. Szmigiero }
213