[CRIU] [PATCH] restore: Don't open same files to mmap mutiple times (v2)

Pavel Emelyanov xemul at virtuozzo.com
Fri Apr 14 05:54:39 PDT 2017


This is symmetrical to dump -- it's typical when one file
is mapped several times in a row. On dump we don't open
the mapping's map_files links for each of such files, thus
it makes sence to do the same on restore.

This speeds restore a little bit due to lower amount of
openat() and close() calls.

v2: Sometimes cached/merged descriptors were closed when they
    were not supposed to.

Signed-off-by: Pavel Emelyanov <xemul at virtuozzo.com>
---
 criu/files-reg.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++---
 criu/include/files-reg.h |  2 +-
 criu/include/vma.h       |  1 +
 criu/mem.c               | 10 ++++----
 criu/pie/restorer.c      | 26 ++++++++++++++++----
 5 files changed, 88 insertions(+), 13 deletions(-)

diff --git a/criu/files-reg.c b/criu/files-reg.c
index 4514275..539f2ee 100644
--- a/criu/files-reg.c
+++ b/criu/files-reg.c
@@ -1616,6 +1616,30 @@ int open_reg_by_id(u32 id)
 	return open_reg_fd(fd);
 }
 
+/*
+ * In many cases subsequent areas have the same file mapped,
+ * so we may save some open-s when mapping them and map the
+ * same descriptor many times (provided fdflags match).
+ */
+static struct {
+	struct file_desc *d;
+	u32 flags;
+	int fd;
+} prev_filemap;
+
+void filemap_fin_opens(void)
+{
+	if (prev_filemap.d) {
+		close(prev_filemap.fd);
+		prev_filemap.d = NULL;
+	}
+}
+
+static inline bool prev_open_match(struct vma_area *vma, u32 flags)
+{
+	return (prev_filemap.d == vma->vmfd) && (prev_filemap.flags == flags);
+}
+
 static int open_filemap(int pid, struct vma_area *vma)
 {
 	u32 flags;
@@ -1636,14 +1660,45 @@ static int open_filemap(int pid, struct vma_area *vma)
 	else
 		flags = O_RDONLY;
 
-	ret = open_path(vma->vmfd, do_open_reg_noseek_flags, &flags);
-	if (ret < 0)
-		return ret;
+	if (prev_open_match(vma, flags)) {
+		BUG_ON(prev_filemap.fd < 0);
+		ret = prev_filemap.fd;
+	} else {
+		ret = open_path(vma->vmfd, do_open_reg_noseek_flags, &flags);
+		if (ret < 0)
+			return ret;
+	}
 
 	vma->e->fd = ret;
 	return 0;
 }
 
+static void close_filemap(struct vma_area *vma)
+{
+	if (unlikely(!vma->e->has_fdflags)) {
+		/*
+		 * For fdflags-less images ... ah, don't care.
+		 */
+		close(vma->e->fd);
+		goto out;
+	}
+
+	if (prev_open_match(vma, vma->e->fdflags))
+		/*
+		 * Keep opened fd for future ->vm_open-s
+		 */
+		goto out;
+
+	if (prev_filemap.d)
+		close(prev_filemap.fd);
+
+	prev_filemap.d = vma->vmfd;
+	prev_filemap.flags = vma->e->fdflags;
+	prev_filemap.fd = vma->e->fd;
+out:
+	vma->e->fd = -1;
+}
+
 int collect_filemap(struct vma_area *vma)
 {
 	struct file_desc *fd;
@@ -1654,6 +1709,7 @@ int collect_filemap(struct vma_area *vma)
 
 	vma->vmfd = fd;
 	vma->vm_open = open_filemap;
+	vma->vm_close = close_filemap;
 	return 0;
 }
 
diff --git a/criu/include/files-reg.h b/criu/include/files-reg.h
index 5a6c691..5ef5e88 100644
--- a/criu/include/files-reg.h
+++ b/criu/include/files-reg.h
@@ -52,7 +52,7 @@ extern int prepare_remaps(void);
 extern int try_clean_remaps(bool only_ghosts);
 
 extern int strip_deleted(struct fd_link *link);
-
+extern void filemap_fin_opens(void);
 extern int prepare_procfs_remaps(void);
 extern int dead_pid_conflict(void);
 
