From f1edcb32f54459b53024c47a79ec40a1d7868f86 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 27 Sep 2013 04:38:00 +0400 Subject: [PATCH] rst: Introduce fine-grained pgid-restore synchronization We can restore task's pgid which is not equal to its pid, only when the respective group leader is alive. To make restore reliable we wait for all group leaders to restore using separate restore stage. It's better to optimize this -- each task has a pointer on its group leader and waits for one to become such. Signed-off-by: Pavel Emelyanov --- cr-restore.c | 58 +++++++++++++++++++++++++++------------------- include/crtools.h | 5 ++++ include/restorer.h | 1 - pstree.c | 5 +++- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/cr-restore.c b/cr-restore.c index fd76aed03..b5078445b 100644 --- a/cr-restore.c +++ b/cr-restore.c @@ -1062,19 +1062,48 @@ static void restore_sid(void) static void restore_pgid(void) { - pid_t pgid; + /* + * Unlike sessions, process groups (a.k.a. pgids) can be joined + * by any task, provided the task with pid == pgid (group leader) + * exists. Thus, in order to restore pgid we must make sure that + * group leader was born and created the group, then join one. + * + * We do this _before_ finishing the forking stage to make sure + * helpers are still with us. + */ - pr_info("Restoring %d to %d pgid\n", current->pid.virt, current->pgid); + pid_t pgid, my_pgid = current->pgid; + + pr_info("Restoring %d to %d pgid\n", current->pid.virt, my_pgid); pgid = getpgrp(); - if (current->pgid == pgid) + if (my_pgid == pgid) return; + if (my_pgid != current->pid.virt) { + struct pstree_item *leader; + + /* + * Wait for leader to become such. + * Missing leader means we're going to crtools + * group (-j option). + */ + + leader = current->rst->pgrp_leader; + if (leader) { + BUG_ON(my_pgid != leader->pid.virt); + futex_wait_until(&leader->rst->pgrp_set, 1); + } + } + pr_info("\twill call setpgid, mine pgid is %d\n", pgid); - if (setpgid(0, current->pgid) != 0) { + if (setpgid(0, my_pgid) != 0) { pr_perror("Can't restore pgid (%d/%d->%d)", current->pid.virt, pgid, current->pgid); exit(1); } + + if (my_pgid == current->pid.virt) + futex_set_and_wake(¤t->rst->pgrp_set, 1); } static int mount_proc(void) @@ -1240,26 +1269,12 @@ static int restore_task_with_children(void *_arg) if (unmap_guard_pages()) exit(1); - /* - * Unlike sessions, process groups (a.k.a. pgids) can be joined - * by any task, provided the task with pid == pgid (group leader) - * exists. Thus, in order to restore pgid we must make sure that - * group leader was born (stage barrier below), created the group - * (the 1st restore_pgid below) and then join one (the 2nd call - * to restore_pgid). - */ - if (current->pgid == current->pid.virt) - restore_pgid(); + restore_pgid(); if (restore_finish_stage(CR_STATE_FORKING) < 0) exit(1); - if (current->pgid != current->pid.virt) - restore_pgid(); - - restore_finish_stage(CR_STATE_RESTORE_PGID); - if (current->state == TASK_HELPER) return 0; @@ -1274,7 +1289,6 @@ static inline int stage_participants(int next_stage) case CR_STATE_RESTORE_NS: return 1; case CR_STATE_FORKING: - case CR_STATE_RESTORE_PGID: return task_entries->nr_tasks + task_entries->nr_helpers; case CR_STATE_RESTORE: case CR_STATE_RESTORE_SIGCHLD: @@ -1472,10 +1486,6 @@ static int restore_root_task(struct pstree_item *init) timing_stop(TIME_FORK); - ret = restore_switch_stage(CR_STATE_RESTORE_PGID); - if (ret < 0) - goto out_kill; - ret = restore_switch_stage(CR_STATE_RESTORE); if (ret < 0) goto out_kill; diff --git a/include/crtools.h b/include/crtools.h index 76b4f6be2..149525e3d 100644 --- a/include/crtools.h +++ b/include/crtools.h @@ -182,6 +182,11 @@ struct rst_info { int service_fd_id; struct fdt *fdt; + + union { + struct pstree_item *pgrp_leader; + futex_t pgrp_set; + }; }; static inline int in_vma_area(struct vma_area *vma, unsigned long addr) diff --git a/include/restorer.h b/include/restorer.h index 9d9e7f4b7..18a82d89a 100644 --- a/include/restorer.h +++ b/include/restorer.h @@ -187,7 +187,6 @@ enum { CR_STATE_FAIL = -1, CR_STATE_RESTORE_NS = 0, /* is used for executing "setup-namespace" scripts */ CR_STATE_FORKING, - CR_STATE_RESTORE_PGID, CR_STATE_RESTORE, CR_STATE_RESTORE_SIGCHLD, /* diff --git a/pstree.c b/pstree.c index ebf106d1a..fbd69ffea 100644 --- a/pstree.c +++ b/pstree.c @@ -520,8 +520,10 @@ static int prepare_pstree_ids(void) break; } - if (gleader) + if (gleader) { + item->rst->pgrp_leader = gleader; continue; + } /* * If the PGID is eq to current one -- this @@ -541,6 +543,7 @@ static int prepare_pstree_ids(void) helper->parent = item; list_add(&helper->sibling, &item->children); task_entries->nr_helpers++; + item->rst->pgrp_leader = helper; pr_info("Add a helper %d for restoring PGID %d\n", helper->pid.virt, helper->pgid);