429 changes: 0 additions & 429 deletions migration/migration.c

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions migration/migration.h
Expand Up @@ -447,8 +447,6 @@ bool migration_is_blocked(Error **errp);
bool migration_in_postcopy(void);
MigrationState *migrate_get_current(void);

int migrate_use_tls(void);

uint64_t ram_get_total_transferred_pages(void);

/* Sending on the return path - generic and then for each message type */
Expand Down
442 changes: 436 additions & 6 deletions migration/options.c

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions migration/options.h
Expand Up @@ -14,6 +14,13 @@
#ifndef QEMU_MIGRATION_OPTIONS_H
#define QEMU_MIGRATION_OPTIONS_H

/* constants */

/* Amount of time to allocate to each "chunk" of bandwidth-throttled
* data. */
#define BUFFER_DELAY 100
#define XFER_LIMIT_RATIO (1000 / BUFFER_DELAY)

/* capabilities */

bool migrate_auto_converge(void);
Expand Down Expand Up @@ -46,6 +53,7 @@ bool migrate_zero_copy_send(void);
*/

bool migrate_postcopy(void);
bool migrate_tls(void);

/* capabilities helpers */

Expand Down Expand Up @@ -73,4 +81,8 @@ int migrate_multifd_zstd_level(void);
uint8_t migrate_throttle_trigger_threshold(void);
uint64_t migrate_xbzrle_cache_size(void);

/* parameters helpers */

bool migrate_params_check(MigrationParameters *params, Error **errp);

#endif
59 changes: 32 additions & 27 deletions migration/postcopy-ram.c
Expand Up @@ -283,19 +283,21 @@ static bool request_ufd_features(int ufd, uint64_t features)
return true;
}

static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis)
static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis,
Error **errp)
{
uint64_t asked_features = 0;
static uint64_t supported_features;

ERRP_GUARD();
/*
* it's not possible to
* request UFFD_API twice per one fd
* userfault fd features is persistent
*/
if (!supported_features) {
if (!receive_ufd_features(&supported_features)) {
error_report("%s failed", __func__);
error_setg(errp, "Userfault feature detection failed");
return false;
}
}
Expand All @@ -317,8 +319,7 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis)
* userfault file descriptor
*/
if (!request_ufd_features(ufd, asked_features)) {
error_report("%s failed: features %" PRIu64, __func__,
asked_features);
error_setg(errp, "Failed features %" PRIu64, asked_features);
return false;
}

Expand All @@ -329,7 +330,8 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis)
have_hp = supported_features & UFFD_FEATURE_MISSING_HUGETLBFS;
#endif
if (!have_hp) {
error_report("Userfault on this host does not support huge pages");
error_setg(errp,
"Userfault on this host does not support huge pages");
return false;
}
}
Expand All @@ -338,24 +340,26 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis)

