CPR with x-ignore-shared can skip migration of shared RAM pages, so only
vmstate is transferred. This relies on guest RAM being externally managed
and re-used in place.

Allow users to pass a pre-opened RAM backing FD via -add-fd and attach it
with memory-backend-file mem-path=/dev/fdset/<id>. File-backed RAM used
plain open()/creat(), so /dev/fdset/<id> could not be resolved through the
fdset mechanism. Switch to qemu_open()/qemu_create(), return -EINVAL when
creation is attempted, and add a hint for missing -add-fd. Add a qtest that
creates a memory-backend-file object from /dev/fdset/<id>.

Signed-off-by: Li Chen <[email protected]>
---
 system/physmem.c                  | 17 ++++++++++++--
 tests/qtest/migration/cpr-tests.c | 38 ++++++++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/system/physmem.c b/system/physmem.c
index c9869e4049..7717de5e7b 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -1650,10 +1650,14 @@ static int file_ram_open(const char *path,
     char *sanitized_name;
     char *c;
     int fd = -1;
+    const char *tmp;
+    const bool is_fdset = strstart(path, "/dev/fdset/", &tmp);
 
     *created = false;
     for (;;) {
-        fd = open(path, readonly ? O_RDONLY : O_RDWR);
+        g_autoptr(Error) local_err = NULL;
+
+        fd = qemu_open(path, readonly ? O_RDONLY : O_RDWR, &local_err);
         if (fd >= 0) {
             /*
              * open(O_RDONLY) won't fail with EISDIR. Check manually if we
@@ -1682,8 +1686,13 @@ static int file_ram_open(const char *path,
                 /* Refuse to create new, readonly files. */
                 return -ENOENT;
             }
+            if (is_fdset) {
+                /* /dev/fdset/N is a QEMU fdset handle and cannot be created. 
*/
+                return -EINVAL;
+            }
             /* @path names a file that doesn't exist, create it */
-            fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
+            local_err = NULL;
+            fd = qemu_create(path, O_RDWR | O_EXCL, 0644, &local_err);
             if (fd >= 0) {
                 *created = true;
                 break;
@@ -2410,6 +2419,10 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, 
MemoryRegion *mr,
     if (fd < 0) {
         error_setg_errno(errp, -fd, "can't open backing store %s for guest 
RAM",
                          mem_path);
+        if (g_str_has_prefix(mem_path, "/dev/fdset/")) {
+            error_append_hint(errp, "Did you forget to pass the backing FD via"
+                              " '-add-fd fd=<n>,set=<id>'?\n");
+        }
         if (!(ram_flags & RAM_READONLY_FD) && !(ram_flags & RAM_SHARED) &&
             fd == -EACCES) {
             /*
diff --git a/tests/qtest/migration/cpr-tests.c 
b/tests/qtest/migration/cpr-tests.c
index 2a186c6f35..5eafc4d678 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -19,6 +19,39 @@
 
 static char *tmpfs;
 
+#ifndef _WIN32
+static void test_mem_backend_file_fdset(void)
+{
+    const uint64_t size = 8 * 1024 * 1024;
+    g_autofree char *file = g_strdup_printf("%s/fdset-ram.bin", tmpfs);
+    QTestState *qts;
+    int fd;
+
+    fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    g_assert_cmpint(fd, >=, 0);
+    g_assert_cmpint(ftruncate(fd, size), ==, 0);
+
+    qts = qtest_init("-machine none -nodefaults");
+    qtest_qmp_fds_assert_success(qts, &fd, 1,
+                                 "{'execute': 'add-fd',"
+                                 " 'arguments': { 'fdset-id': 1 } }");
+    close(fd);
+
+    qtest_qmp_assert_success(qts, "{ 'execute': 'object-add',"
+                             "  'arguments': {"
+                             "    'qom-type': 'memory-backend-file',"
+                             "    'id': 'ram0',"
+                             "    'mem-path': '/dev/fdset/1',"
+                             "    'size': %" PRIu64 " } }",
+                             size);
+    qtest_qmp_assert_success(qts, "{ 'execute': 'object-del',"
+                             "  'arguments': { 'id': 'ram0' } }");
+    qtest_quit(qts);
+
+    unlink(file);
+}
+#endif
+
 static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to)
 {
     migrate_set_parameter_str(from, "mode", "cpr-reboot");
@@ -247,7 +280,10 @@ void migration_test_add_cpr(MigrationTestEnv *env)
 {
     tmpfs = env->tmpfs;
 
-    /* no tests in the smoke set for now */
+#ifndef _WIN32
+    migration_test_add("/migration/fdset/mem-backend-file",
+                       test_mem_backend_file_fdset);
+#endif
 
     if (!env->full_set) {
         return;
-- 
2.52.0


Reply via email to