[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