Skip to content

Commit

Permalink
migration: Allow postcopy_ram_supported_by_host() to report err
Browse files Browse the repository at this point in the history
Instead of print it to STDERR, bring the error upwards so that it can be
reported via QMP responses.

E.g.:

{ "execute": "migrate-set-capabilities" ,
  "arguments": { "capabilities":
  [ { "capability": "postcopy-ram", "state": true } ] } }

{ "error":
  { "class": "GenericError",
    "desc": "Postcopy is not supported: Host backend files need to be TMPFS
    or HUGETLBFS only" } }

Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
  • Loading branch information
xzpeter authored and Juan Quintela committed Apr 26, 2023
1 parent bf3e8f9 commit 6601591
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 34 deletions.
8 changes: 3 additions & 5 deletions migration/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
{
MigrationIncomingState *mis = migration_incoming_get_current();

ERRP_GUARD();
#ifndef CONFIG_LIVE_BLOCK_MIGRATION
if (new_caps[MIGRATION_CAPABILITY_BLOCK]) {
error_setg(errp, "QEMU compiled without old-style (blk/-b, inc/-i) "
Expand All @@ -327,11 +328,8 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
*/
if (!old_caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] &&
runstate_check(RUN_STATE_INMIGRATE) &&
!postcopy_ram_supported_by_host(mis)) {
/* postcopy_ram_supported_by_host will have emitted a more
* detailed message
*/
error_setg(errp, "Postcopy is not supported");
!postcopy_ram_supported_by_host(mis, errp)) {
error_prepend(errp, "Postcopy is not supported: ");
return false;
}

Expand Down
59 changes: 32 additions & 27 deletions migration/postcopy-ram.c
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
3 changes: 2 additions & 1 deletion migration/savevm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,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

0 comments on commit 6601591

Please sign in to comment.