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 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 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 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 151 MigMode cpr_get_incoming_mode(void) 152 { 153 return incoming_mode; 154 } 155 156 void cpr_set_incoming_mode(MigMode mode) 157 { 158 incoming_mode = mode; 159 } 160 161 bool cpr_is_incoming(void) 162 { 163 return incoming_mode != MIG_MODE_NONE; 164 } 165 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 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 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 262 bool cpr_incoming_needed(void *opaque) 263 { 264 MigMode mode = migrate_mode(); 265 return mode == MIG_MODE_CPR_TRANSFER; 266 } 267