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