[CRIU] [PATCH v2] freeze -- Wait for exiting tasks to complete

Cyrill Gorcunov gorcunov at openvz.org
Sun Oct 9 11:48:00 PDT 2016


There is a small race between exitings tasks and cgroup freezer:
it can report a pid of exiting process which we will consider
as a zombie and refuse to checkpoint. Lets do a trick instead:
if we meets such task just wait a bit and repeate a freezing.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 criu/seize.c | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/criu/seize.c b/criu/seize.c
index e1e805a151db..d6db9f096803 100644
--- a/criu/seize.c
+++ b/criu/seize.c
@@ -133,11 +133,16 @@ static int seize_cgroup_tree(char *root_path, const char *state)
 			snprintf(buf, sizeof(buf), "/proc/%d/exe", pid);
 			if (stat(buf, &st) == -1 && errno == ENOENT)
 				continue;
-
-			/* fails when meets a zombie */
+			/*
+			 * fails when meets a zombie, or eixting process:
+			 * there is a small race in a kernel -- the process
+			 * may start exiting and we are trying to freeze it
+			 * before it compete exit procedure. The caller simply
+			 * should wait a bit and try freezing again.
+			 */
 			pr_err("zombie found while seizing\n");
 			fclose(f);
-			return -1;
+			return -EPERM;
 		}
 	}
 	fclose(f);
@@ -150,6 +155,7 @@ static int seize_cgroup_tree(char *root_path, const char *state)
 
 	while ((de = readdir(dir))) {
 		struct stat st;
+		int ret;
 
 		if (dir_dots(de))
 			continue;
@@ -164,10 +170,10 @@ static int seize_cgroup_tree(char *root_path, const char *state)
 
 		if (!S_ISDIR(st.st_mode))
 			continue;
-
-		if (seize_cgroup_tree(path, state) < 0) {
+		ret = seize_cgroup_tree(path, state);
+		if (ret < 0) {
 			closedir(dir);
-			return -1;
+			return ret;
 		}
 	}
 	closedir(dir);
@@ -325,7 +331,7 @@ static int freeze_processes(void)
 
 	static const unsigned long step_ms = 100;
 	unsigned long nr_attempts = (opts.timeout * 1000000) / step_ms;
-	unsigned long i;
+	unsigned long i = 0;
 
 	const struct timespec req = {
 		.tv_nsec	= step_ms * 1000000,
@@ -371,7 +377,7 @@ static int freeze_processes(void)
 		 * not read @tasks pids while freezer in
 		 * transition stage.
 		 */
-		for (i = 0; i <= nr_attempts; i++) {
+		for (; i <= nr_attempts; i++) {
 			state = get_freezer_state(fd);
 			if (!state) {
 				close(fd);
@@ -395,7 +401,18 @@ static int freeze_processes(void)
 		pr_debug("freezing processes: %lu attempts done\n", i);
 	}
 
-	exit_code = seize_cgroup_tree(opts.freeze_cgroup, state);
+	/*
+	 * Pay attention on @i variable -- it's continuation.
+	 */
+	for (; i <= nr_attempts; i++) {
+		exit_code = seize_cgroup_tree(opts.freeze_cgroup, state);
+		if (exit_code == -EPERM) {
+			if (alarm_timeouted())
+				goto err;
+			nanosleep(&req, NULL);
+		} else
+			break;
+	}
 
 err:
 	if (exit_code == 0 || freezer_thawed) {
-- 
2.7.4



More information about the CRIU mailing list