diff --git a/criu/include/vma.h b/criu/include/vma.h
index 37f8b4b..5134505 100644
--- a/criu/include/vma.h
+++ b/criu/include/vma.h
@@ -53,6 +53,7 @@ struct vma_area {
 
 		struct /* for restore */ {
 			int (*vm_open)(int pid, struct vma_area *vma);
+			void (*vm_close)(struct vma_area *vma);
 			struct file_desc *vmfd;
 			unsigned long	*page_bitmap;	/* existent pages */
 			unsigned long	*ppage_bitmap;	/* parent's existent pages */
diff --git a/criu/mem.c b/criu/mem.c
index 3bcf467..56d775d 100644
--- a/criu/mem.c
+++ b/criu/mem.c
@@ -568,8 +568,6 @@ static int map_private_vma(struct pstree_item *t,
 			pr_err("Can't fixup VMA's fd\n");
 			return -1;
 		}
-
-		vma->vm_open = NULL; /* prevent from 2nd open in prepare_vmas */
 	}
 
 	nr_pages = vma_entry_len(vma->e) / PAGE_SIZE;
@@ -667,8 +665,10 @@ static int map_private_vma(struct pstree_item *t,
 		vma->premmaped_addr += PAGE_SIZE;
 	}
 
-	if (vma_area_is(vma, VMA_FILE_PRIVATE))
-		close(vma->e->fd);
+	if (vma_area_is(vma, VMA_FILE_PRIVATE)) {
+		vma->vm_open = NULL; /* prevent from 2nd open in prepare_vmas */
+		vma->vm_close(vma);
+	}
 
 	*tgt_addr += size;
 	return 0;
@@ -709,6 +709,8 @@ static int premap_priv_vmas(struct pstree_item *t, struct vm_area_list *vmas, vo
 			break;
 	}
 
+	filemap_fin_opens();
+
 	return ret;
 }
 
diff --git a/criu/pie/restorer.c b/criu/pie/restorer.c
index 53ecaa0..2520fde 100644
--- a/criu/pie/restorer.c
+++ b/criu/pie/restorer.c
@@ -543,12 +543,14 @@ static long restore_self_exe_late(struct task_restore_args *args)
 	return ret;
 }
 
-static unsigned long restore_mapping(VmaEntry *vma_entry)
+static unsigned long restore_mapping(VmaEntry *vma_entry, int *fdp)
 {
 	int prot	= vma_entry->prot;
 	int flags	= vma_entry->flags | MAP_FIXED;
 	unsigned long addr;
 
+	*fdp = -1;
+
 	if (vma_entry_is(vma_entry, VMA_AREA_SYSVIPC)) {
 		int att_flags;
 		/*
@@ -595,8 +597,7 @@ static unsigned long restore_mapping(VmaEntry *vma_entry)
 			vma_entry->fd,
 			vma_entry->pgoff);
 
-	if (vma_entry->fd != -1)
-		sys_close(vma_entry->fd);
+	*fdp = vma_entry->fd;
 
 	return addr;
 }
@@ -1135,7 +1136,7 @@ static int wait_zombies(struct task_restore_args *task_args)
 long __export_restore_task(struct task_restore_args *args)
 {
 	long ret = -1;
-	int i;
+	int i, fd_to_close = -1;
 	VmaEntry *vma_entry;
 	unsigned long va;
 
@@ -1245,6 +1246,8 @@ long __export_restore_task(struct task_restore_args *args)
 	 * OK, lets try to map new one.
 	 */
 	for (i = 0; i < args->vmas_n; i++) {
+		int this_fd;
+
 		vma_entry = args->vmas + i;
 
 		if (!vma_entry_is(vma_entry, VMA_AREA_REGULAR))
@@ -1253,14 +1256,27 @@ long __export_restore_task(struct task_restore_args *args)
 		if (vma_entry_is_private(vma_entry, args->task_size))
 			continue;
 
-		va = restore_mapping(vma_entry);
+		va = restore_mapping(vma_entry, &this_fd);
 
 		if (va != vma_entry->start) {
 			pr_err("Can't restore %"PRIx64" mapping with %lx\n", vma_entry->start, va);
 			goto core_restore_end;
 		}
+
+		if (this_fd != -1) {
+			/*
+			 * Descriptors on VMA-s may be shared on subsequent
+			 * areas. See close_filemap() and filemap_fin_opens()
+			 */
+			if (fd_to_close != -1 && fd_to_close != this_fd)
+				sys_close(fd_to_close);
+			fd_to_close = this_fd;
+		}
 	}
 
+	if (fd_to_close != -1)
+		sys_close(fd_to_close);
+
 #ifdef CONFIG_VDSO
 	/*
 	 * Proxify vDSO.
-- 
2.5.5


More information about the CRIU mailing list