qemu/migration/cpr.c
<<
>>
Prefs
   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 "hw/vfio/vfio-device.h"
  11#include "migration/cpr.h"
  12#include "migration/misc.h"
  13#include "migration/options.h"
  14#include "migration/qemu-file.h"
  15#include "migration/savevm.h"
  16#include "migration/vmstate.h"
  17#include "monitor/monitor.h"
  18#include "system/runstate.h"
  19#include "trace.h"
  20
  21/*************************************************************************/
  22/* cpr state container for all information to be saved. */
  23
  24CprState cpr_state;
  25
  26/****************************************************************************/
  27
  28typedef struct CprFd {
  29    char *name;
  30    unsigned int namelen;
  31    int id;
  32    int fd;
  33    QLIST_ENTRY(CprFd) next;
  34} CprFd;
  35
  36static const VMStateDescription vmstate_cpr_fd = {
  37    .name = "cpr fd",
  38    .version_id = 1,
  39    .minimum_version_id = 1,
  40    .fields = (VMStateField[]) {
  41        VMSTATE_UINT32(namelen, CprFd),
  42        VMSTATE_VBUFFER_ALLOC_UINT32(name, CprFd, 0, NULL, namelen),
  43        VMSTATE_INT32(id, CprFd),
  44        VMSTATE_FD(fd, CprFd),
  45        VMSTATE_END_OF_LIST()
  46    }
  47};
  48
  49void cpr_save_fd(const char *name, int id, int fd)
  50{
  51    CprFd *elem = g_new0(CprFd, 1);
  52
  53    trace_cpr_save_fd(name, id, fd);
  54    elem->name = g_strdup(name);
  55    elem->namelen = strlen(name) + 1;
  56    elem->id = id;
  57    elem->fd = fd;
  58    QLIST_INSERT_HEAD(&cpr_state.fds, elem, next);
  59}
  60
  61static CprFd *find_fd(CprFdList *head, const char *name, int id)
  62{
  63    CprFd *elem;
  64
  65    QLIST_FOREACH(elem, head, next) {
  66        if (!strcmp(elem->name, name) && elem->id == id) {
  67            return elem;
  68        }
  69    }
  70    return NULL;
  71}
  72
  73void cpr_delete_fd(const char *name, int id)
  74{
  75    CprFd *elem = find_fd(&cpr_state.fds, name, id);
  76
  77    if (elem) {
  78        QLIST_REMOVE(elem, next);
  79        g_free(elem->name);
  80        g_free(elem);
  81    }
  82
  83    trace_cpr_delete_fd(name, id);
  84}
  85
  86int cpr_find_fd(const char *name, int id)
  87{
  88    CprFd *elem = find_fd(&cpr_state.fds, name, id);
  89    int fd = elem ? elem->fd : -1;
  90
  91    trace_cpr_find_fd(name, id, fd);
  92    return fd;
  93}
  94
  95void cpr_resave_fd(const char *name, int id, int fd)
  96{
  97    CprFd *elem = find_fd(&cpr_state.fds, name, id);
  98    int old_fd = elem ? elem->fd : -1;
  99
 100    if (old_fd < 0) {
 101        cpr_save_fd(name, id, fd);
 102    } else if (old_fd != fd) {
 103        error_setg(&error_fatal,
 104                   "internal error: cpr fd '%s' id %d value %d "
 105                   "already saved with a different value %d",
 106                   name, id, fd, old_fd);
 107    }
 108}
 109
 110int cpr_open_fd(const char *path, int flags, const char *name, int id,
 111                Error **errp)
 112{
 113    int fd = cpr_find_fd(name, id);
 114
 115    if (fd < 0) {
 116        fd = qemu_open(path, flags, errp);
 117        if (fd >= 0) {
 118            cpr_save_fd(name, id, fd);
 119        }
 120    }
 121    return fd;
 122}
 123
 124/*************************************************************************/
 125static const VMStateDescription vmstate_cpr_state = {
 126    .name = CPR_STATE,
 127    .version_id = 1,
 128    .minimum_version_id = 1,
 129    .fields = (VMStateField[]) {
 130        VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next),
 131        VMSTATE_END_OF_LIST()
 132    },
 133    .subsections = (const VMStateDescription * const []) {
 134        &vmstate_cpr_vfio_devices,
 135        NULL
 136    }
 137};
 138/*************************************************************************/
 139
 140static QEMUFile *cpr_state_file;
 141
 142QIOChannel *cpr_state_ioc(void)
 143{
 144    return qemu_file_get_ioc(cpr_state_file);
 145}
 146
 147static MigMode incoming_mode = MIG_MODE_NONE;
 148
 149MigMode cpr_get_incoming_mode(void)
 150{
 151    return incoming_mode;
 152}
 153
 154void cpr_set_incoming_mode(MigMode mode)
 155{
 156    incoming_mode = mode;
 157}
 158
 159bool cpr_is_incoming(void)
 160{
 161    return incoming_mode != MIG_MODE_NONE;
 162}
 163
 164int cpr_state_save(MigrationChannel *channel, Error **errp)
 165{
 166    int ret;
 167    QEMUFile *f;
 168    MigMode mode = migrate_mode();
 169
 170    trace_cpr_state_save(MigMode_str(mode));
 171
 172    if (mode == MIG_MODE_CPR_TRANSFER) {
 173        g_assert(channel);
 174        f = cpr_transfer_output(channel, errp);
 175    } else {
 176        return 0;
 177    }
 178    if (!f) {
 179        return -1;
 180    }
 181
 182    qemu_put_be32(f, QEMU_CPR_FILE_MAGIC);
 183    qemu_put_be32(f, QEMU_CPR_FILE_VERSION);
 184
 185    ret = vmstate_save_state(f, &vmstate_cpr_state, &cpr_state, 0);
 186    if (ret) {
 187        error_setg(errp, "vmstate_save_state error %d", ret);
 188        qemu_fclose(f);
 189        return ret;
 190    }
 191
 192    /*
 193     * Close the socket only partially so we can later detect when the other
 194     * end closes by getting a HUP event.
 195     */
 196    qemu_fflush(f);
 197    qio_channel_shutdown(qemu_file_get_ioc(f), QIO_CHANNEL_SHUTDOWN_WRITE,
 198                         NULL);
 199    cpr_state_file = f;
 200    return 0;
 201}
 202
 203int cpr_state_load(MigrationChannel *channel, Error **errp)
 204{
 205    int ret;
 206    uint32_t v;
 207    QEMUFile *f;
 208    MigMode mode = 0;
 209
 210    if (channel) {
 211        mode = MIG_MODE_CPR_TRANSFER;
 212        cpr_set_incoming_mode(mode);
 213        f = cpr_transfer_input(channel, errp);
 214    } else {
 215        return 0;
 216    }
 217    if (!f) {
 218        return -1;
 219    }
 220
 221    trace_cpr_state_load(MigMode_str(mode));
 222
 223    v = qemu_get_be32(f);
 224    if (v != QEMU_CPR_FILE_MAGIC) {
 225        error_setg(errp, "Not a migration stream (bad magic %x)", v);
 226        qemu_fclose(f);
 227        return -EINVAL;
 228    }
 229    v = qemu_get_be32(f);
 230    if (v != QEMU_CPR_FILE_VERSION) {
 231        error_setg(errp, "Unsupported migration stream version %d", v);
 232        qemu_fclose(f);
 233        return -ENOTSUP;
 234    }
 235
 236    ret = vmstate_load_state(f, &vmstate_cpr_state, &cpr_state, 1);
 237    if (ret) {
 238        error_setg(errp, "vmstate_load_state error %d", ret);
 239        qemu_fclose(f);
 240        return ret;
 241    }
 242
 243    /*
 244     * Let the caller decide when to close the socket (and generate a HUP event
 245     * for the sending side).
 246     */
 247    cpr_state_file = f;
 248
 249    return ret;
 250}
 251
 252void cpr_state_close(void)
 253{
 254    if (cpr_state_file) {
 255        qemu_fclose(cpr_state_file);
 256        cpr_state_file = NULL;
 257    }
 258}
 259
 260bool cpr_incoming_needed(void *opaque)
 261{
 262    MigMode mode = migrate_mode();
 263    return mode == MIG_MODE_CPR_TRANSFER;
 264}
 265
 266/*
 267 * cpr_get_fd_param: find a descriptor and return its value.
 268 *
 269 * @name: CPR name for the descriptor
 270 * @fdname: An integer-valued string, or a name passed to a getfd command
 271 * @index: CPR index of the descriptor
 272 * @errp: returned error message
 273 *
 274 * If CPR is not being performed, then use @fdname to find the fd.
 275 * If CPR is being performed, then ignore @fdname, and look for @name
 276 * and @index in CPR state.
 277 *
 278 * On success returns the fd value, else returns -1.
 279 */
 280int cpr_get_fd_param(const char *name, const char *fdname, int index,
 281                     Error **errp)
 282{
 283    ERRP_GUARD();
 284    int fd;
 285
 286    if (cpr_is_incoming()) {
 287        fd = cpr_find_fd(name, index);
 288        if (fd < 0) {
 289            error_setg(errp, "cannot find saved value for fd %s", fdname);
 290        }
 291    } else {
 292        fd = monitor_fd_param(monitor_cur(), fdname, errp);
 293        if (fd >= 0) {
 294            cpr_save_fd(name, index, fd);
 295        } else {
 296            error_prepend(errp, "Could not parse object fd %s:", fdname);
 297        }
 298    }
 299    return fd;
 300}
 301