qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] hostmem-file: add a property 'notrunc' to avoid dat


From: Haozhong Zhang
Subject: [Qemu-devel] [PATCH] hostmem-file: add a property 'notrunc' to avoid data corruption
Date: Thu, 20 Oct 2016 14:13:01 +0800

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 <address@hidden>
---
 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]