[04/10] pstree: add prepare_pstree_leaders to create sid/pgid helpers in advance

Submitted by Pavel Tikhomirov on July 4, 2017, 9:08 a.m.

Details

Message ID 20170704090809.8127-5-ptikhomirov@virtuozzo.com
State New
Series "rework pgid restore for pidnses"
Headers show

Commit Message

Pavel Tikhomirov July 4, 2017, 9:08 a.m.
For shell jobs we skip adding leader to tree as it is fake(external),
but we still initialize ids to use it in get_relative_pid in following
patches.

Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
---
 criu/include/namespaces.h |   2 +
 criu/namespaces.c         |  11 +++
 criu/pstree.c             | 202 ++++++++++++++++++++++++++++++++--------------
 3 files changed, 154 insertions(+), 61 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/include/namespaces.h b/criu/include/namespaces.h
index 3cfa9a9..168ecae 100644
--- a/criu/include/namespaces.h
+++ b/criu/include/namespaces.h
@@ -280,4 +280,6 @@  extern int create_pid_ns_helper(struct ns_id *ns);
 extern int destroy_pid_ns_helpers(void);
 extern int request_set_next_pid(int pid_ns_id, pid_t pid, int sk);
 
+extern struct ns_id *ns_nth_parent(struct ns_id *ns, int n);
+
 #endif /* __CR_NS_H__ */
diff --git a/criu/namespaces.c b/criu/namespaces.c
index 15de9c5..88143a8 100644
--- a/criu/namespaces.c
+++ b/criu/namespaces.c
@@ -2828,5 +2828,16 @@  int destroy_pid_ns_helpers(void)
 	return 0;
 }
 
+/* Search ancestor(n-th parent) namespace of ns */
+struct ns_id *ns_nth_parent(struct ns_id *ns, int n)
+{
+	while(n && ns) {
+		ns = ns->parent;
+		n--;
+	}
+
+	return ns;
+}
+
 struct ns_desc pid_ns_desc = NS_DESC_ENTRY(CLONE_NEWPID, "pid", "pid_for_children");
 struct ns_desc user_ns_desc = NS_DESC_ENTRY(CLONE_NEWUSER, "user", NULL);
diff --git a/criu/pstree.c b/criu/pstree.c
index b4d993f..6f7051f 100644
--- a/criu/pstree.c
+++ b/criu/pstree.c
@@ -991,6 +991,131 @@  int get_free_pids(struct ns_id *ns, pid_t *pids)
 	return MAX_NS_NESTING - i - 1;
 }
 
