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
cpr_save_fd(const char * name,int id,int fd)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
find_fd(CprFdList * head,const char * name,int id)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
cpr_delete_fd(const char * name,int id)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
cpr_find_fd(const char * name,int id)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
cpr_resave_fd(const char * name,int id,int fd)99 void cpr_resave_fd(const char *name, int id, int fd)
100 {
101 CprFd *elem = find_fd(&cpr_state.fds, name, id);
102 int old_fd = elem ? elem->fd : -1;
103
104 if (old_fd < 0) {
105 cpr_save_fd(name, id, fd);
106 } else if (old_fd != fd) {
107 error_setg(&error_fatal,
108 "internal error: cpr fd '%s' id %d value %d "
109 "already saved with a different value %d",
110 name, id, fd, old_fd);
111 }
112 }
113
cpr_open_fd(const char * path,int flags,const char * name,int id,Error ** errp)114 int cpr_open_fd(const char *path, int flags, const char *name, int id,
115 Error **errp)
116 {
117 int fd = cpr_find_fd(name, id);
118
119 if (fd < 0) {
120 fd = qemu_open(path, flags, errp);
121 if (fd >= 0) {
122 cpr_save_fd(name, id, fd);
123 }
124 }
125 return fd;
126 }
127
128 /*************************************************************************/
129 #define CPR_STATE "CprState"
130
131 static const VMStateDescription vmstate_cpr_state = {
132 .name = CPR_STATE,
133 .version_id = 1,
134 .minimum_version_id = 1,
135 .fields = (VMStateField[]) {
136 VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
137 VMSTATE_END_OF_LIST()
138 }
139 };
140 /*************************************************************************/
141
142 static QEMUFile *cpr_state_file;
143
cpr_state_ioc(void)144 QIOChannel *cpr_state_ioc(void)
145 {
146 return qemu_file_get_ioc(cpr_state_file);
147 }
148
149 static MigMode incoming_mode = MIG_MODE_NONE;
150
cpr_get_incoming_mode(void)151 MigMode cpr_get_incoming_mode(void)
152 {
153 return incoming_mode;
154 }
155
cpr_set_incoming_mode(MigMode mode)156 void cpr_set_incoming_mode(MigMode mode)
157 {
158 incoming_mode = mode;
159 }
160
cpr_is_incoming(void)161 bool cpr_is_incoming(void)
162 {
163 return incoming_mode != MIG_MODE_NONE;
164 }
165
cpr_state_save(MigrationChannel * channel,Error ** errp)166 int cpr_state_save(MigrationChannel *channel, Error **errp)
167 {
168 int ret;
169 QEMUFile *f;
170 MigMode mode = migrate_mode();
171
172 trace_cpr_state_save(MigMode_str(mode));
173
174 if (mode == MIG_MODE_CPR_TRANSFER) {
175 g_assert(channel);
176 f = cpr_transfer_output(channel, errp);
177 } else {
178 return 0;
179 }
180 if (!f) {
181 return -1;
182 }
183
184 qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
185 qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
186
187 ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
188 if (ret) {
189 error_setg(errp, "vmstate_save_state error %d", ret);
190 qemu_fclose(f);
191 return ret;
192 }
193
194 /*
195 * Close the socket only partially so we can later detect when the other
196 * end closes by getting a HUP event.
197 */
198 qemu_fflush(f);
199 qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
200 NULL);
201 cpr_state_file = f;
202 return 0;
203 }
204
cpr_state_load(MigrationChannel * channel,Error ** errp)205 int cpr_state_load(MigrationChannel *channel, Error **errp)
206 {
207 int ret;
208 uint32_t v;
209 QEMUFile *f;
210 MigMode mode = 0;
211
212 if (channel) {
213 mode = MIG_MODE_CPR_TRANSFER;
214 cpr_set_incoming_mode(mode);
215 f = cpr_transfer_input(channel, errp);
216 } else {
217 return 0;
218 }
219 if (!f) {
220 return -1;
221 }
222
223 trace_cpr_state_load(MigMode_str(mode));
224
225 v = qemu_get_be32(f);
226 if (v != QEMU_CPR_FILE_MAGIC) {
227 error_setg(errp, "Not a migration stream (bad magic %x)", v);
228 qemu_fclose(f);
229 return -EINVAL;
230 }
231 v = qemu_get_be32(f);
232 if (v != QEMU_CPR_FILE_VERSION) {
233 error_setg(errp, "Unsupported migration stream version %d", v);
234 qemu_fclose(f);
235 return -ENOTSUP;
236 }
237
238 ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
239 if (ret) {
240 error_setg(errp, "vmstate_load_state error %d", ret);
241 qemu_fclose(f);
242 return ret;
243 }
244
245 /*
246 * Let the caller decide when to close the socket (and generate a HUP event
247 * for the sending side).
248 */
249 cpr_state_file = f;
250
251 return ret;
252 }
253
cpr_state_close(void)254 void cpr_state_close(void)
255 {
256 if (cpr_state_file) {
257 qemu_fclose(cpr_state_file);
258 cpr_state_file = NULL;
259 }
260 }
261
cpr_incoming_needed(void * opaque)262 bool cpr_incoming_needed(void *opaque)
263 {
264 MigMode mode = migrate_mode();
265 return mode == MIG_MODE_CPR_TRANSFER;
266 }
267