qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] hostmem-file: add a property 'notrunc' to avoid data corruption
@ 2016-10-20  6:13 Haozhong Zhang
  2016-10-20  6:35 ` no-reply
  2016-10-20 12:34 ` Igor Mammedov
  0 siblings, 2 replies; 43+ messages in thread
From: Haozhong Zhang @ 2016-10-20  6:13 UTC (permalink / raw)
  To: qemu-devel
  Cc: Eduardo Habkost, Igor Mammedov, Paolo Bonzini, Peter Crosthwaite,
	Richard Henderson, Xiao Guangrong, Haozhong Zhang

If a file is used as the backend of memory-backend-file and its size is
not identical to the property 'size', the file will be truncated. For a
file used as the backend of vNVDIMM, its data is expected to be
persistent and the truncation may corrupt the existing data.

A property 'notrunc' is added to memory-backend-file to allow users to
control the truncation. If 'notrunc' is on, QEMU will not truncate the
backend file and require the property 'size' is not larger than the file
size. If 'notrunc' is off, QEMU will behave as before.

Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
---
 backends/hostmem-file.c | 25 ++++++++++++++++-
 exec.c                  | 74 +++++++++++++++++++++++++++++++++++++++++++++++--
 include/exec/memory.h   |  3 ++
 include/exec/ram_addr.h |  3 +-
 memory.c                |  4 ++-
 numa.c                  |  2 +-
 6 files changed, 105 insertions(+), 6 deletions(-)

diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c
index 42efb2f..c2df14c 100644
--- a/backends/hostmem-file.c
+++ b/backends/hostmem-file.c
@@ -32,6 +32,7 @@ struct HostMemoryBackendFile {
     HostMemoryBackend parent_obj;
 
     bool share;
+    bool notrunc;
     char *mem_path;
 };
 
@@ -57,7 +58,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
         path = object_get_canonical_path(OBJECT(backend));
         memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
                                  path,
-                                 backend->size, fb->share,
+                                 backend->size, fb->share, fb->notrunc,
                                  fb->mem_path, errp);
         g_free(path);
     }
@@ -103,6 +104,25 @@ static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
     fb->share = value;
 }
 
+static bool file_memory_backend_get_notrunc(Object *o, Error **errp)
+{
+    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+
+    return fb->notrunc;
+}
+
+static void file_memory_backend_set_notrunc(Object *o, bool value, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(o);
+    HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+
+    if (memory_region_size(&backend->mr)) {
+        error_setg(errp, "cannot change property value");
+        return;
+    }
+    fb->notrunc = value;
+}
+
 static void
 file_backend_class_init(ObjectClass *oc, void *data)
 {
@@ -116,6 +136,9 @@ file_backend_class_init(ObjectClass *oc, void *data)
     object_class_property_add_str(oc, "mem-path",
         get_mem_path, set_mem_path,
         &error_abort);
+    object_class_property_add_bool(oc, "notrunc",
+        file_memory_backend_get_notrunc, file_memory_backend_set_notrunc,
+        &error_abort);
 }
 
 static void file_backend_instance_finalize(Object *o)
diff --git a/exec.c b/exec.c
index e63c5a1..aacaee1 100644
--- a/exec.c
+++ b/exec.c
@@ -63,6 +63,11 @@
 #include "qemu/mmap-alloc.h"
 #endif
 
+#include <sys/ioctl.h>
+#ifdef CONFIG_LINUX
+#include <linux/fs.h>
+#endif
+
 //#define DEBUG_SUBPAGE
 
 #if !defined(CONFIG_USER_ONLY)
@@ -91,6 +96,12 @@ static MemoryRegion io_mem_unassigned;
  */
 #define RAM_RESIZEABLE (1 << 2)
 
+/* If the backend of RAM is a file and the RAM size is not identical
+ * to the file size, do not truncate the file to the RAM size. This
+ * flag is used to avoid corrupting the existing data on the file.
+ */
+#define RAM_NOTRUNC    (1 << 3)
+
 #endif
 
 struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
@@ -1188,6 +1199,48 @@ void qemu_mutex_unlock_ramlist(void)
 }
 
 #ifdef __linux__