/* Callback from postcopy_ram_supported_by_host block iterator.
*/
static int test_ramblock_postcopiable(RAMBlock *rb)
static int test_ramblock_postcopiable(RAMBlock *rb, Error **errp)
{
const char *block_name = qemu_ram_get_idstr(rb);
ram_addr_t length = qemu_ram_get_used_length(rb);
size_t pagesize = qemu_ram_pagesize(rb);
QemuFsType fs;

if (length % pagesize) {
error_report("Postcopy requires RAM blocks to be a page size multiple,"
" block %s is 0x" RAM_ADDR_FMT " bytes with a "
"page size of 0x%zx", block_name, length, pagesize);
error_setg(errp,
"Postcopy requires RAM blocks to be a page size multiple,"
" block %s is 0x" RAM_ADDR_FMT " bytes with a "
"page size of 0x%zx", block_name, length, pagesize);
return 1;
}

if (rb->fd >= 0) {
fs = qemu_fd_getfs(rb->fd);
if (fs != QEMU_FS_TYPE_TMPFS && fs != QEMU_FS_TYPE_HUGETLBFS) {
error_report("Host backend files need to be TMPFS or HUGETLBFS only");
error_setg(errp,
"Host backend files need to be TMPFS or HUGETLBFS only");
return 1;
}
}
Expand All @@ -368,7 +372,8 @@ static int test_ramblock_postcopiable(RAMBlock *rb)
* normally fine since if the postcopy succeeds it gets turned back on at the
* end.
*/
bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
bool postcopy_ram_supported_by_host(MigrationIncomingState *mis,
Error **errp)
{
long pagesize = qemu_real_host_page_size();
int ufd = -1;
Expand All @@ -377,29 +382,27 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
struct uffdio_register reg_struct;
struct uffdio_range range_struct;
uint64_t feature_mask;
Error *local_err = NULL;
RAMBlock *block;

ERRP_GUARD();
if (qemu_target_page_size() > pagesize) {
error_report("Target page size bigger than host page size");
error_setg(errp, "Target page size bigger than host page size");
goto out;
}

ufd = uffd_open(O_CLOEXEC);
if (ufd == -1) {
error_report("%s: userfaultfd not available: %s", __func__,
strerror(errno));
error_setg(errp, "Userfaultfd not available: %s", strerror(errno));
goto out;
}

/* Give devices a chance to object */
if (postcopy_notify(POSTCOPY_NOTIFY_PROBE, &local_err)) {
error_report_err(local_err);
if (postcopy_notify(POSTCOPY_NOTIFY_PROBE, errp)) {
goto out;
}

/* Version and features check */
if (!ufd_check_and_apply(ufd, mis)) {
if (!ufd_check_and_apply(ufd, mis, errp)) {
goto out;
}

Expand All @@ -417,7 +420,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
* affect in reality, or we can revisit.
*/
RAMBLOCK_FOREACH(block) {
if (test_ramblock_postcopiable(block)) {
if (test_ramblock_postcopiable(block, errp)) {
goto out;
}
}
Expand All @@ -427,7 +430,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
* it was enabled.
*/
if (munlockall()) {
error_report("%s: munlockall: %s", __func__, strerror(errno));
error_setg(errp, "munlockall() failed: %s", strerror(errno));
goto out;
}

Expand All @@ -439,8 +442,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
testarea = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE |
MAP_ANONYMOUS, -1, 0);
if (testarea == MAP_FAILED) {
error_report("%s: Failed to map test area: %s", __func__,
strerror(errno));
error_setg(errp, "Failed to map test area: %s", strerror(errno));
goto out;
}
g_assert(QEMU_PTR_IS_ALIGNED(testarea, pagesize));
Expand All @@ -450,23 +452,23 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;

if (ioctl(ufd, UFFDIO_REGISTER, &reg_struct)) {
error_report("%s userfault register: %s", __func__, strerror(errno));
error_setg(errp, "UFFDIO_REGISTER failed: %s", strerror(errno));
goto out;
}

range_struct.start = (uintptr_t)testarea;
range_struct.len = pagesize;
if (ioctl(ufd, UFFDIO_UNREGISTER, &range_struct)) {
error_report("%s userfault unregister: %s", __func__, strerror(errno));
error_setg(errp, "UFFDIO_UNREGISTER failed: %s", strerror(errno));
goto out;
}

feature_mask = (__u64)1 << _UFFDIO_WAKE |
(__u64)1 << _UFFDIO_COPY |
(__u64)1 << _UFFDIO_ZEROPAGE;
if ((reg_struct.ioctls & feature_mask) != feature_mask) {
error_report("Missing userfault map features: %" PRIx64,
(uint64_t)(~reg_struct.ioctls & feature_mask));
error_setg(errp, "Missing userfault map features: %" PRIx64,
(uint64_t)(~reg_struct.ioctls & feature_mask));
goto out;
}

Expand Down Expand Up @@ -1188,6 +1190,8 @@ static int postcopy_temp_pages_setup(MigrationIncomingState *mis)

int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
Error *local_err = NULL;

/* Open the fd for the kernel to give us userfaults */
mis->userfault_fd = uffd_open(O_CLOEXEC | O_NONBLOCK);
if (mis->userfault_fd == -1) {
Expand All @@ -1200,7 +1204,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
* Although the host check already tested the API, we need to
* do the check again as an ABI handshake on the new fd.
*/
if (!ufd_check_and_apply(mis->userfault_fd, mis)) {
if (!ufd_check_and_apply(mis->userfault_fd, mis, &local_err)) {
error_report_err(local_err);
return -1;
}

Expand Down
3 changes: 2 additions & 1 deletion migration/postcopy-ram.h
Expand Up @@ -14,7 +14,8 @@
#define QEMU_POSTCOPY_RAM_H

/* Return true if the host supports everything we need to do postcopy-ram */
bool postcopy_ram_supported_by_host(MigrationIncomingState *mis);
bool postcopy_ram_supported_by_host(MigrationIncomingState *mis,
Error **errp);

/*
* Make all of RAM sensitive to accesses to areas that haven't yet been written
Expand Down
6 changes: 5 additions & 1 deletion migration/savevm.c
Expand Up @@ -536,6 +536,9 @@ static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field,
field->version_id);
fprintf(out_file, "%*s\"field_exists\": %s,\n", indent, "",
field->field_exists ? "true" : "false");
if (field->flags & VMS_ARRAY) {
fprintf(out_file, "%*s\"num\": %d,\n", indent, "", field->num);
}
fprintf(out_file, "%*s\"size\": %zu", indent, "", field->size);
if (field->vmsd != NULL) {
fprintf(out_file, ",\n");
Expand Down Expand Up @@ -1753,7 +1756,8 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
return -EINVAL;
}

if (!postcopy_ram_supported_by_host(mis)) {
if (!postcopy_ram_supported_by_host(mis, &local_err)) {
error_report_err(local_err);
postcopy_state_set(POSTCOPY_INCOMING_NONE);
return -1;
}
Expand Down
3 changes: 2 additions & 1 deletion migration/tls.c
Expand Up @@ -22,6 +22,7 @@
#include "channel.h"
#include "migration.h"
#include "tls.h"
#include "options.h"
#include "crypto/tlscreds.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
Expand Down Expand Up @@ -165,7 +166,7 @@ void migration_tls_channel_connect(MigrationState *s,

bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc)
{
if (!migrate_use_tls()) {
if (!migrate_tls()) {
return false;
}

Expand Down
13 changes: 10 additions & 3 deletions scripts/vmstate-static-checker.py
Expand Up @@ -134,6 +134,11 @@ def exists_in_substruct(fields, item):
return check_fields_match(fields["Description"]["name"],
substruct_fields[0]["field"], item)

def size_total(entry):
size = entry["size"]
if "num" not in entry:
return size
return size * entry["num"]

def check_fields(src_fields, dest_fields, desc, sec):
# This function checks for all the fields in a section. If some
Expand Down Expand Up @@ -249,17 +254,19 @@ def check_fields(src_fields, dest_fields, desc, sec):
continue

if s_item["field"] == "unused" or d_item["field"] == "unused":
if s_item["size"] == d_item["size"]:
s_size = size_total(s_item)
d_size = size_total(d_item)
if s_size == d_size:
continue

if d_item["field"] == "unused":
advance_dest = False
unused_count = d_item["size"] - s_item["size"]
unused_count = d_size - s_size;
continue

if s_item["field"] == "unused":
advance_src = False
unused_count = s_item["size"] - d_item["size"]
unused_count = s_size - d_size
continue

print("Section \"" + sec + "\",", end=' ')
Expand Down