[CRIU] [PATCH v2 7/7] lazy-pages: allow handling multiple processes

Mike Rapoport rppt at linux.vnet.ibm.com
Mon Apr 18 06:34:38 PDT 2016


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

diff --git a/criu/uffd.c b/criu/uffd.c
index 652efa4..15cfa56 100644
--- a/criu/uffd.c
+++ b/criu/uffd.c
@@ -43,8 +43,69 @@ struct lazy_pages_info {
 
 	unsigned long total_pages;
 	unsigned long copied_pages;
+
+	struct hlist_node hash;
 };
 
+#define LPI_HASH_SIZE	16
+static struct hlist_head lpi_hash[LPI_HASH_SIZE];
+
+static struct lazy_pages_info *lpi_init(void)
+{
+	struct lazy_pages_info *lpi = NULL;
+
+	lpi = malloc(sizeof(*lpi));
+	if (!lpi) {
+		pr_err("allocation failed\n");
+		return NULL;
+	}
+
+	memset(lpi, 0, sizeof(*lpi));
+	INIT_LIST_HEAD(&lpi->pages);
+	INIT_HLIST_NODE(&lpi->hash);
+
+	return lpi;
+}
+
+static void lpi_fini(struct lazy_pages_info *lpi)
+{
+	if (lpi->uffd)
+		close(lpi->uffd);
+	free(lpi);
+}
+
+static void lpi_hash_init(void)
+{
+	int i;
+
+	for (i = 0; i < LPI_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&lpi_hash[i]);
+}
+
+struct lazy_pages_info *uffd_to_lpi(int uffd)
+{
+	struct lazy_pages_info *lpi;
+	struct hlist_head *head;
+
+	head = &lpi_hash[uffd % LPI_HASH_SIZE];
+	hlist_for_each_entry(lpi, head, hash)
+		if (lpi->uffd == uffd)
+			return lpi;
+
+	return NULL;
+}
+
+static void lpi_hash_fini(void)
+{
+	struct lazy_pages_info *p;
+	struct hlist_node *n;
+	int i;
+
+	for (i = 0; i < LPI_HASH_SIZE; i++)
+		hlist_for_each_entry_safe(p, n, &lpi_hash[i], hash)
+			lpi_fini(p);
+}
+
 static int send_uffd(int sendfd, int pid)
 {
 	int fd;
@@ -165,33 +226,33 @@ out:
 
 static int find_vmas(struct lazy_pages_info *lpi);
 
-static int ud_open(struct lazy_pages_info *lpi, int listen,
-		   struct sockaddr_un *saddr)
+static struct lazy_pages_info *ud_open(int listen, struct sockaddr_un *saddr)
 {
+	struct lazy_pages_info *lpi;
 	int client;
 	int ret = -1;
 	socklen_t len;
 	int uffd_flags;
 
-	memset(lpi, 0, sizeof(*lpi));
-	INIT_LIST_HEAD(&lpi->pages);
-
 	/* accept new client request */
 	len = sizeof(struct sockaddr_un);
 	if ((client = accept(listen, saddr, &len)) < 0) {
 		pr_perror("server_accept error: %d", client);
 		close(listen);
-		return -1;
+		return NULL;
 	}
 
 	pr_debug("client fd %d\n", client);
 
+	lpi = lpi_init();
+	if (!lpi)
+		goto out;
+
 	/* The "transfer protocol" is first the pid as int and then
 	 * the FD for UFFD */
 	ret = recv(client, &lpi->pid, sizeof(lpi->pid), 0);
 	if (ret != sizeof(lpi->pid)) {
 		pr_perror("PID recv error:");
-		ret = -1;
 		goto out;
 	}
 	pr_debug("received PID: %d\n", lpi->pid);
@@ -199,7 +260,6 @@ static int ud_open(struct lazy_pages_info *lpi, int listen,
 	lpi->uffd = recv_fd(client);
 	if (lpi->uffd < 0) {
 		pr_perror("recv_fd error:");
-		ret = -1;
 		goto out;
 	}
 	pr_debug("lpi->uffd %d\n", lpi->uffd);
@@ -214,11 +274,16 @@ static int ud_open(struct lazy_pages_info *lpi, int listen,
 	 * so that it is trackable when all pages have been transferred.
 	 */
 	if ((lpi->total_pages = find_vmas(lpi)) == -1)
-		return -1;
+		goto out;
+
+	hlist_add_head(&lpi->hash, &lpi_hash[lpi->uffd % LPI_HASH_SIZE]);
+
+	return lpi;
 
 out:
+	free(lpi);
 	close(client);
-	return ret;
+	return NULL;
 }
 
 static int get_page(struct lazy_pages_info *lpi, unsigned long addr, void *dest)
@@ -581,7 +646,8 @@ static int handle_user_fault(struct lazy_pages_info *lpi, void *dest)
 
 static int lazy_pages_summary(struct lazy_pages_info *lpi)
 {
-	pr_debug("With UFFD transferred pages: (%ld/%ld)\n", lpi->copied_pages, lpi->total_pages);
+	pr_debug("Process %d: with UFFD transferred pages: (%ld/%ld)\n",
+		 lpi->pid, lpi->copied_pages, lpi->total_pages);
 
 	if ((lpi->copied_pages != lpi->total_pages) && (lpi->total_pages > 0)) {
 		pr_warn("Only %ld of %ld pages transferred via UFFD\n", lpi->copied_pages,
@@ -595,13 +661,14 @@ static int lazy_pages_summary(struct lazy_pages_info *lpi)
 
 #define POLL_TIMEOUT 5000
 
-static int handle_requests(struct lazy_pages_info *lpi, int epollfd,
-			   struct epoll_event *events)
+static int handle_requests(int epollfd, struct epoll_event *events)
 {
 	int nr_fds = task_entries->nr_tasks;
+	struct lazy_pages_info *lpi;
 	int ret = -1;
 	unsigned long ps;
 	void *dest;
+	int i;
 
 	/* All operations will be done on page size */
 	ps = page_size();
@@ -624,23 +691,31 @@ static int handle_requests(struct lazy_pages_info *lpi, int epollfd,
 			break;
 		}
 
-		ret = handle_user_fault(lpi, dest);
-		if (ret < 0)
-			goto out;
+		for (i = 0; i < ret; i++) {
+			lpi = uffd_to_lpi(events[i].data.fd);
+			ret = handle_user_fault(lpi, dest);
+			if (ret < 0)
+				goto out;
+		}
 	}
 	pr_debug("Handle remaining pages\n");
-	ret = handle_remaining_pages(lpi, dest);
-	if (ret < 0) {
-		pr_err("Error during remaining page copy\n");
-		ret = 1;
-		goto out;
+	for (i = 0; i < LPI_HASH_SIZE; i++) {
+		hlist_for_each_entry(lpi, &lpi_hash[i], hash) {
+			ret = handle_remaining_pages(lpi, dest);
+			if (ret < 0) {
+				pr_err("Error during remaining page copy\n");
+				ret = 1;
+				goto out;
+			}
+		}
 	}
 
-	ret = lazy_pages_summary(lpi);
+	for (i = 0; i < LPI_HASH_SIZE; i++)
+		hlist_for_each_entry(lpi, &lpi_hash[i], hash)
+			ret += lazy_pages_summary(lpi);
 
 out:
 	free(dest);
-	close(lpi->uffd);
 	return ret;
 
 }
@@ -657,12 +732,6 @@ static int lazy_pages_prepare_pstree(void)
 	if (prepare_pstree() == -1)
 		return -1;
 
-	/* bail out early until we know how to handle multiple tasks */
-	if (task_entries->nr_tasks > 1) {
-		pr_msg("lazy-pages cannot restore more than one task, sorry\n");
-		return -1;
-	}
-
 	return 0;
 }
 
@@ -703,8 +772,9 @@ static int epoll_add_fd(int epollfd, int fd)
 	return 0;
 }
 
-static int prepare_uffds(struct lazy_pages_info *lpi, int epollfd)
+static int prepare_uffds(int epollfd)
 {
+	int i;
 	int listen;
 	struct sockaddr_un saddr;
 
@@ -714,29 +784,29 @@ static int prepare_uffds(struct lazy_pages_info *lpi, int epollfd)
 		return -1;
 	}
 
-	if (ud_open(lpi, listen, &saddr) < 0) {
-		pr_perror("uffd open error");
-		goto close_unix_sock;
+	for (i = 0; i < task_entries->nr_tasks; i++) {
+		struct lazy_pages_info *lpi;
+		lpi = ud_open(listen, &saddr);
+		if (!lpi)
+			goto close_uffd;
+		if (epoll_add_fd(epollfd, lpi->uffd))
+			goto close_uffd;
 	}
 
-	if (epoll_add_fd(epollfd, lpi->uffd))
-		goto close_uffd;
-
 	close(listen);
 	return 0;
 
 close_uffd:
-	close(lpi->uffd);
-close_unix_sock:
+	lpi_hash_fini();
 	close(listen);
 	return -1;
 }
 
 int cr_lazy_pages()
 {
-	struct lazy_pages_info lpi;
 	struct epoll_event *events;
 	int epollfd;
+	int ret;
 
 	if (!opts.addr) {
 		pr_info("Please specify a file name for the unix domain socket\n");
@@ -746,6 +816,8 @@ int cr_lazy_pages()
 		return -1;
 	}
 
+	lpi_hash_init();
+
 	if (lazy_pages_prepare_pstree())
 		return -1;
 
@@ -753,10 +825,13 @@ int cr_lazy_pages()
 	if (epollfd < 0)
 		return -1;
 
-	if (prepare_uffds(&lpi, epollfd))
+	if (prepare_uffds(epollfd))
 		return -1;
 
-	return handle_requests(&lpi, epollfd, events);
+	ret = handle_requests(epollfd, events);
+	lpi_hash_fini();
+
+	return ret;
 }
 
 #else /* CONFIG_HAS_UFFD */
-- 
1.9.1



More information about the CRIU mailing list