X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=savevm.c;h=2f631d4045cd45ce03b639897dafe459cf3cafd9;hb=d607a52364e7bfc1cd6d3e425b898e86be4e525d;hp=a2f6bc040224f78427b17331ec24f2c776437969;hpb=d9658c4732a88efc42ab43bda69ae9f62559205b;p=qemu.git diff --git a/savevm.c b/savevm.c index a2f6bc040..2f631d404 100644 --- a/savevm.c +++ b/savevm.c @@ -40,6 +40,8 @@ #include "trace.h" #include "qemu/bitops.h" #include "qemu/iov.h" +#include "block/snapshot.h" +#include "block/qapi.h" #define SELF_ANNOUNCE_ROUNDS 5 @@ -95,18 +97,18 @@ static void qemu_announce_self_once(void *opaque) if (--count) { /* delay 50ms, 150ms, 250ms, ... */ - qemu_mod_timer(timer, qemu_get_clock_ms(rt_clock) + + timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100); } else { - qemu_del_timer(timer); - qemu_free_timer(timer); + timer_del(timer); + timer_free(timer); } } void qemu_announce_self(void) { static QEMUTimer *timer; - timer = qemu_new_timer_ms(rt_clock, qemu_announce_self_once, &timer); + timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); qemu_announce_self_once(&timer); } @@ -147,35 +149,8 @@ typedef struct QEMUFileSocket QEMUFile *file; } QEMUFileSocket; -typedef struct { - Coroutine *co; - int fd; -} FDYieldUntilData; - -static void fd_coroutine_enter(void *opaque) -{ - FDYieldUntilData *data = opaque; - qemu_set_fd_handler(data->fd, NULL, NULL, NULL); - qemu_coroutine_enter(data->co, NULL); -} - -/** - * Yield until a file descriptor becomes readable - * - * Note that this function clobbers the handlers for the file descriptor. - */ -static void coroutine_fn yield_until_fd_readable(int fd) -{ - FDYieldUntilData data; - - assert(qemu_in_coroutine()); - data.co = qemu_coroutine_self(); - data.fd = fd; - qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data); - qemu_coroutine_yield(); -} - -static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt) +static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) { QEMUFileSocket *s = opaque; ssize_t len; @@ -218,18 +193,6 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) return len; } -static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - len = qemu_send_full(s->fd, buf, size, 0); - if (len < size) { - len = -socket_error(); - } - return len; -} - static int socket_close(void *opaque) { QEMUFileSocket *s = opaque; @@ -333,13 +296,13 @@ QEMUFile *qemu_popen_cmd(const char *command, const char *mode) FILE *stdio_file; QEMUFileStdio *s; - stdio_file = popen(command, mode); - if (stdio_file == NULL) { + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + fprintf(stderr, "qemu_popen: Argument validity check failed\n"); return NULL; } - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { - fprintf(stderr, "qemu_popen: Argument validity check failed\n"); + stdio_file = popen(command, mode); + if (stdio_file == NULL) { return NULL; } @@ -367,9 +330,94 @@ static const QEMUFileOps stdio_file_write_ops = { .close = stdio_fclose }; +static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len, offset; + ssize_t size = iov_size(iov, iovcnt); + ssize_t total = 0; + + assert(iovcnt > 0); + offset = 0; + while (size > 0) { + /* Find the next start position; skip all full-sized vector elements */ + while (offset >= iov[0].iov_len) { + offset -= iov[0].iov_len; + iov++, iovcnt--; + } + + /* skip `offset' bytes from the (now) first element, undo it on exit */ + assert(iovcnt > 0); + iov[0].iov_base += offset; + iov[0].iov_len -= offset; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && errno == EINTR); + if (len == -1) { + return -errno; + } + + /* Undo the changes above */ + iov[0].iov_base -= offset; + iov[0].iov_len += offset; + + /* Prepare for the next iteration */ + offset += len; + total += len; + size -= len; + } + + return total; +} + +static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = read(s->fd, buf, size); + if (len != -1) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (errno != EINTR) { + break; + } + } + + if (len == -1) { + len = -errno; + } + return len; +} + +static int unix_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + close(s->fd); + g_free(s); + return 0; +} + +static const QEMUFileOps unix_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = unix_get_buffer, + .close = unix_close +}; + +static const QEMUFileOps unix_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = unix_writev_buffer, + .close = unix_close +}; + QEMUFile *qemu_fdopen(int fd, const char *mode) { - QEMUFileStdio *s; + QEMUFileSocket *s; if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || @@ -378,21 +426,15 @@ QEMUFile *qemu_fdopen(int fd, const char *mode) return NULL; } - s = g_malloc0(sizeof(QEMUFileStdio)); - s->stdio_file = fdopen(fd, mode); - if (!s->stdio_file) - goto fail; + s = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; if(mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &stdio_file_read_ops); + s->file = qemu_fopen_ops(s, &unix_read_ops); } else { - s->file = qemu_fopen_ops(s, &stdio_file_write_ops); + s->file = qemu_fopen_ops(s, &unix_write_ops); } return s->file; - -fail: - g_free(s); - return NULL; } static const QEMUFileOps socket_read_ops = { @@ -403,22 +445,31 @@ static const QEMUFileOps socket_read_ops = { static const QEMUFileOps socket_write_ops = { .get_fd = socket_get_fd, - .put_buffer = socket_put_buffer, .writev_buffer = socket_writev_buffer, .close = socket_close }; -QEMUFile *qemu_fopen_socket(int fd, const char *mode) +bool qemu_file_mode_is_not_valid(const char *mode) { - QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket)); - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 'b' || mode[2] != 0) { fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + return true; + } + + return false; +} + +QEMUFile *qemu_fopen_socket(int fd, const char *mode) +{ + QEMUFileSocket *s; + + if (qemu_file_mode_is_not_valid(mode)) { return NULL; } + s = g_malloc0(sizeof(QEMUFileSocket)); s->fd = fd; if (mode[0] == 'w') { qemu_set_block(s->fd); @@ -433,10 +484,7 @@ QEMUFile *qemu_fopen(const char *filename, const char *mode) { QEMUFileStdio *s; - if (mode == NULL || - (mode[0] != 'r' && mode[0] != 'w') || - mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + if (qemu_file_mode_is_not_valid(mode)) { return NULL; } @@ -457,6 +505,21 @@ fail: return NULL; } +static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + int ret; + QEMUIOVector qiov; + + qemu_iovec_init_external(&qiov, iov, iovcnt); + ret = bdrv_writev_vmstate(opaque, &qiov, pos); + if (ret < 0) { + return ret; + } + + return qiov.size; +} + static int block_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { @@ -480,8 +543,9 @@ static const QEMUFileOps bdrv_read_ops = { }; static const QEMUFileOps bdrv_write_ops = { - .put_buffer = block_put_buffer, - .close = bdrv_fclose + .put_buffer = block_put_buffer, + .writev_buffer = block_writev_buffer, + .close = bdrv_fclose }; static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) @@ -502,6 +566,13 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) return f; } +/* + * Get last error for stream f + * + * Return negative error value if there has been an error on previous + * operations, return 0 if no error happened. + * + */ int qemu_file_get_error(QEMUFile *f) { return f->last_error; @@ -525,7 +596,7 @@ static inline bool qemu_file_is_writable(QEMUFile *f) * If there is writev_buffer QEMUFileOps it uses it otherwise uses * put_buffer ops. */ -static void qemu_fflush(QEMUFile *f) +void qemu_fflush(QEMUFile *f) { ssize_t ret = 0; @@ -535,7 +606,7 @@ static void qemu_fflush(QEMUFile *f) if (f->ops->writev_buffer) { if (f->iovcnt > 0) { - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt); + ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); } } else { if (f->buf_index > 0) { @@ -552,6 +623,65 @@ static void qemu_fflush(QEMUFile *f) } } +void ram_control_before_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->before_ram_iterate) { + ret = f->ops->before_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_after_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->after_ram_iterate) { + ret = f->ops->after_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_load_hook(QEMUFile *f, uint64_t flags) +{ + int ret = -EINVAL; + + if (f->ops->hook_ram_load) { + ret = f->ops->hook_ram_load(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } else { + qemu_file_set_error(f, ret); + } +} + +size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size, int *bytes_sent) +{ + if (f->ops->save_page) { + int ret = f->ops->save_page(f, f->opaque, block_offset, + offset, size, bytes_sent); + + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_sent && *bytes_sent > 0) { + qemu_update_position(f, *bytes_sent); + } else if (ret < 0) { + qemu_file_set_error(f, ret); + } + } + + return ret; + } + + return RAM_SAVE_CONTROL_NOT_SUPP; +} + static void qemu_fill_buffer(QEMUFile *f) { int len; @@ -585,6 +715,11 @@ int qemu_get_fd(QEMUFile *f) return -1; } +void qemu_update_position(QEMUFile *f, size_t size) +{ + f->pos += size; +} + /** Closes the file * * Returns negative error value if any error happened on previous operations or @@ -626,7 +761,7 @@ static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) f->iov[f->iovcnt++].iov_len = size; } - if (f->buf_index >= IO_BUF_SIZE || f->iovcnt >= MAX_IOV_SIZE) { + if (f->iovcnt >= MAX_IOV_SIZE) { qemu_fflush(f); } } @@ -662,12 +797,10 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) f->bytes_xfer += size; if (f->ops->writev_buffer) { add_to_iovec(f, f->buf + f->buf_index, l); - f->buf_index += l; - } else { - f->buf_index += l; - if (f->buf_index == IO_BUF_SIZE) { - qemu_fflush(f); - } + } + f->buf_index += l; + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); } if (qemu_file_get_error(f)) { break; @@ -687,12 +820,10 @@ void qemu_put_byte(QEMUFile *f, int v) f->bytes_xfer++; if (f->ops->writev_buffer) { add_to_iovec(f, f->buf + f->buf_index, 1); - f->buf_index++; - } else { - f->buf_index++; - if (f->buf_index == IO_BUF_SIZE) { - qemu_fflush(f); - } + } + f->buf_index++; + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); } } @@ -855,23 +986,23 @@ uint64_t qemu_get_be64(QEMUFile *f) /* timer */ -void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +void timer_put(QEMUFile *f, QEMUTimer *ts) { uint64_t expire_time; - expire_time = qemu_timer_expire_time_ns(ts); + expire_time = timer_expire_time_ns(ts); qemu_put_be64(f, expire_time); } -void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +void timer_get(QEMUFile *f, QEMUTimer *ts) { uint64_t expire_time; expire_time = qemu_get_be64(f); if (expire_time != -1) { - qemu_mod_timer_ns(ts, expire_time); + timer_mod_ns(ts, expire_time); } else { - qemu_del_timer(ts); + timer_del(ts); } } @@ -1215,14 +1346,14 @@ const VMStateInfo vmstate_info_float64 = { static int get_timer(QEMUFile *f, void *pv, size_t size) { QEMUTimer *v = pv; - qemu_get_timer(f, v); + timer_get(f, v); return 0; } static void put_timer(QEMUFile *f, void *pv, size_t size) { QEMUTimer *v = pv; - qemu_put_timer(f, v); + timer_put(f, v); } const VMStateInfo vmstate_info_timer = { @@ -2183,26 +2314,15 @@ out: return ret; } -static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, - const char *name) +static BlockDriverState *find_vmstate_bs(void) { - QEMUSnapshotInfo *sn_tab, *sn; - int nb_sns, i, ret; - - ret = -ENOENT; - nb_sns = bdrv_snapshot_list(bs, &sn_tab); - if (nb_sns < 0) - return ret; - for(i = 0; i < nb_sns; i++) { - sn = &sn_tab[i]; - if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { - *sn_info = *sn; - ret = 0; - break; + BlockDriverState *bs = NULL; + while ((bs = bdrv_next(bs))) { + if (bdrv_can_snapshot(bs)) { + return bs; } } - g_free(sn_tab); - return ret; + return NULL; } /* @@ -2212,18 +2332,21 @@ static int del_existing_snapshots(Monitor *mon, const char *name) { BlockDriverState *bs; QEMUSnapshotInfo sn1, *snapshot = &sn1; - int ret; + Error *err = NULL; bs = NULL; while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs) && bdrv_snapshot_find(bs, snapshot, name) >= 0) { - ret = bdrv_snapshot_delete(bs, name); - if (ret < 0) { + bdrv_snapshot_delete_by_id_or_name(bs, name, &err); + if (error_is_set(&err)) { monitor_printf(mon, - "Error while deleting snapshot on '%s'\n", - bdrv_get_device_name(bs)); + "Error while deleting snapshot on device '%s':" + " %s\n", + bdrv_get_device_name(bs), + error_get_pretty(err)); + error_free(err); return -1; } } @@ -2259,7 +2382,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) } } - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No block device can accept snapshots\n"); return; @@ -2274,7 +2397,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) qemu_gettimeofday(&tv); sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; - sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); + sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (name) { ret = bdrv_snapshot_find(bs, old_sn, name); @@ -2340,7 +2463,7 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp) f = qemu_fopen(filename, "wb"); if (!f) { - error_set(errp, QERR_OPEN_FILE_FAILED, filename); + error_setg_file_open(errp, errno, filename); goto the_end; } ret = qemu_save_device_state(f); @@ -2361,7 +2484,7 @@ int load_vmstate(const char *name) QEMUFile *f; int ret; - bs_vm_state = bdrv_snapshots(); + bs_vm_state = find_vmstate_bs(); if (!bs_vm_state) { error_report("No block device supports snapshots"); return -ENOTSUP; @@ -2437,10 +2560,10 @@ int load_vmstate(const char *name) void do_delvm(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; - int ret; + Error *err = NULL; const char *name = qdict_get_str(qdict, "name"); - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No block device supports snapshots\n"); return; @@ -2449,15 +2572,14 @@ void do_delvm(Monitor *mon, const QDict *qdict) bs1 = NULL; while ((bs1 = bdrv_next(bs1))) { if (bdrv_can_snapshot(bs1)) { - ret = bdrv_snapshot_delete(bs1, name); - if (ret < 0) { - if (ret == -ENOTSUP) - monitor_printf(mon, - "Snapshots not supported on device '%s'\n", - bdrv_get_device_name(bs1)); - else - monitor_printf(mon, "Error %d while deleting snapshot on " - "'%s'\n", ret, bdrv_get_device_name(bs1)); + bdrv_snapshot_delete_by_id_or_name(bs, name, &err); + if (error_is_set(&err)) { + monitor_printf(mon, + "Error while deleting snapshot on device '%s':" + " %s\n", + bdrv_get_device_name(bs), + error_get_pretty(err)); + error_free(err); } } } @@ -2470,9 +2592,8 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict) int nb_sns, i, ret, available; int total; int *available_snapshots; - char buf[256]; - bs = bdrv_snapshots(); + bs = find_vmstate_bs(); if (!bs) { monitor_printf(mon, "No available block device supports snapshots\n"); return; @@ -2513,10 +2634,12 @@ void do_info_snapshots(Monitor *mon, const QDict *qdict) } if (total > 0) { - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL); + monitor_printf(mon, "\n"); for (i = 0; i < total; i++) { sn = &sn_tab[available_snapshots[i]]; - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn); + monitor_printf(mon, "\n"); } } else { monitor_printf(mon, "There is no suitable snapshot available\n");