[CRIU] [PATCH 2/2] proc-parse: Don't open and stat every single map_files link

Pavel Emelyanov xemul at parallels.com
Fri Jan 31 05:30:57 PST 2014


Quite a lot of VMAs in tasks map the same file with different
perms. In that case we may skip opening all these files, but
"borrow" one from the previous VMA parsed.

There's little sense in seeking more that just previous VMA,
as same files are rarely (can be though) mapped in different
locations.

After this on a basic Centos6 container the number of opens and
stats in this function drops from ~1500 to ~500.

Signed-off-by: Pavel Emelyanov <xemul at parallels.com>
---
 cr-dump.c     |  2 ++
 include/vma.h |  2 ++
 proc_parse.c  | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/cr-dump.c b/cr-dump.c
index 00a7ac0..fed67c4 100644
--- a/cr-dump.c
+++ b/cr-dump.c
@@ -116,6 +116,8 @@ static void close_vma_file(struct vma_area *vma)
 		return;
 	if (vma->vma.status & VMA_AREA_SOCKET)
 		return;
+	if (vma->file_borrowed)
+		return;
 
 	close(vma->vm_file_fd);
 }
diff --git a/include/vma.h b/include/vma.h
index 481b690..2bd4844 100644
--- a/include/vma.h
+++ b/include/vma.h
@@ -25,6 +25,8 @@ struct vma_area {
 	unsigned long		*ppage_bitmap; /* parent's existent pages */
 
 	unsigned long		premmaped_addr;
+
+	bool			file_borrowed;
 };
 
 extern int collect_mappings(pid_t pid, struct vm_area_list *vma_area_list);
diff --git a/proc_parse.c b/proc_parse.c
index bd19f2f..c63391d 100644
--- a/proc_parse.c
+++ b/proc_parse.c
@@ -142,13 +142,41 @@ static inline int is_anon_shmem_map(dev_t dev)
 	return kerndat_shmem_dev == dev;
 }
 
-static int vma_get_mapfile(struct vma_area *vma, DIR *mfd)
+struct vma_file_info {
+	int dev_maj;
+	int dev_min;
+	unsigned long ino;
+	struct vma_area *vma;
+};
+
+static inline int vfi_equal(struct vma_file_info *a, struct vma_file_info *b)
+{
+	return ((a->ino ^ b->ino) |
+			(a->dev_maj ^ b->dev_maj) |
+			(a->dev_min ^ b->dev_min)) == 0;
+}
+
+static int vma_get_mapfile(struct vma_area *vma, DIR *mfd,
+		struct vma_file_info *vfi, struct vma_file_info *prev_vfi)
 {
 	char path[32];
 
 	if (!mfd)
 		return 0;
 
+	if (prev_vfi->vma && vfi_equal(vfi, prev_vfi)) {
+		struct vma_area *prev = prev_vfi->vma;
+
+		pr_debug("vma %lx borrows vfi from previous %lx\n",
+				vma->vma.start, prev->vma.start);
+		vma->vm_file_fd = prev->vm_file_fd;
+		if (prev->vma.status & VMA_AREA_SOCKET)
+			vma->vma.status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
+		vma->file_borrowed = true;
+
+		return 0;
+	}
+
 	/* Figure out if it's file mapping */
 	snprintf(path, sizeof(path), "%lx-%lx", vma->vma.start, vma->vma.end);
 
@@ -184,10 +212,10 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 	struct vma_area *vma_area = NULL;
 	unsigned long start, end, pgoff;
 	bool prev_growsdown = false;
-	unsigned long ino;
 	char r, w, x, s;
-	int dev_maj, dev_min;
 	int ret = -1;
+	struct vma_file_info vfi;
+	struct vma_file_info prev_vfi = {};
 
 	DIR *map_files_dir = NULL;
 	FILE *smaps = NULL;
@@ -249,6 +277,9 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 				vma_area_list->priv_size += pages;
 				vma_area_list->longest = max(vma_area_list->longest, pages);
 			}
+
+			prev_vfi = vfi;
+			prev_vfi.vma = vma_area;
 		}
 
 		if (eof)
@@ -260,8 +291,8 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 
 		memset(file_path, 0, 6);
 		num = sscanf(buf, "%lx-%lx %c%c%c%c %lx %x:%x %lu %5s",
-			     &start, &end, &r, &w, &x, &s, &pgoff, &dev_maj,
-			     &dev_min, &ino, file_path);
+			     &start, &end, &r, &w, &x, &s, &pgoff,
+			     &vfi.dev_maj, &vfi.dev_min, &vfi.ino, file_path);
 		if (num < 10) {
 			pr_err("Can't parse: %s\n", buf);
 			goto err;
@@ -272,7 +303,7 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 		vma_area->vma.pgoff	= pgoff;
 		vma_area->vma.prot	= PROT_NONE;
 
-		if (vma_get_mapfile(vma_area, map_files_dir))
+		if (vma_get_mapfile(vma_area, map_files_dir, &vfi, &prev_vfi))
 			goto err_bogus_mapfile;
 
 		if (r == 'r')
@@ -309,7 +340,18 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 		 * Some mapping hints for restore, we save this on
 		 * disk and restore might need to analyze it.
 		 */
-		if (vma_area->vm_file_fd >= 0) {
+		if (vma_area->file_borrowed) {
+			struct vma_area *prev = prev_vfi.vma;
+
+			/*
+			 * Pick-up flags that might be set in the branch below.
+			 * Status is copied as-is as it should be zero here,
+			 * and have full match with the previous.
+			 */
+			vma_area->vma.flags |= (prev->vma.flags & MAP_ANONYMOUS);
+			vma_area->vma.status = prev->vma.status;
+			vma_area->vma.shmid = prev->vma.shmid;
+		} else if (vma_area->vm_file_fd >= 0) {
 			struct stat st_buf;
 
 			if (fstat(vma_area->vm_file_fd, &st_buf) < 0) {
@@ -350,7 +392,7 @@ int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_file
 			 */
 			if (vma_area->vma.flags & MAP_SHARED) {
 				vma_area->vma.status |= VMA_ANON_SHARED;
-				vma_area->vma.shmid = ino;
+				vma_area->vma.shmid = vfi.ino;
 			} else {
 				vma_area->vma.status |= VMA_ANON_PRIVATE;
 			}
-- 
1.8.4.2


More information about the CRIU mailing list