[CRIU] [RFC PATCH v2 21/23] lazy-pages: add handling of UFFD_EVENT_FORK

Mike Rapoport rppt at linux.vnet.ibm.com
Mon Feb 6 03:44:14 PST 2017


Signed-off-by: Mike Rapoport <rppt at linux.vnet.ibm.com>
---
 criu/uffd.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/criu/uffd.c b/criu/uffd.c
index f2a6bfa..b357d08 100644
--- a/criu/uffd.c
+++ b/criu/uffd.c
@@ -74,6 +74,8 @@ struct lazy_pages_info {
 	struct list_head iovs;
 	struct list_head reqs;
 
+	struct lazy_pages_info *parent;
+
 	struct page_read pr;
 
 	unsigned long total_pages;
@@ -88,6 +90,7 @@ struct lazy_pages_info {
 
 static LIST_HEAD(lpis);
 static LIST_HEAD(exiting_lpis);
+static LIST_HEAD(pending_lpis);
 
 static int handle_uffd_event(struct epoll_rfd *lpfd);
 
@@ -366,6 +369,38 @@ static int split_iov(struct lazy_iov *iov, unsigned long addr, bool new_below)
 	return 0;
 }
 
+static int copy_lazy_iovs(struct lazy_pages_info *src,
+			  struct lazy_pages_info *dst)
+{
+	struct lazy_iov *iov, *new, *n;
+	int max_iov_len = 0;
+
+	list_for_each_entry(iov, &src->iovs, l) {
+		new = xzalloc(sizeof(*new));
+		if (!new)
+			return -1;
+
+		new->base = iov->base;
+		new->img_base = iov->img_base;
+		new->len = iov->len;
+
+		list_add_tail(&new->l, &dst->iovs);
+
+		if (new->len > max_iov_len)
+			max_iov_len = new->len;
+	}
+
+	if (posix_memalign(&dst->buf, PAGE_SIZE, max_iov_len))
+		goto free_iovs;
+
+	return 0;
+
+free_iovs:
+	list_for_each_entry_safe(iov, n, &dst->iovs, l)
+		xfree(iov);
+	return -1;
+}
+
 /*
  * Purge range (addr, addr + len) from lazy_iovs. The range may
  * cover several continuous IOVs.
@@ -830,6 +865,58 @@ static int complete_exits(int epollfd)
 	return 0;
 }
 
+static int handle_fork(struct lazy_pages_info *parent_lpi, struct uffd_msg *msg)
+{
+	struct lazy_pages_info *lpi;
+	int uffd = msg->arg.fork.ufd;
+
+	lp_debug(parent_lpi, "FORK: child with ufd=%d\n", uffd);
+
+	lpi = lpi_init();
+	if (!lpi)
+		return -1;
+
+	if (copy_lazy_iovs(parent_lpi, lpi))
+		goto out;
+
+	lpi->pid = parent_lpi->pid;
+	lpi->lpfd.fd = uffd;
+	lpi->parent = parent_lpi->parent ? parent_lpi->parent : parent_lpi;
+	lpi->copied_pages = lpi->parent->copied_pages;
+	lpi->total_pages = lpi->parent->total_pages;
+	list_add_tail(&lpi->l, &pending_lpis);
+
+	dup_page_read(&lpi->parent->pr, &lpi->pr);
+
+	return 1;
+
+out:
+	lpi_fini(lpi);
+	return -1;
+}
+
+static int complete_forks(int epollfd, struct epoll_event **events, int *nr_fds)
+{
+	struct lazy_pages_info *lpi, *n;
+
+	list_for_each_entry(lpi, &pending_lpis, l)
+		(*nr_fds)++;
+
+	*events = xrealloc(*events, sizeof(struct epoll_event) * (*nr_fds));
+	if (!*events)
+		return -1;
+
+	list_for_each_entry_safe(lpi, n, &pending_lpis, l) {
+		if (epoll_add_rfd(epollfd, &lpi->lpfd))
+			return -1;
+
+		list_del_init(&lpi->l);
+		list_add_tail(&lpi->l, &lpis);
+	}
+
+	return 0;
+}
+
 static int handle_page_fault(struct lazy_pages_info *lpi, struct uffd_msg *msg)
 {
 	struct lp_req *req;
@@ -898,6 +985,8 @@ static int handle_uffd_event(struct epoll_rfd *lpfd)
 		return handle_remap(lpi, &msg);
 	case UFFD_EVENT_EXIT:
 		return handle_exit(lpi, &msg);
+	case UFFD_EVENT_FORK:
+		return handle_fork(lpi, &msg);
 	default:
 		lp_err(lpi, "unexpected uffd event %u\n", msg.event);
 		return -1;
@@ -938,6 +1027,8 @@ static int handle_requests(int epollfd, struct epoll_event *events, int nr_fds)
 		if (ret < 0)
 			goto out;
 		if (ret > 0) {
+			if (complete_forks(epollfd, &events, &nr_fds))
+				return -1;
 			if (complete_exits(epollfd))
 				return -1;
 			continue;
-- 
1.9.1



More information about the CRIU mailing list