Commit 49c1d436 authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

pstree: move all code about pstree in a separate file

Signed-off-by: 's avatarAndrey Vagin <avagin@openvz.org>
Looks-cool-to: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 18cd7406
...@@ -56,6 +56,7 @@ OBJS += eventfd.o ...@@ -56,6 +56,7 @@ OBJS += eventfd.o
OBJS += eventpoll.o OBJS += eventpoll.o
OBJS += mount.o OBJS += mount.o
OBJS += inotify.o OBJS += inotify.o
OBJS += pstree.o
DEPS := $(patsubst %.o,%.d,$(OBJS)) DEPS := $(patsubst %.o,%.d,$(OBJS))
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "eventfd.h" #include "eventfd.h"
#include "eventpoll.h" #include "eventpoll.h"
#include "inotify.h" #include "inotify.h"
#include "pstree.h"
#ifndef CONFIG_X86_64 #ifndef CONFIG_X86_64
# error No x86-32 support yet # error No x86-32 support yet
...@@ -49,26 +50,6 @@ ...@@ -49,26 +50,6 @@
static char loc_buf[PAGE_SIZE]; static char loc_buf[PAGE_SIZE];
static struct pstree_item *root_item = NULL;
static void free_pstree(struct pstree_item *root_item)
{
struct pstree_item *item = root_item, *parent;
while (item) {
if (!list_empty(&item->children)) {
item = list_first_entry(&item->children, struct pstree_item, list);
continue;
}
parent = item->parent;
list_del(&item->list);
xfree(item->threads);
xfree(item);
item = parent;
}
}
void free_mappings(struct list_head *vma_area_list) void free_mappings(struct list_head *vma_area_list)
{ {
struct vma_area *vma_area, *p; struct vma_area *vma_area, *p;
...@@ -888,24 +869,6 @@ err: ...@@ -888,24 +869,6 @@ err:
return -1; return -1;
} }
struct pstree_item *__alloc_pstree_item(bool rst)
{
struct pstree_item *item;
item = xzalloc(sizeof(*item) + (rst ? sizeof(item->rst[0]) : 0));
if (!item)
return NULL;
INIT_LIST_HEAD(&item->children);
item->threads = NULL;
item->nr_threads = 0;
item->pid.virt = -1;
item->pid.real = -1;
item->born_sid = -1;
return item;
}
static int get_children(struct pstree_item *item) static int get_children(struct pstree_item *item)
{ {
u32 *ch; u32 *ch;
...@@ -939,30 +902,6 @@ static void unseize_task_and_threads(const struct pstree_item *item, int st) ...@@ -939,30 +902,6 @@ static void unseize_task_and_threads(const struct pstree_item *item, int st)
unseize_task(item->threads[i].real, st); /* item->pid will be here */ unseize_task(item->threads[i].real, st); /* item->pid will be here */
} }
struct pstree_item *pstree_item_next(struct pstree_item *item)
{
if (!list_empty(&item->children)) {
item = list_first_entry(&item->children, struct pstree_item, list);
return item;
}
while (1) {
if (item->parent == NULL) {
item = NULL;
break;
}
if (item->list.next == &item->parent->children) {
item = item->parent;
continue;
} else {
item = list_entry(item->list.next, struct pstree_item, list);
break;
}
}
return item;
}
static void pstree_switch_state(struct pstree_item *root_item, int st) static void pstree_switch_state(struct pstree_item *root_item, int st)
{ {
struct pstree_item *item = root_item; struct pstree_item *item = root_item;
...@@ -1171,47 +1110,6 @@ try_again: ...@@ -1171,47 +1110,6 @@ try_again:
return ret; return ret;
} }
static int dump_pstree(struct pstree_item *root_item)
{
struct pstree_item *item = root_item;
struct pstree_entry e;
int ret = -1, i;
int pstree_fd;
pr_info("\n");
pr_info("Dumping pstree (pid: %d)\n", root_item->pid.real);
pr_info("----------------------------------------\n");
pstree_fd = open_image(CR_FD_PSTREE, O_DUMP);
if (pstree_fd < 0)
return -1;
for_each_pstree_item(item) {
pr_info("Process: %d(%d)\n", item->pid.virt, item->pid.real);
e.pid = item->pid.virt;
e.ppid = item->parent ? item->parent->pid.virt : 0;
e.pgid = item->pgid;
e.sid = item->sid;
e.nr_threads = item->nr_threads;
if (write_img(pstree_fd, &e))
goto err;
for (i = 0; i < item->nr_threads; i++) {
if (write_img_buf(pstree_fd,
&item->threads[i].virt, sizeof(u32)))
goto err;
}
}
ret = 0;
err:
pr_info("----------------------------------------\n");
close(pstree_fd);
return ret;
}
static int dump_task_thread(struct parasite_ctl *parasite_ctl, struct pid *tid) static int dump_task_thread(struct parasite_ctl *parasite_ctl, struct pid *tid)
{ {
struct core_entry *core; struct core_entry *core;
......
...@@ -46,11 +46,9 @@ ...@@ -46,11 +46,9 @@
#include "shmem.h" #include "shmem.h"
#include "mount.h" #include "mount.h"
#include "inotify.h" #include "inotify.h"
#include "pstree.h"
static struct task_entries *task_entries;
static struct pstree_item *me; static struct pstree_item *me;
static struct pstree_item *root_item = NULL;
static int restore_task_with_children(void *); static int restore_task_with_children(void *);
static int sigreturn_restore(pid_t pid, struct list_head *vmas, int nr_vmas); static int sigreturn_restore(pid_t pid, struct list_head *vmas, int nr_vmas);
...@@ -69,219 +67,6 @@ static int shmem_remap(void *old_addr, void *new_addr, unsigned long size) ...@@ -69,219 +67,6 @@ static int shmem_remap(void *old_addr, void *new_addr, unsigned long size)
return 0; return 0;
} }
static int max_pid = 0;
static int prepare_pstree(void)
{
int ret = 0, i, ps_fd;
struct pstree_item *pi, *parent = NULL;
pr_info("Reading image tree\n");
task_entries = mmap(NULL, TASK_ENTRIES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0);
if (task_entries == MAP_FAILED) {
pr_perror("Can't map shmem");
return -1;
}
task_entries->nr = 0;
task_entries->nr_tasks = 0;
futex_set(&task_entries->start, CR_STATE_FORKING);
ps_fd = open_image_ro(CR_FD_PSTREE);
if (ps_fd < 0)
return ps_fd;
while (1) {
struct pstree_entry e;
ret = read_img_eof(ps_fd, &e);
if (ret <= 0)
break;
ret = -1;
pi = alloc_pstree_item_with_rst();
if (pi == NULL)
break;
pi->pid.virt = e.pid;
if (e.pid > max_pid)
max_pid = e.pid;
pi->pgid = e.pgid;
if (e.pgid > max_pid)
max_pid = e.pgid;
pi->sid = e.sid;
if (e.sid > max_pid)
max_pid = e.sid;
if (e.ppid == 0) {
BUG_ON(root_item);
root_item = pi;
pi->parent = NULL;
INIT_LIST_HEAD(&pi->list);
} else {
/*
* Fast path -- if the pstree image is not edited, the
* parent of any item should have already being restored
* and sit among the last item's ancestors.
*/
while (parent) {
if (parent->pid.virt == e.ppid)
break;
parent = parent->parent;
}
if (parent == NULL)
for_each_pstree_item(parent)
if (parent->pid.virt == e.ppid)
break;
if (parent == NULL) {
pr_err("Can't find a parent for %d", pi->pid.virt);
xfree(pi);
break;
}
pi->parent = parent;
list_add(&pi->list, &parent->children);
}
parent = pi;
pi->nr_threads = e.nr_threads;
pi->threads = xmalloc(e.nr_threads * sizeof(struct pid));
if (!pi->threads)
break;
ret = 0;
for (i = 0; i < e.nr_threads; i++) {
ret = read_img_buf(ps_fd, &pi->threads[i].virt, sizeof(u32));
if (ret < 0)
break;
}
if (ret < 0)
break;
task_entries->nr += e.nr_threads;
task_entries->nr_tasks++;
}
if (!ret)
futex_set(&task_entries->nr_in_progress, task_entries->nr_tasks);
close(ps_fd);
return ret;
}
static int prepare_pstree_ids(void)
{
struct pstree_item *item, *child, *helper, *tmp;
LIST_HEAD(helpers);
/*
* 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, list) {
if (item->sid == root_item->sid || item->sid == item->pid.virt)
continue;
helper = alloc_pstree_item();
if (helper == NULL)
return -1;
helper->sid = item->sid;
helper->pgid = item->sid;
helper->pid.virt = item->sid;
helper->state = TASK_HELPER;
helper->parent = root_item;
list_add_tail(&helper->list, &helpers);
pr_info("Add a helper %d for restoring SID %d\n",
helper->pid.virt, helper->sid);
child = list_entry(item->list.prev, struct pstree_item, list);
item = child;
list_for_each_entry_safe_continue(child, tmp, &root_item->children, list) {
if (child->sid != helper->sid)
continue;
if (child->sid == child->pid.virt)
continue;
pr_info("Attach %d to the temporary task %d\n",
child->pid.virt, helper->pid.virt);
child->parent = helper;
list_move(&child->list, &helper->children);
}
}
/* Try to connect helpers to session leaders */
for_each_pstree_item(item) {
if (!item->parent) /* skip the root task */
continue;
if (item->state == TASK_HELPER)
continue;
if (item->sid != item->pid.virt) {
struct pstree_item *parent;
if (item->parent->sid == item->sid)
continue;
/* the task could fork a child before and after setsid() */
parent = item->parent;
while (parent && parent->pid.virt != item->sid) {
if (parent->born_sid != -1 && parent->born_sid != item->sid) {
pr_err("Can't determing with which sid (%d or %d)"
"the process %d was born\n",
parent->born_sid, item->sid, parent->pid.virt);
return -1;
}
parent->born_sid = item->sid;
pr_info("%d was born with sid %d\n", parent->pid.virt, item->sid);
parent = parent->parent;
}
if (parent == NULL) {
pr_err("Can't find a session leader for %d\n", item->sid);
return -1;
}
continue;
}
pr_info("Session leader %d\n", item->sid);
/* Try to find helpers, who should be connected to the leader */
list_for_each_entry(child, &helpers, list) {
if (child->state != TASK_HELPER)
continue;
if (child->sid != item->sid)
continue;
child->pgid = item->pgid;
child->pid.virt = ++max_pid;
child->parent = item;
list_move(&child->list, &item->children);
pr_info("Attach %d to the task %d\n",
child->pid.virt, item->pid.virt);
break;
}
}
/* All other helpers are session leaders for own sessions */
list_splice(&helpers, &root_item->children);
return 0;
}
static int prepare_shared(void) static int prepare_shared(void)
{ {
int ret = 0; int ret = 0;
...@@ -478,6 +263,7 @@ static int pstree_wait_helpers() ...@@ -478,6 +263,7 @@ static int pstree_wait_helpers()
return 0; return 0;
} }
static int restore_one_alive_task(int pid) static int restore_one_alive_task(int pid)
{ {
pr_info("Restoring resources\n"); pr_info("Restoring resources\n");
...@@ -547,6 +333,8 @@ static inline int sig_fatal(int sig) ...@@ -547,6 +333,8 @@ static inline int sig_fatal(int sig)
return (sig > 0) && (sig < SIGMAX) && (SIG_FATAL_MASK & (1 << sig)); return (sig > 0) && (sig < SIGMAX) && (SIG_FATAL_MASK & (1 << sig));
} }
struct task_entries *task_entries;
static int restore_one_fake(int pid) static int restore_one_fake(int pid)
{ {
/* We should wait here, otherwise last_pid will be changed. */ /* We should wait here, otherwise last_pid will be changed. */
...@@ -846,16 +634,6 @@ static void mount_proc(void) ...@@ -846,16 +634,6 @@ static void mount_proc(void)
set_proc_mountpoint(proc_mountpoint); set_proc_mountpoint(proc_mountpoint);
} }
static bool restore_before_setsid(struct pstree_item *child)
{
int csid = child->born_sid == -1 ? child->sid : child->born_sid;
if (child->parent->born_sid == csid)
return true;
return false;
}
static int restore_task_with_children(void *_arg) static int restore_task_with_children(void *_arg)
{ {
struct cr_clone_arg *ca = _arg; struct cr_clone_arg *ca = _arg;
...@@ -1038,8 +816,25 @@ out: ...@@ -1038,8 +816,25 @@ out:
return 0; return 0;
} }
static int prepare_task_entries()
{
task_entries = mmap(NULL, TASK_ENTRIES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, 0, 0);
if (task_entries == MAP_FAILED) {
pr_perror("Can't map shmem");
return -1;
}
task_entries->nr = 0;
task_entries->nr_tasks = 0;
futex_set(&task_entries->start, CR_STATE_FORKING);
return 0;
}
static int restore_all_tasks(pid_t pid, struct cr_options *opts) static int restore_all_tasks(pid_t pid, struct cr_options *opts)
{ {
if (prepare_task_entries() < 0)
return -1;
if (prepare_pstree() < 0) if (prepare_pstree() < 0)
return -1; return -1;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "image.h" #include "image.h"
#include "uts_ns.h" #include "uts_ns.h"
#include "ipc_ns.h" #include "ipc_ns.h"
#include "pstree.h"
#define DEF_PAGES_PER_LINE 6 #define DEF_PAGES_PER_LINE 6
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "util-net.h" #include "util-net.h"
#include "lock.h" #include "lock.h"
#include "sockets.h" #include "sockets.h"
#include "pstree.h"
static struct fdinfo_list_entry *fdinfo_list; static struct fdinfo_list_entry *fdinfo_list;
static int nr_fdinfo_list; static int nr_fdinfo_list;
......
...@@ -182,28 +182,6 @@ struct pid ...@@ -182,28 +182,6 @@ struct pid
u32 virt; /* used all over in the images and saved after restore */ u32 virt; /* used all over in the images and saved after restore */
}; };
struct pstree_item {
struct list_head list;
struct pid pid;
struct pstree_item *parent;
struct list_head children; /* array of children */
pid_t pgid;
pid_t sid;
pid_t born_sid;
int state; /* TASK_XXX constants */
int nr_threads; /* number of threads */
struct pid *threads; /* array of threads */
struct rst_info rst[0];
};
extern struct pstree_item *__alloc_pstree_item(bool rst);
#define alloc_pstree_item() __alloc_pstree_item(false)
#define alloc_pstree_item_with_rst() __alloc_pstree_item(true)
extern struct pstree_item *pstree_item_next(struct pstree_item *item);
#define for_each_pstree_item(pi) \
for (pi = root_item; pi != NULL; pi = pstree_item_next(pi))
static inline int in_vma_area(struct vma_area *vma, unsigned long addr) static inline int in_vma_area(struct vma_area *vma, unsigned long addr)
{ {
return addr >= (unsigned long)vma->vma.start && return addr >= (unsigned long)vma->vma.start &&
......
#ifndef PSTREE_H__
#define PSTREE_H__
#include "list.h"
#include "crtools.h"
struct pstree_item {
struct list_head list;
struct pid pid;
struct pstree_item *parent;
struct list_head children; /* array of children */
pid_t pgid;
pid_t sid;
pid_t born_sid;
int state; /* TASK_XXX constants */
int nr_threads; /* number of threads */
struct pid *threads; /* array of threads */
struct rst_info rst[0];
};
extern void free_pstree(struct pstree_item *root_item);
extern struct pstree_item *__alloc_pstree_item(bool rst);
#define alloc_pstree_item() __alloc_pstree_item(false)
#define alloc_pstree_item_with_rst() __alloc_pstree_item(true)
extern struct pstree_item *root_item;
extern struct pstree_item *pstree_item_next(struct pstree_item *item);
#define for_each_pstree_item(pi) \
for (pi = root_item; pi != NULL; pi = pstree_item_next(pi))
extern bool restore_before_setsid(struct pstree_item *child);
extern int prepare_pstree(void);
extern int prepare_pstree_ids(void);
extern int dump_pstree(struct pstree_item *root_item);
struct task_entries;
extern struct task_entries *task_entries;
#endif
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include "pstree.h"
#include "restorer.h"
#include "util.h"
struct pstree_item *root_item;
void free_pstree(struct pstree_item *root_item)
{
struct pstree_item *item = root_item, *parent;
while (item) {
if (!list_empty(&item->children)) {
item = list_first_entry(&item->children, struct pstree_item, list);
continue;
}
parent = item->parent;
list_del(&item->list);
xfree(item->threads);
xfree(item);
item = parent;
}
}
struct pstree_item *__alloc_pstree_item(bool rst)
{
struct pstree_item *item;
item = xzalloc(sizeof(*item) + (rst ? sizeof(item->rst[0]) : 0));
if (!item)
return NULL;
INIT_LIST_HEAD(&item->children);
item->threads = NULL;
item->nr_threads = 0;
item->pid.virt = -1;
item->pid.real = -1;
item->born_sid = -1;
return item;
}
struct pstree_item *pstree_item_next(struct pstree_item *item)
{
if (!list_empty(&item->children)) {
item = list_first_entry(&item->children, struct pstree_item, list);
return item;
}
while (1) {
if (item->parent == NULL) {
item = NULL;
break;
}
if (item->list.next == &item->parent->children) {
item = item->parent;
continue;
} else {
item = list_entry(item->list.next, struct pstree_item, list);
break;
}
}
return item;
}
int dump_pstree(struct pstree_item *root_item)
{
struct pstree_item *item = root_item;
struct pstree_entry e;
int ret = -1, i;
int pstree_fd;
pr_info("\n");
pr_info("Dumping pstree (pid: %d)\n", root_item->pid.real);
pr_info("----------------------------------------\n");
pstree_fd = open_image(CR_FD_PSTREE, O_DUMP);
if (pstree_fd < 0)
return -1;
for_each_pstree_item(item) {
pr_info("Process: %d(%d)\n", item->pid.virt, item->pid.real);
e.pid = item->pid.virt;
e.ppid = item->parent ? item->parent->pid.virt : 0;
e.pgid = item->pgid;
e.sid = item->sid;
e.nr_threads = item->nr_threads;
if (write_img(pstree_fd, &e))
goto err;
for (i = 0; i < item->nr_threads; i++) {
if (write_img_buf(pstree_fd,
&item->threads[i].virt, sizeof(u32)))
goto err;
}
}
ret = 0;
err:
pr_info("----------------------------------------\n");
close(pstree_fd);
return ret;
}
static int max_pid = 0;
int prepare_pstree(void)
{
int ret = 0, i, ps_fd;
struct pstree_item *pi, *parent = NULL;
pr_info("Reading image tree\n");
ps_fd = open_image_ro(CR_FD_PSTREE);
if (ps_fd < 0)
return ps_fd;
while (1) {
struct pstree_entry e;
ret = read_img_eof(ps_fd, &e);
if (ret <= 0)
break;
ret = -1;
pi = alloc_pstree_item_with_rst();
if (pi == NULL)
break;
pi->pid.virt = e.pid;
if (e.pid > max_pid)
max_pid = e.pid;
pi->pgid = e.pgid;
if (e.pgid > max_pid)
max_pid = e.pgid;
pi->sid = e.sid;
if (e.sid > max_pid)
max_pid = e.sid;
if (e.ppid == 0) {
BUG_ON(root_item);
root_item = pi;
pi->parent = NULL;
INIT_LIST_HEAD(&pi->list);
} else {
/*
* Fast path -- if the pstree image is not edited, the
* parent of any item should have already being restored
* and sit among the last item's ancestors.
*/
while (parent) {
if (parent->pid.virt == e.ppid)
break;
parent = parent->parent;
}
if (parent == NULL)
for_each_pstree_item(parent)
if (parent->pid.virt == e.ppid)
break;
if (parent == NULL) {
pr_err("Can't find a parent for %d", pi->pid.virt);
xfree(pi);
break;
}
pi->parent = parent;
list_add(&pi->list, &parent->children);
}
parent = pi;
pi->nr_threads = e.nr_threads;
pi->threads = xmalloc(e.nr_threads * sizeof(struct pid));
if (!pi->threads)
break;
ret = 0;
for (i = 0; i < e.nr_threads; i++) {
ret = read_img_buf(ps_fd, &pi->threads[i].virt, sizeof(u32));
if (ret < 0)
break;
}
if (ret < 0)
break;
task_entries->nr += e.nr_threads;
task_entries->nr_tasks++;
}
if (!ret)
futex_set(&task_entries->nr_in_progress, task_entries->nr_tasks);
close(ps_fd);
return ret;
}
int prepare_pstree_ids(void)
{
struct pstree_item *item, *child, *helper, *tmp;
LIST_HEAD(helpers);
/*
* 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, list) {
if (item->sid == root_item->sid || item->sid == item->pid.virt)
continue;
helper = alloc_pstree_item();
if (helper == NULL)
return -1;
helper->sid = item->sid;
helper->pgid = item->sid;
helper->pid.virt = item->sid;
helper->state = TASK_HELPER;
helper->parent = root_item;
list_add_tail(&helper->list, &helpers);
pr_info("Add a helper %d for restoring SID %d\n",
helper->pid.virt, helper->sid);
child = list_entry(item->list.prev, struct pstree_item, list);
item = child;
list_for_each_entry_safe_continue(child, tmp, &root_item->children, list) {
if (child->sid != helper->sid)
continue;
if (child->sid == child->pid.virt)
continue;
pr_info("Attach %d to the temporary task %d\n",
child->pid.virt, helper->pid.virt);
child->parent = helper;
list_move(&child->list, &helper->children);
}
}
/* Try to connect helpers to session leaders */
for_each_pstree_item(item) {
if (!item->parent) /* skip the root task */
continue;
if (item->state == TASK_HELPER)
continue;
if (item->sid != item->pid.virt) {
struct pstree_item *parent;
if (item->parent->sid == item->sid)
continue;
/* the task could fork a child before and after setsid() */
parent = item->parent;
while (parent && parent->pid.virt != item->sid) {
if (parent->born_sid != -1 && parent->born_sid != item->sid) {
pr_err("Can't determing with which sid (%d or %d)"
"the process %d was born\n",
parent->born_sid, item->sid, parent->pid.virt);
return -1;
}
parent->born_sid = item->sid;
pr_info("%d was born with sid %d\n", parent->pid.virt, item->sid);
parent = parent->parent;
}
if (parent == NULL) {
pr_err("Can't find a session leader for %d\n", item->sid);
return -1;
}
continue;
}
pr_info("Session leader %d\n", item->sid);
/* Try to find helpers, who should be connected to the leader */
list_for_each_entry(child, &helpers, list) {
if (child->state != TASK_HELPER)
continue;
if (child->sid != item->sid)
continue;
child->pgid = item->pgid;
child->pid.virt = ++max_pid;
child->parent = item;
list_move(&child->list, &item->children);
pr_info("Attach %d to the task %d\n",
child->pid.virt, item->pid.virt);
break;
}
}
/* All other helpers are session leaders for own sessions */
list_splice(&helpers, &root_item->children);
return 0;
}
bool restore_before_setsid(struct pstree_item *child)
{
int csid = child->born_sid == -1 ? child->sid : child->born_sid;
if (child->parent->born_sid == csid)
return true;
return false;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment