[CRIU] [PATCH v5 5/5] Reorder uffd code

Adrian Reber adrian at lisas.de
Wed Mar 9 23:39:54 PST 2016


From: Adrian Reber <areber at redhat.com>

In order to support uffd from remote hosts the uffd code is re-ordered
to better handle network transfers.

Signed-off-by: Adrian Reber <areber at redhat.com>
---
 criu/uffd.c | 216 +++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 157 insertions(+), 59 deletions(-)

diff --git a/criu/uffd.c b/criu/uffd.c
index 288571e..4af293f 100644
--- a/criu/uffd.c
+++ b/criu/uffd.c
@@ -178,24 +178,37 @@ out:
 	return ret;
 }
 
-static void get_page(unsigned long addr, void *dest, struct page_read *pr)
+static int get_page(unsigned long addr, void *dest)
 {
 	struct iovec iov;
 	int ret;
 	unsigned char buf[PAGE_SIZE];
+	struct page_read pr;
 
-	ret = open_page_read(root_item->pid.virt, pr, PR_TASK | PR_MOD);
-	pr_debug("ret %d\n", ret);
+	ret = open_page_read(root_item->pid.virt, &pr, PR_TASK | PR_MOD);
+	pr_debug("get_page ret %d\n", ret);
 
-	/* TODO: return code checking */
-	ret = pr->get_pagemap(pr, &iov);
-	pr_debug("get_pagemap  ret %d\n", ret);
-	ret = seek_pagemap_page(pr, addr, true);
-	pr_debug("seek_pagemap_page %x\n", ret);
-	ret = pr->read_pages(pr, addr, 1, buf);
+	ret = pr.get_pagemap(&pr, &iov);
+	pr_debug("get_pagemap ret %d\n", ret);
+	if (ret <= 0)
+		return ret;
+
+	ret = seek_pagemap_page(&pr, addr, true);
+	pr_debug("seek_pagemap_page ret 0x%x\n", ret);
+	if (ret <= 0)
+		return ret;
+
+	ret = pr.read_pages(&pr, addr, 1, buf);
 	pr_debug("read_pages ret %d\n", ret);
+	if (ret <= 0)
+		return ret;
+
 	memcpy(dest, buf, PAGE_SIZE);
-	pr->close(pr);
+
+	if (pr.close)
+		pr.close(&pr);
+
+	return 1;
 }
 
 #define UFFD_FLAG_SENT	0x1
@@ -207,12 +220,14 @@ struct uffd_pages_struct {
 	int flags;
 };
 
