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

Andrew Vagin avagin at virtuozzo.com
Wed Jun 15 21:45:09 PDT 2016


On Mon, Apr 18, 2016 at 04:34:38PM +0300, Mike Rapoport wrote:
> 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);

   	
CID 163485 (#2 of 2): Dereference null return value (NULL_RETURNS)
7. dereference: Dereferencing a pointer that might be null dest when
calling handle_user_fault. [show details]

			if (lpi == NULL)
				goto somewere;

> +			ret = handle_user_fault(lpi, dest);

ret is used in for()

> +			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
> 
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list