[CRIU] [PATCH 2/3] pstree: rework init reparent handling for later use in pid namespaces

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Wed Feb 8 00:55:22 PST 2017


Put code to handle_init_reparent, make it work with any pstree_item
root, as later will use it on each pidns init.

Consider the case when branch reparented to init riping some
session in two pieces but representative of these session in
reparented branch is not the immediate child of init, see
can_inherit_sid (now the process tree which had only 'init'
reparents[no child subreapers] will be restored without errors,
all cases are handled).

Collect all helper processes in separate list, so that it would
be easier to find them with get_helper_by_sid for other ripped
pieces of these sid.

Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
 criu/pstree.c | 170 ++++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 106 insertions(+), 64 deletions(-)

diff --git a/criu/pstree.c b/criu/pstree.c
index 7c74607..693ccc4 100644
--- a/criu/pstree.c
+++ b/criu/pstree.c
@@ -619,84 +619,129 @@ static int get_free_pid()
 	return -1;
 }
 
-static int prepare_pstree_ids(void)
+static int is_session_leader(struct pstree_item *item)
 {
-	struct pstree_item *item, *child, *helper, *tmp;
-	LIST_HEAD(helpers);
+	return item->sid == item->pid->ns[0].virt;
+}
 
-	pid_t current_pgid = getpgid(getpid());
+static int can_inherit_sid(struct pstree_item *item)
+{
+	struct pstree_item *parent;
 
-	/*
-	 * Some task can be reparented to init. A helper task should be added
-	 * for restoring sid of such tasks. The helper tasks will be exited
-	 * immediately after forking children and all children will be
-	 * reparented to init.
-	 */
-	list_for_each_entry(item, &root_item->children, sibling) {
-		struct pstree_item *leader;
+	parent = item->parent;
+	while (parent) {
+		if (item->sid == parent->sid)
+			return 1;
+		if (!is_session_leader(parent))
+			break;
+		parent = parent->parent;
+	}
+	return 0;
+}
 
-		/*
-		 * If a child belongs to the root task's session or it's
-		 * a session leader himself -- this is a simple case, we
-		 * just proceed in a normal way.
-		 */
-		if (item->sid == root_item->sid || item->sid == item->pid->ns[0].virt)
+static struct pstree_item *get_helper_by_sid(int sid, struct list_head *helpers) {
+	struct pstree_item *helper;
+	list_for_each_entry(helper, helpers, sibling)
+		if (helper->sid == sid)
+			return helper;
+	return NULL;
+}
+
+static int handle_init_reparent(struct pstree_item *init)
+{
+	struct pstree_item *item, *helper, *child, *tmp;
+	LIST_HEAD(helpers);
+
+	for_each_pssubtree_item(item, init) {
+skip:
+		if (!item)
+			break;
+		if (item == init) /* skip init task */
 			continue;
 
-		leader = pstree_item_by_virt(item->sid);
-		BUG_ON(leader == NULL);
-		if (leader->pid->state != TASK_UNDEF) {
-			pid_t pid;
+		if (is_session_leader(item)) /* session leader has no trouble with sid */
+			continue;
 
-			pid = get_free_pid();
-			if (pid < 0)
-				break;
-			helper = lookup_create_item(pid);
-			if (helper == NULL)
-				return -1;
+		if (can_inherit_sid(item)) {
+			/* descendants of non-leader should be fine, skip them */
+			item = pssubtree_item_next(item, init, true);
+			goto skip;
+		}
 
-			pr_info("Session leader %d\n", item->sid);
+		helper = get_helper_by_sid(item->sid, &helpers);
+		if (!helper) {
+			struct pstree_item *leader;
 
-			helper->sid = item->sid;
-			helper->pgid = leader->pgid;
-			helper->ids = leader->ids;
-			helper->parent = leader;
-			list_add(&helper->sibling, &leader->children);
+			leader = pstree_item_by_virt(item->sid);
+			BUG_ON(leader == NULL);
+			if (leader->pid->state != TASK_UNDEF) {
+				pid_t pid;
 
-			pr_info("Attach %d to the task %d\n",
-					helper->pid->ns[0].virt, leader->pid->ns[0].virt);
-		} else {
-			helper = leader;
-			helper->sid = item->sid;
-			helper->pgid = item->sid;
-			helper->parent = root_item;
-			helper->ids = root_item->ids;
+				pid = get_free_pid();
+				if (pid < 0)
+					break;
+				helper = lookup_create_item(pid);
+				if (helper == NULL)
+					return -1;
+
+				pr_info("Session leader %d\n", item->sid);
+
+				helper->sid = item->sid;
+				helper->pgid = leader->pgid;
+				helper->ids = leader->ids;
+				helper->parent = leader;
+			} else {
+				helper = leader;
+				helper->sid = item->sid;
+				helper->pgid = item->sid;
+				helper->parent = init;
+				helper->ids = init->ids;
+			}
 			list_add_tail(&helper->sibling, &helpers);
+			init_pstree_helper(helper);
+
+			pr_info("Add a helper %d for restoring SID %d\n",
+					helper->pid->ns[0].virt, helper->sid);
 		}
-		init_pstree_helper(helper);
 
-		pr_info("Add a helper %d for restoring SID %d\n",
-				helper->pid->ns[0].virt, helper->sid);
+		child = item;
+		while (child->parent && child->parent != init)
+			child = child->parent;
 
-		child = list_entry(item->sibling.prev, struct pstree_item, sibling);
-		item = child;
+		pr_info("Attach %d to the temporary task %d\n",
+				child->pid->ns[0].virt, helper->pid->ns[0].virt);
+		if (child->sibling.next == &init->children)
+			/* last child of init */
+			item = NULL;
+		else
+			/* skip the subtree that we reparent to helper */
+			item = list_entry(child->sibling.next, struct pstree_item, sibling);
+		child->parent = helper;
+		list_move(&child->sibling, &helper->children);
+		goto skip;
+	}
 
-		/*
-		 * Stack on helper task all children with target sid.
-		 */
-		list_for_each_entry_safe_continue(child, tmp, &root_item->children, sibling) {
-			if (child->sid != helper->sid)
-				continue;
-			if (child->sid == child->pid->ns[0].virt)
-				continue;
+	list_for_each_entry_safe(helper, tmp, &helpers, sibling) {
+		list_move(&helper->sibling, &helper->parent->children);
+		pr_info("Attach helper %d to the task %d\n",
+				helper->pid->ns[0].virt, helper->parent->pid->ns[0].virt);
+	}
+	return 0;
+}
 
-			pr_info("Attach %d to the temporary task %d\n",
-					child->pid->ns[0].virt, helper->pid->ns[0].virt);
+static int prepare_pstree_ids(void)
+{
+	struct pstree_item *item, *helper;
+	pid_t current_pgid = getpgid(getpid());
 
-			child->parent = helper;
-			list_move(&child->sibling, &helper->children);
-		}
-	}
+	/*
+	 * Some task can be reparented to init. A helper task should be added
+	 * for restoring sid of such tasks. The helper tasks will be exited
+	 * immediately after forking children and all children will be
+	 * reparented to init.
+	 */
+	if (handle_init_reparent(root_item) < 0)
+		return -1;
 
 	/* Try to connect helpers to session leaders */
 	for_each_pstree_item(item) {
@@ -735,9 +780,6 @@ static int prepare_pstree_ids(void)
 		}
 	}
 
-	/* All other helpers are session leaders for own sessions */
-	list_splice(&helpers, &root_item->children);
-
 	/* Add a process group leader if it is absent  */
 	for_each_pstree_item(item) {
 		struct pid *pid;
-- 
2.9.3



More information about the CRIU mailing list