+int is_group_leader(struct pstree_item *item)
+{
+	if (vpid(item) == vpgid(item)) {
+		BUG_ON(!equal_pid(item->pid, item->pgid));
+		return 1;
+	}
+	return 0;
+}
+
+/* Add session and process group leader helpers to proper namespaces */
+static void prepare_pstree_leaders(void) {
+	struct pstree_item *item;
+
+	for_each_pstree_item(item) {
+		struct pstree_item *session_leader, *group_leader;
+
+		if (item->pid->state == TASK_UNDEF)
+			continue;
+		/* item->sid item->pgid should be allocated/initialized here */
+
+		if (is_session_leader(item))
+			continue;
+
+		session_leader = pstree_item_by_virt(vsid(item));
+		BUG_ON(session_leader == NULL);
+
+		if (session_leader->pid->state == TASK_UNDEF) {
+			struct ns_id *pidns;
+			struct pstree_item *init;
+
+			pidns = lookup_ns_by_id(item->ids->pid_ns_id, &pid_ns_desc);
+			BUG_ON(!pidns);
+			pidns = ns_nth_parent(pidns, item->pid->level - session_leader->pid->level);
+			BUG_ON(!pidns);
+
+			/* For shell job we have no init */
+			if (!opts.shell_job || is_session_leader(root_item)
+					|| vsid(root_item) != vpid(session_leader)) {
+				init = __pstree_item_by_virt(pidns, INIT_PID);
+				BUG_ON(!init);
+			} else {
+				init = root_item;
+			}
+
+			/*
+			 * Session leader has pid==sid==pgid==item->sid with
+			 * ending zeroes cut, and corresponding shift in pidnses
+			 * is needed, put the leader in init's children.
+			 */
+			memcpy(session_leader->sid, item->sid, PID_SIZE(session_leader->sid->level));
+			memcpy(session_leader->pgid, item->sid, PID_SIZE(session_leader->pgid->level));
+			session_leader->ids = init->ids;
+
+			/*
+			 * Skip adding fake helper for external sid
+			 * for shell_job to tree, as it already exists
+			 * somwhere outside
+			 */
+			if (!opts.shell_job || is_session_leader(root_item)
+					|| vsid(root_item) != vpid(session_leader)) {
+				session_leader->parent = init;
+				add_child_task(session_leader, session_leader->parent);
+			}
+			init_pstree_helper(session_leader);
+
+			pr_info("Add a helper %d for restoring SID %d\n",
+					vpid(session_leader), vsid(session_leader));
+		}
+
+		if (is_group_leader(item))
+			continue;
+
+		group_leader = pstree_item_by_virt(vpgid(item));
+		BUG_ON(group_leader == NULL);
+
+		/* Only the item with full pgid has full info of leader's pidns */
+		if (!equal_pid(item->pgid, group_leader->pid))
+			continue;
+
+		if (group_leader->pid->state == TASK_UNDEF) {
+			struct ns_id *pidns;
+			struct pstree_item *init;
+
+			pidns = lookup_ns_by_id(item->ids->pid_ns_id, &pid_ns_desc);
+			BUG_ON(!pidns);
+			pidns = ns_nth_parent(pidns, item->pid->level - group_leader->pid->level);
+			BUG_ON(!pidns);
+
+			/* For shell job we have no init */
+			if (!opts.shell_job || is_session_leader(root_item)
+					|| vpgid(root_item) != vpid(group_leader)) {
+				init = __pstree_item_by_virt(pidns, INIT_PID);
+				BUG_ON(!init);
+			} else {
+				init = root_item;
+			}
+
+			/*
+			 * Process group leader has pid==pgid==item->pgid and
+			 * sid==item->sid with ending zeroes cut, and corresponding
+			 * shift in pidnses is needed, put the leader in session
+			 * leader's children to have sid inherited.
+			 */
+			memcpy(group_leader->sid, item->sid, PID_SIZE(group_leader->sid->level));
+			memcpy(group_leader->pgid, item->pgid, PID_SIZE(group_leader->pgid->level));
+			group_leader->ids = init->ids;
+
+			/*
+			 * Skip adding fake helper for external pgid
+			 * for shell_job to tree, as it already exists
+			 * somwhere outside
+			 */
+			if (!opts.shell_job || is_session_leader(root_item)
+					|| vpgid(root_item) != vpid(group_leader)) {
+				group_leader->parent = session_leader;
+				add_child_task(group_leader, group_leader->parent);
+			}
+			init_pstree_helper(group_leader);
+
+			pr_info("Add a helper %d for restoring PGID %d\n",
+					vpid(group_leader), vpgid(group_leader));
+		}
+	}
+}
+
 static int can_inherit_sid(struct pstree_item *item)
 {
 	struct pstree_item *parent;
@@ -1054,7 +1179,8 @@  static int handle_init_reparent(struct ns_id *ns, void *oarg)
 			int level;
 
 			leader = pstree_item_by_virt(vsid(item));
-			BUG_ON(leader == NULL);
+			BUG_ON(leader == NULL || leader->pid->state == TASK_UNDEF);
+			BUG_ON(!is_session_leader(leader));
 
 			if (leader->pid->level > init->pid->level)
 				/*
@@ -1067,37 +1193,6 @@  static int handle_init_reparent(struct ns_id *ns, void *oarg)
 				 */
 				goto skip_descendants;
 
-			if (leader->pid->state == TASK_UNDEF) {
-				struct ns_id *leader_pid_ns = ns;
-				struct pstree_item *linit;
-				int i;
-
-				/*
-				 * Search a proper pidns where session leader helper
-				 * can be created (using the fact that all processes
-				 * of some session should be in pidns of leader or
-				 * some ancestor pidns)
-				 */
-				for (i = 0; i < init->pid->level - leader->pid->level; i++) {
-					BUG_ON(!leader_pid_ns->parent);
-					leader_pid_ns = leader_pid_ns->parent;
-				}
-				BUG_ON(!leader_pid_ns);
-				linit = __pstree_item_by_virt(leader_pid_ns, INIT_PID);
-				BUG_ON(!linit);
-
-				pr_info("Add a session leader helper %d\n", vsid(item));
-
-				memcpy(leader->sid, item->sid, PID_SIZE(leader->sid->level));
-				memcpy(leader->pgid, item->sid, PID_SIZE(leader->pgid->level));
-				leader->ids = linit->ids;
-				leader->parent = linit;
-
-				add_child_task(leader, leader->parent);
-				init_pstree_helper(leader);
-			}
-			BUG_ON(!is_session_leader(leader));
-
 			level = get_free_pids(ns, pid);
 			if (level <= 0)
 				return -1;
@@ -1148,8 +1243,9 @@  static int handle_init_reparent(struct ns_id *ns, void *oarg)
 
 static int prepare_pstree_ids(void)
 {
-	struct pstree_item *item, *helper;
-	pid_t current_pgid = getpgid(getpid());
+	struct pstree_item *item;
+
+	prepare_pstree_leaders();
 
 	/*
 	 * Some task can be reparented to init. A helper task should be added
@@ -1224,44 +1320,28 @@  static int prepare_pstree_ids(void)
 	if (!list_empty(&top_pid_ns->children))
 		return 0;
 
-	/* Add a process group leader if it is absent  */
+	/* Setup pgrp_leader to wait for it to became a real leader */
 	for_each_pstree_item(item) {
 		struct pid *pid;
 
-		if (!item->pgid || equal_pid(item->pid, item->pgid))
+		if (is_group_leader(item))
 			continue;
 
-		pid = pstree_pid_by_virt(vpgid(item));
-		if (pid->state != TASK_UNDEF) {
-			BUG_ON(pid->state == TASK_THREAD);
-			rsti(item)->pgrp_leader = pid->item;
-			continue;
-		}
-
 		/*
 		 * If the PGID is eq to current one -- this
 		 * means we're inheriting group from the current
-		 * task so we need to escape creating a helper here.
+		 * or setpgid group to some already created group,
+		 * so we do not need to wait leaders's creation.
 		 */
-		if (current_pgid == vpgid(item))
+		if (opts.shell_job && !is_session_leader(root_item)
+				&& vpgid(root_item) == vpgid(item))
 			continue;
 
-		helper = pid->item;
-
-		vsid(helper) = vsid(item);
-		vpgid(helper) = vpgid(item);
-		vpid(helper) = vpgid(item);
-		helper->parent = item;
-		helper->ids = item->ids;
-		if (init_pstree_helper(helper)) {
-			pr_err("Can't init helper\n");
-			return -1;
-		}
-		add_child_task(helper, item);
-		rsti(item)->pgrp_leader = helper;
-
-		pr_info("Add a helper %d for restoring PGID %d\n",
-				vpid(helper), vpgid(helper));
+		pid = pstree_pid_by_virt(vpgid(item));
+		BUG_ON(pid == NULL || pid->state == TASK_UNDEF);
+		BUG_ON(pid->state == TASK_THREAD);
+		rsti(item)->pgrp_leader = pid->item;
+		continue;
 	}
 
 	return 0;