xref: /openbmc/qemu/migration/cpr.c (revision aa90f1161bb17a4863e16ec2f75104cff0752d4e)
1e7d79011SSteve Sistare /*
2e7d79011SSteve Sistare  * Copyright (c) 2021-2024 Oracle and/or its affiliates.
3e7d79011SSteve Sistare  *
4e7d79011SSteve Sistare  * This work is licensed under the terms of the GNU GPL, version 2 or later.
5e7d79011SSteve Sistare  * See the COPYING file in the top-level directory.
6e7d79011SSteve Sistare  */
7e7d79011SSteve Sistare 
8e7d79011SSteve Sistare #include "qemu/osdep.h"
9e7d79011SSteve Sistare #include "qapi/error.h"
10e7d79011SSteve Sistare #include "migration/cpr.h"
11e7d79011SSteve Sistare #include "migration/misc.h"
12e7d79011SSteve Sistare #include "migration/options.h"
13e7d79011SSteve Sistare #include "migration/qemu-file.h"
14e7d79011SSteve Sistare #include "migration/savevm.h"
15e7d79011SSteve Sistare #include "migration/vmstate.h"
16e7d79011SSteve Sistare #include "system/runstate.h"
17e7d79011SSteve Sistare #include "trace.h"
18e7d79011SSteve Sistare 
19e7d79011SSteve Sistare /*************************************************************************/
20e7d79011SSteve Sistare /* cpr state container for all information to be saved. */
21e7d79011SSteve Sistare 
22e7d79011SSteve Sistare typedef QLIST_HEAD(CprFdList, CprFd) CprFdList;
23e7d79011SSteve Sistare 
24e7d79011SSteve Sistare typedef struct CprState {
25e7d79011SSteve Sistare     CprFdList fds;
26e7d79011SSteve Sistare } CprState;
27e7d79011SSteve Sistare 
28e7d79011SSteve Sistare static CprState cpr_state;
29e7d79011SSteve Sistare 
30e7d79011SSteve Sistare /****************************************************************************/
31e7d79011SSteve Sistare 
32e7d79011SSteve Sistare typedef struct CprFd {
33e7d79011SSteve Sistare     char *name;
34e7d79011SSteve Sistare     unsigned int namelen;
35e7d79011SSteve Sistare     int id;
36e7d79011SSteve Sistare     int fd;
37e7d79011SSteve Sistare     QLIST_ENTRY(CprFd) next;
38e7d79011SSteve Sistare } CprFd;
39e7d79011SSteve Sistare 
40e7d79011SSteve Sistare static const VMStateDescription vmstate_cpr_fd = {
41e7d79011SSteve Sistare     .name = "cpr fd",
42e7d79011SSteve Sistare     .version_id = 1,
43e7d79011SSteve Sistare     .minimum_version_id = 1,
44e7d79011SSteve Sistare     .fields = (VMStateField[]) {
45e7d79011SSteve Sistare         VMSTATE_UINT32(namelen, CprFd),
46e7d79011SSteve Sistare         VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen),
47e7d79011SSteve Sistare         VMSTATE_INT32(id, CprFd),
48624e6e65SSteve Sistare         VMSTATE_FD(fd, CprFd),
49e7d79011SSteve Sistare         VMSTATE_END_OF_LIST()
50e7d79011SSteve Sistare     }
51e7d79011SSteve Sistare };
52e7d79011SSteve Sistare 
cpr_save_fd(const char * name,int id,int fd)53e7d79011SSteve Sistare void cpr_save_fd(const char *name, int id, int fd)
54e7d79011SSteve Sistare {
55e7d79011SSteve Sistare     CprFd *elem = g_new0(CprFd, 1);
56e7d79011SSteve Sistare 
57e7d79011SSteve Sistare     trace_cpr_save_fd(name, id, fd);
58e7d79011SSteve Sistare     elem->name = g_strdup(name);
59e7d79011SSteve Sistare     elem->namelen = strlen(name) + 1;
60e7d79011SSteve Sistare     elem->id = id;
61e7d79011SSteve Sistare     elem->fd = fd;
62e7d79011SSteve Sistare     QLIST_INSERT_HEAD(&cpr_state.fds, elem, next);
63e7d79011SSteve Sistare }
64e7d79011SSteve Sistare 
find_fd(CprFdList * head,const char * name,int id)65e7d79011SSteve Sistare static CprFd *find_fd(CprFdList *head, const char *name, int id)
66e7d79011SSteve Sistare {
67e7d79011SSteve Sistare     CprFd *elem;
68e7d79011SSteve Sistare 
69e7d79011SSteve Sistare     QLIST_FOREACH(elem, head, next) {
70e7d79011SSteve Sistare         if (!strcmp(elem->name, name) && elem->id == id) {
71e7d79011SSteve Sistare             return elem;
72e7d79011SSteve Sistare         }
73e7d79011SSteve Sistare     }
74e7d79011SSteve Sistare     return NULL;
75e7d79011SSteve Sistare }
76e7d79011SSteve Sistare 
cpr_delete_fd(const char * name,int id)77e7d79011SSteve Sistare void cpr_delete_fd(const char *name, int id)
78e7d79011SSteve Sistare {
79e7d79011SSteve Sistare     CprFd *elem = find_fd(&cpr_state.fds, name, id);
80e7d79011SSteve Sistare 
81e7d79011SSteve Sistare     if (elem) {
82e7d79011SSteve Sistare         QLIST_REMOVE(elem, next);
83e7d79011SSteve Sistare         g_free(elem->name);
84e7d79011SSteve Sistare         g_free(elem);
85e7d79011SSteve Sistare     }
86e7d79011SSteve Sistare 
87e7d79011SSteve Sistare     trace_cpr_delete_fd(name, id);
88e7d79011SSteve Sistare }
89e7d79011SSteve Sistare 
cpr_find_fd(const char * name,int id)90e7d79011SSteve Sistare int cpr_find_fd(const char *name, int id)
91e7d79011SSteve Sistare {
92e7d79011SSteve Sistare     CprFd *elem = find_fd(&cpr_state.fds, name, id);
93e7d79011SSteve Sistare     int fd = elem ? elem->fd : -1;
94e7d79011SSteve Sistare 
95e7d79011SSteve Sistare     trace_cpr_find_fd(name, id, fd);
96e7d79011SSteve Sistare     return fd;
97e7d79011SSteve Sistare }
98e7d79011SSteve Sistare /*************************************************************************/
99e7d79011SSteve Sistare #define CPR_STATE "CprState"
100e7d79011SSteve Sistare 
101e7d79011SSteve Sistare static const VMStateDescription vmstate_cpr_state = {
102e7d79011SSteve Sistare     .name = CPR_STATE,
103e7d79011SSteve Sistare     .version_id = 1,
104e7d79011SSteve Sistare     .minimum_version_id = 1,
105e7d79011SSteve Sistare     .fields = (VMStateField[]) {
106e7d79011SSteve Sistare         VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
107e7d79011SSteve Sistare         VMSTATE_END_OF_LIST()
108e7d79011SSteve Sistare     }
109e7d79011SSteve Sistare };
110e7d79011SSteve Sistare /*************************************************************************/
111e7d79011SSteve Sistare 
112e7d79011SSteve Sistare static QEMUFile *cpr_state_file;
113e7d79011SSteve Sistare 
cpr_state_ioc(void)114e7d79011SSteve Sistare QIOChannel *cpr_state_ioc(void)
115e7d79011SSteve Sistare {
116e7d79011SSteve Sistare     return qemu_file_get_ioc(cpr_state_file);
117e7d79011SSteve Sistare }
118e7d79011SSteve Sistare 
119624e6e65SSteve Sistare static MigMode incoming_mode = MIG_MODE_NONE;
120624e6e65SSteve Sistare 
cpr_get_incoming_mode(void)121624e6e65SSteve Sistare MigMode cpr_get_incoming_mode(void)
122624e6e65SSteve Sistare {
123624e6e65SSteve Sistare     return incoming_mode;
124624e6e65SSteve Sistare }
125624e6e65SSteve Sistare 
cpr_set_incoming_mode(MigMode mode)126624e6e65SSteve Sistare void cpr_set_incoming_mode(MigMode mode)
127624e6e65SSteve Sistare {
128624e6e65SSteve Sistare     incoming_mode = mode;
129624e6e65SSteve Sistare }
130624e6e65SSteve Sistare 
cpr_is_incoming(void)131*1632a201SSteve Sistare bool cpr_is_incoming(void)
132*1632a201SSteve Sistare {
133*1632a201SSteve Sistare     return incoming_mode != MIG_MODE_NONE;
134*1632a201SSteve Sistare }
135*1632a201SSteve Sistare 
cpr_state_save(MigrationChannel * channel,Error ** errp)136e7d79011SSteve Sistare int cpr_state_save(MigrationChannel *channel, Error **errp)
137e7d79011SSteve Sistare {
138e7d79011SSteve Sistare     int ret;
139e7d79011SSteve Sistare     QEMUFile *f;
140e7d79011SSteve Sistare     MigMode mode = migrate_mode();
141e7d79011SSteve Sistare 
142e7d79011SSteve Sistare     trace_cpr_state_save(MigMode_str(mode));
143e7d79011SSteve Sistare 
144624e6e65SSteve Sistare     if (mode == MIG_MODE_CPR_TRANSFER) {
145b451705eSSteve Sistare         g_assert(channel);
146624e6e65SSteve Sistare         f = cpr_transfer_output(channel, errp);
147624e6e65SSteve Sistare     } else {
148e7d79011SSteve Sistare         return 0;
149624e6e65SSteve Sistare     }
150624e6e65SSteve Sistare     if (!f) {
151624e6e65SSteve Sistare         return -1;
152624e6e65SSteve Sistare     }
153e7d79011SSteve Sistare 
154e7d79011SSteve Sistare     qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
155e7d79011SSteve Sistare     qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
156e7d79011SSteve Sistare 
157e7d79011SSteve Sistare     ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
158e7d79011SSteve Sistare     if (ret) {
159e7d79011SSteve Sistare         error_setg(errp, "vmstate_save_state error %d", ret);
160e7d79011SSteve Sistare         qemu_fclose(f);
161e7d79011SSteve Sistare         return ret;
162e7d79011SSteve Sistare     }
163e7d79011SSteve Sistare 
164e7d79011SSteve Sistare     /*
165e7d79011SSteve Sistare      * Close the socket only partially so we can later detect when the other
166e7d79011SSteve Sistare      * end closes by getting a HUP event.
167e7d79011SSteve Sistare      */
168e7d79011SSteve Sistare     qemu_fflush(f);
169e7d79011SSteve Sistare     qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
170e7d79011SSteve Sistare                          NULL);
171e7d79011SSteve Sistare     cpr_state_file = f;
172e7d79011SSteve Sistare     return 0;
173e7d79011SSteve Sistare }
174e7d79011SSteve Sistare 
cpr_state_load(MigrationChannel * channel,Error ** errp)175e7d79011SSteve Sistare int cpr_state_load(MigrationChannel *channel, Error **errp)
176e7d79011SSteve Sistare {
177e7d79011SSteve Sistare     int ret;
178e7d79011SSteve Sistare     uint32_t v;
179e7d79011SSteve Sistare     QEMUFile *f;
180e7d79011SSteve Sistare     MigMode mode = 0;
181e7d79011SSteve Sistare 
182624e6e65SSteve Sistare     if (channel) {
183624e6e65SSteve Sistare         mode = MIG_MODE_CPR_TRANSFER;
184624e6e65SSteve Sistare         cpr_set_incoming_mode(mode);
185624e6e65SSteve Sistare         f = cpr_transfer_input(channel, errp);
186624e6e65SSteve Sistare     } else {
187e7d79011SSteve Sistare         return 0;
188624e6e65SSteve Sistare     }
189624e6e65SSteve Sistare     if (!f) {
190624e6e65SSteve Sistare         return -1;
191624e6e65SSteve Sistare     }
192e7d79011SSteve Sistare 
193e7d79011SSteve Sistare     trace_cpr_state_load(MigMode_str(mode));
194e7d79011SSteve Sistare 
195e7d79011SSteve Sistare     v = qemu_get_be32(f);
196e7d79011SSteve Sistare     if (v != QEMU_CPR_FILE_MAGIC) {
197e7d79011SSteve Sistare         error_setg(errp, "Not a migration stream (bad magic %x)", v);
198e7d79011SSteve Sistare         qemu_fclose(f);
199e7d79011SSteve Sistare         return -EINVAL;
200e7d79011SSteve Sistare     }
201e7d79011SSteve Sistare     v = qemu_get_be32(f);
202e7d79011SSteve Sistare     if (v != QEMU_CPR_FILE_VERSION) {
203e7d79011SSteve Sistare         error_setg(errp, "Unsupported migration stream version %d", v);
204e7d79011SSteve Sistare         qemu_fclose(f);
205e7d79011SSteve Sistare         return -ENOTSUP;
206e7d79011SSteve Sistare     }
207e7d79011SSteve Sistare 
208e7d79011SSteve Sistare     ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
209e7d79011SSteve Sistare     if (ret) {
210e7d79011SSteve Sistare         error_setg(errp, "vmstate_load_state error %d", ret);
211e7d79011SSteve Sistare         qemu_fclose(f);
212e7d79011SSteve Sistare         return ret;
213e7d79011SSteve Sistare     }
214e7d79011SSteve Sistare 
215e7d79011SSteve Sistare     /*
216e7d79011SSteve Sistare      * Let the caller decide when to close the socket (and generate a HUP event
217e7d79011SSteve Sistare      * for the sending side).
218e7d79011SSteve Sistare      */
219e7d79011SSteve Sistare     cpr_state_file = f;
220e7d79011SSteve Sistare 
221e7d79011SSteve Sistare     return ret;
222e7d79011SSteve Sistare }
223e7d79011SSteve Sistare 
cpr_state_close(void)224e7d79011SSteve Sistare void cpr_state_close(void)
225e7d79011SSteve Sistare {
226e7d79011SSteve Sistare     if (cpr_state_file) {
227e7d79011SSteve Sistare         qemu_fclose(cpr_state_file);
228e7d79011SSteve Sistare         cpr_state_file = NULL;
229e7d79011SSteve Sistare     }
230e7d79011SSteve Sistare }
231