static int buffered_close(void *opaque) { QEMUFileBuffered *s = opaque; ssize_t ret = 0; int ret2; DPRINTF("closing\n"); s->xfer_limit = INT_MAX; while (!qemu_file_get_error(s->file) && s->buffer_size) { ret = buffered_flush(s); if (ret < 0) { break; } if (s->freeze_output) { ret = migrate_fd_wait_for_unfreeze(s->migration_state); if (ret < 0) { break; } } } ret2 = migrate_fd_close(s->migration_state); if (ret >= 0) { ret = ret2; } qemu_del_timer(s->timer); qemu_free_timer(s->timer); g_free(s->buffer); g_free(s); return ret; }
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileBuffered *s = opaque; int offset = 0, error; ssize_t ret; DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); error = qemu_file_get_error(s->file); if (error) { DPRINTF("flush when error, bailing: %s\n", strerror(-error)); return error; } DPRINTF("unfreezing output\n"); s->freeze_output = 0; buffered_flush(s); while (!s->freeze_output && offset < size) { if (s->bytes_xfer > s->xfer_limit) { DPRINTF("transfer limit exceeded when putting\n"); break; } ret = s->put_buffer(s->opaque, buf + offset, size - offset); if (ret == -EAGAIN) { DPRINTF("backend not ready, freezing\n"); s->freeze_output = 1; break; } if (ret <= 0) { DPRINTF("error putting\n"); qemu_file_set_error(s->file, ret); offset = -EINVAL; break; } DPRINTF("put %zd byte(s)\n", ret); offset += ret; s->bytes_xfer += ret; } if (offset >= 0) { DPRINTF("buffering %d bytes\n", size - offset); buffered_append(s, buf + offset, size - offset); offset = size; } if (pos == 0 && size == 0) { DPRINTF("file is ready\n"); if (s->bytes_xfer <= s->xfer_limit) { DPRINTF("notifying client\n"); s->put_ready(s->opaque); } } return offset; }
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileBuffered *s = opaque; int offset = 0; ssize_t ret; DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); if (s->has_error) { DPRINTF("flush when error, bailing\n"); return -EINVAL; } DPRINTF("unfreezing output\n"); s->freeze_output = 0; buffered_flush(s); while (!s->freeze_output && offset < size) { if (s->bytes_xfer > s->xfer_limit) { DPRINTF("transfer limit exceeded when putting\n"); break; } ret = s->put_buffer(s->opaque, buf + offset, size - offset); if (ret == -EAGAIN) { DPRINTF("backend not ready, freezing\n"); s->freeze_output = 1; break; } if (ret <= 0) { DPRINTF("error putting\n"); s->has_error = 1; offset = -EINVAL; break; } DPRINTF("put %zd byte(s)\n", ret); offset += ret; s->bytes_xfer += ret; } if (offset >= 0) { DPRINTF("buffering %d bytes\n", size - offset); buffered_append(s, buf + offset, size - offset); offset = size; } return offset; }
QEMUFile *qemu_fopen_ops_buffered(void *opaque, size_t bytes_per_sec, BufferedPutFunc *put_buffer, BufferedPutReadyFunc *put_ready, BufferedWaitForUnfreezeFunc *wait_for_unfreeze, BufferedCloseFunc *close) { // dummy functions & dummy calls buffered_append(NULL, NULL, 0); buffered_flush(NULL); buffered_put_buffer(opaque, NULL, 0, 0); buffered_close(opaque); buffered_rate_limit(opaque); buffered_set_rate_limit(opaque, 0); buffered_get_rate_limit(opaque); buffered_rate_tick(opaque); return NULL; }
static void buffered_rate_tick(void *opaque) { QEMUFileBuffered *s = opaque; if (s->has_error) return; qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); if (s->freeze_output) return; s->bytes_xfer = 0; buffered_flush(s); /* Add some checks around this */ s->put_ready(s->opaque); }
static int buffered_close(void *opaque) { QEMUFileBuffered *s = opaque; int ret; DPRINTF("closing\n"); while (!s->has_error && s->buffer_size) { buffered_flush(s); if (s->freeze_output) s->wait_for_unfreeze(s); } ret = s->close(s->opaque); qemu_del_timer(s->timer); qemu_free_timer(s->timer); qemu_free(s->buffer); qemu_free(s); return ret; }
static int buffered_close(void *opaque) { MigrationState *s = opaque; ssize_t ret = 0; int ret2; DPRINTF("closing\n"); s->xfer_limit = INT_MAX; while (!qemu_file_get_error(s->file) && s->buffer_size) { ret = buffered_flush(s); if (ret < 0) { break; } } ret2 = migrate_fd_close(s); if (ret >= 0) { ret = ret2; } s->complete = true; return ret; }
static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) { QEMUFileBuffered *s = opaque; ssize_t error; DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); error = qemu_file_get_error(s->file); if (error) { DPRINTF("flush when error, bailing: %s\n", strerror(-error)); return error; } DPRINTF("unfreezing output\n"); s->freeze_output = 0; if (size > 0) { DPRINTF("buffering %d bytes\n", size); buffered_append(s, buf, size); } error = buffered_flush(s); if (error < 0) { DPRINTF("buffered flush error. bailing: %s\n", strerror(-error)); return error; } if (pos == 0 && size == 0) { DPRINTF("file is ready\n"); if (!s->freeze_output && s->bytes_xfer < s->xfer_limit) { DPRINTF("notifying client\n"); migrate_fd_put_ready(s->migration_state); } } return size; }
static void *buffered_file_thread(void *opaque) { MigrationState *s = opaque; int64_t initial_time = qemu_get_clock_ms(rt_clock); int64_t max_size = 0; bool last_round = false; int ret; qemu_mutex_lock_iothread(); DPRINTF("beginning savevm\n"); ret = qemu_savevm_state_begin(s->file, &s->params); if (ret < 0) { DPRINTF("failed, %d\n", ret); qemu_mutex_unlock_iothread(); goto out; } qemu_mutex_unlock_iothread(); while (true) { int64_t current_time = qemu_get_clock_ms(rt_clock); uint64_t pending_size; qemu_mutex_lock_iothread(); if (s->state != MIG_STATE_ACTIVE) { DPRINTF("put_ready returning because of non-active state\n"); qemu_mutex_unlock_iothread(); break; } if (s->complete) { qemu_mutex_unlock_iothread(); break; } if (s->bytes_xfer < s->xfer_limit) { DPRINTF("iterate\n"); pending_size = qemu_savevm_state_pending(s->file, max_size); DPRINTF("pending size %lu max %lu\n", pending_size, max_size); if (pending_size && pending_size >= max_size) { ret = qemu_savevm_state_iterate(s->file); if (ret < 0) { qemu_mutex_unlock_iothread(); break; } } else { int old_vm_running = runstate_is_running(); int64_t start_time, end_time; DPRINTF("done iterating\n"); start_time = qemu_get_clock_ms(rt_clock); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); if (old_vm_running) { vm_stop(RUN_STATE_FINISH_MIGRATE); } else { vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); } ret = qemu_savevm_state_complete(s->file); if (ret < 0) { qemu_mutex_unlock_iothread(); break; } else { migrate_fd_completed(s); } end_time = qemu_get_clock_ms(rt_clock); s->total_time = end_time - s->total_time; s->downtime = end_time - start_time; if (s->state != MIG_STATE_COMPLETED) { if (old_vm_running) { vm_start(); } } last_round = true; } } qemu_mutex_unlock_iothread(); if (current_time >= initial_time + BUFFER_DELAY) { uint64_t transferred_bytes = s->bytes_xfer; uint64_t time_spent = current_time - initial_time; double bandwidth = transferred_bytes / time_spent; max_size = bandwidth * migrate_max_downtime() / 1000000; DPRINTF("transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 "\n", transferred_bytes, time_spent, bandwidth, max_size); s->bytes_xfer = 0; initial_time = current_time; } if (!last_round && (s->bytes_xfer >= s->xfer_limit)) { /* usleep expects microseconds */ g_usleep((initial_time + BUFFER_DELAY - current_time)*1000); } ret = buffered_flush(s); if (ret < 0) { break; } } out: if (ret < 0) { migrate_fd_error(s); } g_free(s->buffer); return NULL; }