+static uint64_t get_file_size(const char *path, Error **errp)
+{
+    int fd;
+    struct stat st;
+    uint64_t size = 0;
+    Error *local_err = NULL;
+
+    fd = qemu_open(path, O_RDONLY);
+    if (fd < 0) {
+        error_setg(&local_err, "cannot open file");
+        goto out;
+    }
+
+    if (stat(path, &st)) {
+        error_setg(&local_err, "cannot get file stat");
+        goto out_fclose;
+    }
+
+    switch (st.st_mode & S_IFMT) {
+    case S_IFREG:
+        size = st.st_size;
+        break;
+
+    case S_IFBLK:
+        if (ioctl(fd, BLKGETSIZE64, &size)) {
+            error_setg(&local_err, "cannot get size of block device");
+            size = 0;
+        }
+        break;
+
+    default:
+        error_setg(&local_err,
+                   "only block device on Linux and regular file are supported");
+    }
+
+ out_fclose:
+    close(fd);
+ out:
+    error_propagate(errp, local_err);
+    return size;
+}
+
 static void *file_ram_alloc(RAMBlock *block,
                             ram_addr_t memory,
                             const char *path,
@@ -1271,7 +1324,7 @@ static void *file_ram_alloc(RAMBlock *block,
      * If anything goes wrong with it under other filesystems,
      * mmap will fail.
      */
-    if (ftruncate(fd, memory)) {
+    if (!(block->flags & RAM_NOTRUNC) && ftruncate(fd, memory)) {
         perror("ftruncate");
     }
 
@@ -1597,7 +1650,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
 
 #ifdef __linux__
 RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                   bool share, const char *mem_path,
+                                   bool share, bool notrunc,
+                                   const char *mem_path,
                                    Error **errp)
 {
     RAMBlock *new_block;
@@ -1619,12 +1673,28 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
         return NULL;
     }
 
+    if (notrunc) {
+        uint64_t file_size = get_file_size(mem_path, &local_err);
+
+        if (local_err) {
+            error_propagate(errp, local_err);
+            return NULL;
+        }
+
+        if (size > file_size) {
+            error_setg(errp,
+                       "notrunc enabled, but size is larger than file size");
+            return NULL;
+        }
+    }
+
     size = HOST_PAGE_ALIGN(size);
     new_block = g_malloc0(sizeof(*new_block));
     new_block->mr = mr;
     new_block->used_length = size;
     new_block->max_length = size;
     new_block->flags = share ? RAM_SHARED : 0;
+    new_block->flags |= notrunc ? RAM_NOTRUNC : 0;
     new_block->host = file_ram_alloc(new_block, size,
                                      mem_path, errp);
     if (!new_block->host) {
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 10d7eac..c0116e1 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -418,6 +418,8 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
  * @name: the name of the region.
  * @size: size of the region.
  * @share: %true if memory must be mmaped with the MAP_SHARED flag
+ * @notrunc: %true do not truncate the file @path to @size if the
+ *           file size is not identical to @size
  * @path: the path in which to allocate the RAM.
  * @errp: pointer to Error*, to store an error if it happens.
  */
@@ -426,6 +428,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       const char *name,
                                       uint64_t size,
                                       bool share,
+                                      bool notrunc,
                                       const char *path,
                                       Error **errp);
 #endif
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index 54d7108..4fd3ec8 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -96,7 +96,8 @@ void qemu_mutex_lock_ramlist(void);
 void qemu_mutex_unlock_ramlist(void);
 
 RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
-                                   bool share, const char *mem_path,
+                                   bool share, bool notrunc,
+                                   const char *mem_path,
                                    Error **errp);
 RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
                                   MemoryRegion *mr, Error **errp);
diff --git a/memory.c b/memory.c
index 58f9269..5430412 100644
--- a/memory.c
+++ b/memory.c
@@ -1334,6 +1334,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
                                       const char *name,
                                       uint64_t size,
                                       bool share,
+                                      bool trunc,
                                       const char *path,
                                       Error **errp)
 {
@@ -1341,7 +1342,8 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
     mr->ram = true;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_ram;
-    mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp);
+    mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, trunc, path,
+                                             errp);
     mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
 }
 #endif
diff --git a/numa.c b/numa.c
index 9c09e45..6cf1fb5 100644
--- a/numa.c
+++ b/numa.c
@@ -412,7 +412,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner,
 #ifdef __linux__
         Error *err = NULL;
         memory_region_init_ram_from_file(mr, owner, name, ram_size, false,
-                                         mem_path, &err);
+                                         false, mem_path, &err);
         if (err) {
             error_report_err(err);
             if (mem_prealloc) {
-- 
2.10.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2016-10-25 10:01 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-20  6:13 [Qemu-devel] [PATCH] hostmem-file: add a property 'notrunc' to avoid data corruption Haozhong Zhang
2016-10-20  6:35 ` no-reply
2016-10-20 12:34 ` Igor Mammedov
2016-10-20 13:11   ` Haozhong Zhang
2016-10-20 13:34     ` Eduardo Habkost
2016-10-20 13:47       ` Haozhong Zhang
2016-10-20 13:42     ` Igor Mammedov
2016-10-20 13:56       ` Eduardo Habkost
2016-10-20 14:15         ` Igor Mammedov
2016-10-20 14:47           ` Eduardo Habkost
2016-10-20 15:35             ` Igor Mammedov
2016-10-20 16:56               ` Eduardo Habkost
2016-10-21  9:31                 ` Igor Mammedov
2016-10-21 11:53                   ` Eduardo Habkost
2016-10-21 13:26                     ` Igor Mammedov
2016-10-21  7:22               ` Haozhong Zhang
2016-10-21 11:07                 ` Igor Mammedov
2016-10-21 11:25                   ` Haozhong Zhang
2016-10-21 11:56                   ` Eduardo Habkost
2016-10-20 14:22         ` Haozhong Zhang
2016-10-20 15:14           ` Eduardo Habkost
2016-10-20 13:56       ` Haozhong Zhang
2016-10-20 13:21   ` Eduardo Habkost
2016-10-20 13:33     ` Haozhong Zhang
2016-10-20 13:47       ` Eduardo Habkost
2016-10-20 14:17         ` Igor Mammedov
2016-10-20 15:15           ` Eduardo Habkost
2016-10-20 15:41             ` Igor Mammedov
2016-10-20 16:59               ` Eduardo Habkost
2016-10-21 10:28                 ` Igor Mammedov
2016-10-21 11:44                   ` Eduardo Habkost
2016-10-20 13:47       ` Igor Mammedov
2016-10-20 13:57         ` Eduardo Habkost
2016-10-20 14:18           ` Igor Mammedov
2016-10-20 15:00             ` Eduardo Habkost
2016-10-20 15:14               ` Igor Mammedov
2016-10-20 13:27   ` Daniel P. Berrange
2016-10-20 13:40     ` Eduardo Habkost
2016-10-20 13:54     ` Igor Mammedov
2016-10-20 13:55   ` Kevin Wolf
2016-10-24 13:10     ` Eduardo Habkost
2016-10-25  6:42       ` Haozhong Zhang
2016-10-25 10:01         ` Eduardo Habkost

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).