-static int uffd_copy_page(int uffd, struct page_read *pr, __u64 address, void *dest)
+static int uffd_copy_page(int uffd, __u64 address, void *dest)
 {
 	struct uffdio_copy uffdio_copy;
 	int rc;
 
-	get_page(address, dest, pr);
+	rc = get_page(address, dest);
+	if (rc <= 0)
+		return -1;
 
 	uffdio_copy.dst = address;
 	uffdio_copy.src = (unsigned long) dest;
@@ -308,6 +323,86 @@ static int collect_uffd_pages(struct page_read *pr, struct list_head *uffd_list,
 	return 1;
 }
 
+static int handle_remaining_pages(int uffd, struct list_head *uffd_list, unsigned long *vma_size,
+				  void *dest)
+{
+	unsigned long uffd_copied_pages = 0;
+	struct uffd_pages_struct *uffd_pages;
+	int rc;
+
+	pr_debug("remaining vma_size: 0x%lx\n", *vma_size);
+	pr_debug("uffd_copied_pages:    %ld\n", uffd_copied_pages);
+
+	list_for_each_entry(uffd_pages, uffd_list, list) {
+		pr_debug("Checking remaining pages 0x%lx (flags 0x%x)\n",
+			 uffd_pages->addr, uffd_pages->flags);
+		if (uffd_pages->flags & UFFD_FLAG_SENT)
+			continue;
+
+		rc = uffd_copy_page(uffd, uffd_pages->addr, dest);
+		if (rc < 0) {
+			pr_err("Error during UFFD copy\n");
+			return -1;
+		}
+		*vma_size -= rc;
+
+		pr_debug("remaining vma_size: 0x%lx\n", *vma_size);
+		uffd_copied_pages++;
+		uffd_pages->flags |= UFFD_FLAG_SENT;
+	}
+
+	return uffd_copied_pages;
+}
+
+
+static int handle_regular_pages(int uffd, struct list_head *uffd_list, unsigned long *vma_size,
+				void *dest, __u64 address)
+{
+	int rc;
+	struct uffd_pages_struct *uffd_pages;
+
+	rc = uffd_copy_page(uffd, address, dest);
+	if (rc < 0) {
+		pr_err("Error during UFFD copy\n");
+		return -1;
+	}
+	*vma_size -= rc;
+
+	/*
+	 * Mark this page as having been already transferred, so
+	 * that it has not to be copied again later.
+	 */
+	list_for_each_entry(uffd_pages, uffd_list, list) {
+		if (uffd_pages->addr == address)
+			uffd_pages->flags |= UFFD_FLAG_SENT;
+	}
+
+
+	return 1;
+}
+
+static int handle_vdso_pages(int uffd, struct list_head *uffd_list, unsigned long *vma_size,
+			     void *dest)
+{
+	int rc;
+	struct uffd_pages_struct *uffd_pages;
+	int uffd_copied_pages = 0;
+
+	list_for_each_entry(uffd_pages, uffd_list, list) {
+		if (!(uffd_pages->flags & UFFD_FLAG_VDSO))
+			continue;
+		rc = uffd_copy_page(uffd, uffd_pages->addr, dest);
+		if (rc < 0) {
+			pr_err("Error during UFFD copy\n");
+			return -1;
+		}
+		*vma_size -= rc;
+		uffd_copied_pages++;
+		uffd_pages->flags |= UFFD_FLAG_SENT;
+	}
+	return uffd_copied_pages;
+}
+
 /*
  *  Setting up criu infrastructure to easily
  *  access the dump results.
@@ -341,6 +436,7 @@ int uffd_listen()
 	struct timeval timeout;
 	int uffd;
 	unsigned long uffd_copied_pages = 0;
+	unsigned long total_pages = 0;
 	int uffd_flags;
 	struct uffd_pages_struct *uffd_pages;
 	bool vdso_sent = false;
@@ -376,8 +472,10 @@ int uffd_listen()
 	dest = malloc(ps);
 
 	rc = open_page_read(root_item->pid.virt, &pr, PR_TASK);
-	if (rc <= 0)
-		return 1;
+	if (rc <= 0) {
+		rc = 1;
+		goto out;
+	}
 	/*
 	 * This puts all pages which should be handled by userfaultfd
 	 * in the list uffd_list. This list is later used to detect if
@@ -386,13 +484,22 @@ int uffd_listen()
 	 */
 	do {
 		rc = collect_uffd_pages(&pr, &uffd_list, &vma_size);
-		if (rc == -1)
-			return 1;
+		if (rc == -1) {
+			rc = 1;
+			goto out;
+		}
 	} while (rc);
 
 	if (pr.close)
 		pr.close(&pr);
 
+
+	/* Count detected pages */
+	list_for_each_entry(uffd_pages, &uffd_list, list)
+	    total_pages++;
+
+	pr_debug("Found %ld pages to be handled by UFFD\n", total_pages);
+
 	while (1) {
 		bool page_sent = false;
 		/*
@@ -434,18 +541,13 @@ int uffd_listen()
 		 */
 		if (!vdso_sent) {
 			pr_debug("Pushing VDSO pages once\n");
-			list_for_each_entry(uffd_pages, &uffd_list, list) {
-				if (!(uffd_pages->flags & UFFD_FLAG_VDSO))
-					continue;
-				rc = uffd_copy_page(uffd, &pr, uffd_pages->addr, dest);
-				if (rc < 0) {
-					pr_err("Error during UFFD copy\n");
-					return 1;
-				}
-				vma_size -= rc;
-				uffd_copied_pages++;
-				uffd_pages->flags |= UFFD_FLAG_SENT;
+			rc = handle_vdso_pages(uffd, &uffd_list, &vma_size, dest);
+			if (rc < 0) {
+				pr_err("Error during VDSO handling\n");
+				rc = 1;
+				goto out;
 			}
+			uffd_copied_pages += rc;
 			vdso_sent = true;
 		}
 
@@ -467,45 +569,41 @@ int uffd_listen()
 
 		if (msg.event != UFFD_EVENT_PAGEFAULT) {
 			pr_err("unexpected msg event %u\n", msg.event);
-			return 1;
+			rc = 1;
+			goto out;
 		}
 
-		rc = uffd_copy_page(uffd, &pr, address, dest);
+		rc = handle_regular_pages(uffd, &uffd_list, &vma_size, dest, address);
 		if (rc < 0) {
-			pr_err("Error during UFFD copy\n");
-			return 1;
+			pr_err("Error during regular page copy\n");
+			rc = 1;
+			goto out;
 		}
-		vma_size -= rc;
-		uffd_copied_pages++;
 
-		/*
-		 * Mark this page as having been already transferred, so
-		 * that it has not to be copied again later.
-		 */
-		list_for_each_entry(uffd_pages, &uffd_list, list) {
-			if (uffd_pages->addr == address)
-				uffd_pages->flags |= UFFD_FLAG_SENT;
-		}
-	}
-	pr_debug("remaining vma_size: 0x%lx\n", vma_size);
-	pr_debug("uffd_copied_pages:    %ld\n", uffd_copied_pages);
-	list_for_each_entry(uffd_pages, &uffd_list, list) {
-		pr_debug("Checking remaining pages 0x%lx (flags 0x%x)\n",
-			 uffd_pages->addr, uffd_pages->flags);
-		if (uffd_pages->flags & UFFD_FLAG_SENT)
-			continue;
+		uffd_copied_pages += rc;
 
-		rc = uffd_copy_page(uffd, &pr, uffd_pages->addr, dest);
-		if (rc < 0) {
-			pr_err("Error during UFFD copy\n");
-			return 1;
-		}
-		vma_size -= rc;
+	}
+	pr_debug("Handle remaining pages\n");
+	rc = handle_remaining_pages(uffd, &uffd_list, &vma_size, dest);
+	if (rc < 0) {
+		pr_err("Error during remaining page copy\n");
+		rc = 1;
+		goto out;
+	}
 
-		pr_debug("remaining vma_size: 0x%lx\n", vma_size);
-		pr_debug("uffd_copied_pages:    %ld\n", ++uffd_copied_pages);
-		uffd_pages->flags |= UFFD_FLAG_SENT;
+	uffd_copied_pages += rc;
+	pr_debug("With UFFD transferred pages: (%ld/%ld)\n", uffd_copied_pages, total_pages);
+	if (uffd_copied_pages != total_pages) {
+		pr_warn("Only %ld of %ld pages transferred via UFFD\n", uffd_copied_pages,
+			total_pages);
+		pr_warn("Something probably went wrong.\n");
+		rc = 1;
+		goto out;
 	}
+	rc = 0;
+
+out:
+	free(dest);
 	close(uffd);
-	return 0;
+	return rc;
 }
-- 
1.8.3.1



More information about the CRIU mailing list