2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-31 14:25:49 +00:00

Revert "ctrools: Rewrite task/threads stopping engine"

This reverts commit 6da51eee3f.

It breaks transition/file_read test case
This commit is contained in:
Cyrill Gorcunov
2012-02-01 19:00:53 +04:00
parent 57454c524b
commit 63b88720a3
9 changed files with 153 additions and 228 deletions

252
cr-dump.c
View File

@@ -724,7 +724,7 @@ err:
return ret;
}
static int parse_threads(struct pstree_item *item, int pid_dir)
static int parse_threads(pid_t pid, int pid_dir, struct pstree_item *item)
{
struct dirent *de;
DIR *dir;
@@ -733,7 +733,7 @@ static int parse_threads(struct pstree_item *item, int pid_dir)
dir = opendir_proc(pid_dir, "task");
if (!dir) {
pr_perror("Can't open %d/task", item->pid);
pr_perror("Can't open %d/task", pid);
return -1;
}
@@ -762,7 +762,7 @@ static int parse_threads(struct pstree_item *item, int pid_dir)
return 0;
}
static int parse_children(struct pstree_item *item, int pid_dir)
static int parse_children(pid_t pid, int pid_dir, struct pstree_item *item)
{
FILE *file;
char *tok;
@@ -774,7 +774,7 @@ static int parse_children(struct pstree_item *item, int pid_dir)
file = fopen_proc(pid_dir, "task/%d/children", item->threads[i]);
if (!file) {
pr_perror("Can't open %d children %d",
item->pid, item->threads[i]);
pid, item->threads[i]);
goto err;
}
@@ -806,154 +806,91 @@ err:
return -1;
}
static void unseize_task_and_threads(struct pstree_item *item, enum cr_task_state st)
static struct pstree_item *add_pstree_entry(pid_t pid, int pid_dir, struct list_head *list)
{
int i;
for (i = 0; i < item->nr_threads; i++)
unseize_task(item->threads[i], st); /* item->pid will be here */
}
static void pstree_switch_state(struct list_head *list, struct cr_options *opts)
{
struct pstree_item *item;
list_for_each_entry(item, list, list) {
unseize_task_and_threads(item, opts->final_state);
if (opts->leader_only)
break;
}
}
static int seize_threads(struct pstree_item *item)
{
int i = 0, ret;
if ((item->state == TASK_DEAD) && (item->nr_threads > 1)) {
pr_err("Zombies with threads are not supported\n");
goto err;
}
for (i = 0; i < item->nr_threads; i++) {
if (item->pid == item->threads[i])
continue;
pr_info("\tSeizing %d's %d thread\n", item->pid, item->threads[i]);
ret = seize_task(item->threads[i]);
if (ret < 0)
goto err;
if (ret == TASK_SHOULD_BE_DEAD) {
pr_err("Potentially zombie thread not supported\n");
goto err;
}
if (ret == TASK_STOPPED) {
pr_err("Stopped threads not supported\n");
goto err;
}
}
return 0;
err:
for (i--; i >= 0; i--) {
if (item->pid == item->threads[i])
continue;
unseize_task(item->threads[i], CR_TASK_STOP /* FIXME */);
}
return -1;
}
static struct pstree_item *collect_task(pid_t pid, struct list_head *list)
{
int ret, pid_dir;
struct pstree_item *item;
item = xzalloc(sizeof(*item));
if (!item)
goto err;
ret = seize_task(pid);
if (ret < 0)
if (parse_threads(pid, pid_dir, item))
goto err_free;
if (parse_children(pid, pid_dir, item))
goto err_free;
pr_info("Seized task %d, state %d\n", pid, ret);
item->pid = pid;
item->state = ret;
pid_dir = open_pid_proc(pid);
if (pid_dir < 0)
goto err_free;
if (item->state == TASK_SHOULD_BE_DEAD) {
struct proc_pid_stat_small ps;
ret = parse_pid_stat_small(pid, pid_dir, &ps);
if (ret < 0)
goto err_close;
if (ps.state != 'Z') {
pr_err("Unseizeable non-zombie %d found, state %c\n",
item->pid, ps.state);
goto err_close;
}
item->state = TASK_DEAD;
}
ret = parse_threads(item, pid_dir);
if (ret < 0)
goto err_close;
ret = seize_threads(item);
if (ret < 0)
goto err_close;
ret = parse_children(item, pid_dir);
if (ret < 0)
goto err_close;
if ((item->state == TASK_DEAD) && (item->nr_children > 0)) {
pr_err("Zombie with children?! O_o Run, run, run!\n");
goto err_close;
}
close(pid_dir);
list_add_tail(&item->list, list);
pr_info("Collected %d in %d state\n", item->pid, item->state);
return item;
err_close:
close(pid_dir);
err_free:
xfree(item->children);
xfree(item->threads);
xfree(item->children);
xfree(item);
err:
return NULL;
}
static int collect_pstree(pid_t pid, struct list_head *pstree_list, int leader_only)
static const int state_sigs[] = {
[CR_TASK_STOP] = SIGSTOP,
[CR_TASK_RUN] = SIGCONT,
[CR_TASK_KILL] = SIGKILL,
};
static int ps_switch_state(int pid, enum cr_task_state state)
{
return kill(pid, state_sigs[state]);
}
static void pstree_switch_state(struct list_head *list,
enum cr_task_state state, int leader_only)
{
struct pstree_item *item;
int i;
pr_info("Collecting tasks starting from %d\n", pid);
item = collect_task(pid, pstree_list);
if (item == NULL)
return -1;
/*
* Since ptrace-seize doesn't work on frozen tasks
* we stick with explicit tasks stopping via stop
* signal, but in future it's aimed to switch to
* kernel freezer.
*/
list_for_each_entry(item, list, list) {
kill(item->pid, state_sigs[state]);
if (leader_only)
return 0;
break;
}
}
for (i = 0; i < item->nr_children; i++)
if (collect_pstree(item->children[i], pstree_list, 0) < 0)
return -1;
static int collect_pstree(pid_t pid, struct list_head *pstree_list)
{
struct pstree_item *item;
unsigned long i;
int pid_dir;
int ret = -1;
return 0;
pid_dir = open_pid_proc(pid);
if (pid_dir < 0)
goto err;
if (ps_switch_state(pid, CR_TASK_STOP))
goto err;
item = add_pstree_entry(pid, pid_dir, pstree_list);
if (!item)
goto err;
for (i = 0; i < item->nr_children; i++) {
ret = collect_pstree(item->children[i], pstree_list);
if (ret)
goto err_close;
}
ret = 0;
err_close:
close(pid_dir);
err:
return ret;
}
static int dump_pstree(pid_t pid, struct list_head *pstree_list, struct cr_fdset *cr_fdset)
@@ -1148,12 +1085,25 @@ static int dump_task_thread(pid_t pid, struct cr_fdset *cr_fdset)
if (!core)
goto err;
ret = seize_task(pid);
if (ret) {
pr_err("Failed to seize thread (pid: %d) with %d\n",
pid, ret);
goto err_free;
}
pr_info("Dumping GP/FPU registers ... ");
ret = get_task_regs(pid, core);
if (ret)
goto err_free;
pr_info("OK\n");
ret = unseize_task(pid);
if (ret) {
pr_err("Can't unsieze thread (pid: %d)\n", pid);
goto err_free;
}
core->tc.task_state = TASK_ALIVE;
core->tc.exit_code = 0;
@@ -1172,6 +1122,16 @@ static int dump_one_zombie(struct pstree_item *item, struct proc_pid_stat *pps,
{
struct core_entry *core;
if (item->nr_children) {
pr_err("Zombie %d with children.\n", item->pid);
return -1;
}
if (item->nr_threads > 1) {
pr_err("Zombie %d with threads.\n", item->pid);
return -1;
}
cr_fdset = cr_fdset_open(item->pid, CR_FD_DESC_CORE, cr_fdset);
if (cr_fdset == NULL)
return -1;
@@ -1231,11 +1191,6 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
pr_info("Dumping task (pid: %d)\n", pid);
pr_info("========================================\n");
if (item->state == TASK_STOPPED) {
pr_err("Stopped tasks are not supported\n");
goto err;
}
pid_dir = open_pid_proc(pid);
if (pid_dir < 0) {
pr_perror("Can't open %d proc dir", pid);
@@ -1247,8 +1202,17 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
if (ret < 0)
goto err;
if (item->state == TASK_DEAD)
switch (pps_buf.state) {
case 'Z':
return dump_one_zombie(item, &pps_buf, cr_fdset);
case 'T':
/* Stopped -- can dump one */
break;
default:
ret = -1;
pr_err("Task in bad state: %c\n", pps_buf.state);
goto err;
};
ret = -1;
if (!cr_fdset_open(item->pid, CR_FD_DESC_TASK, cr_fdset))
@@ -1260,6 +1224,13 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
goto err;
}
ret = seize_task(pid);
if (ret) {
pr_err("Failed to seize task (pid: %d) with %d\n",
pid, ret);
goto err;
}
ret = dump_task_core_seized(pid, pid_dir, &pps_buf, cr_fdset);
if (ret) {
pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
@@ -1302,6 +1273,12 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
goto err;
}
ret = unseize_task(pid);
if (ret) {
pr_err("Can't unsieze (pid: %d) task\n", pid);
goto err;
}
ret = dump_task_files(pid, pid_dir, cr_fdset);
if (ret) {
pr_err("Dump files (pid: %d) failed with %d\n", pid, ret);
@@ -1349,7 +1326,7 @@ int cr_dump_tasks(pid_t pid, struct cr_options *opts)
pr_info("Dumping process (pid: %d)\n", pid);
pr_info("========================================\n");
if (collect_pstree(pid, &pstree_list, opts->leader_only))
if (collect_pstree(pid, &pstree_list))
goto err;
if (opts->namespaces_flags) {
@@ -1390,8 +1367,17 @@ int cr_dump_tasks(pid_t pid, struct cr_options *opts)
ret = 0;
err:
pstree_switch_state(&pstree_list, opts);
switch (opts->final_state) {
case CR_TASK_RUN:
case CR_TASK_KILL:
pstree_switch_state(&pstree_list,
opts->final_state, opts->leader_only);
case CR_TASK_STOP: /* they are already stopped */
break;
}
free_pstree(&pstree_list);
close_cr_fdset(&cr_fdset);
return ret;

View File

@@ -289,7 +289,7 @@ int main(int argc, char *argv[])
int action = -1;
int log_inited = 0;
static const char short_opts[] = "dsf:p:t:hcD:o:n:";
static const char short_opts[] = "df:p:t:hcD:o:n:";
BUILD_BUG_ON(PAGE_SIZE != PAGE_IMAGE_SIZE);
@@ -306,9 +306,6 @@ int main(int argc, char *argv[])
for (opt = getopt_long(argc - 1, argv + 1, short_opts, NULL, &idx); opt != -1;
opt = getopt_long(argc - 1, argv + 1, short_opts, NULL, &idx)) {
switch (opt) {
case 's':
opts.final_state = CR_TASK_STOP;
break;
case 'p':
pid = atoi(optarg);
opts.leader_only = true;
@@ -319,6 +316,7 @@ int main(int argc, char *argv[])
break;
case 'c':
opts.show_pages_content = true;
opts.final_state = CR_TASK_RUN;
break;
case 'f':
opts.show_dump_file = optarg;
@@ -400,9 +398,10 @@ usage:
printk(" -p checkpoint/restore only a single process identified by pid\n");
printk(" -t checkpoint/restore the whole process tree identified by pid\n");
printk(" -f show contents of a checkpoint file\n");
printk(" -c show contents of pages dumped in hexdump format\n");
printk(" -c in case of checkpoint -- continue running the process after\n"
" checkpoint complete, in case of showing file contents --\n"
" show contents of pages dumped in hexdump format\n");
printk(" -d detach after restore\n");
printk(" -s leave tasks in stopped state after checkpoint instead of killing them\n");
printk(" -n checkpoint/restore namespaces - values must be separated by comma\n");
printk(" supported: uts, ipc\n");

View File

@@ -43,6 +43,7 @@ enum {
};
enum cr_task_state {
CR_TASK_RUN,
CR_TASK_STOP,
CR_TASK_KILL,
};
@@ -138,7 +139,6 @@ struct vma_area {
struct pstree_item {
struct list_head list;
pid_t pid; /* leader pid */
int state; /* TASK_XXX constants */
u32 nr_children; /* number of children */
u32 nr_threads; /* number of threads */
u32 *threads; /* array of threads */

View File

@@ -317,7 +317,6 @@ struct core_entry {
};
} __packed;
#define TASK_SHOULD_BE_DEAD 0x0
#define TASK_ALIVE 0x1
#define TASK_DEAD 0x2
#define TASK_STOPPED 0x3 /* FIXME - implement */

View File

@@ -4,12 +4,6 @@
#define PROC_TASK_COMM_LEN 32
#define PROC_TASK_COMM_LEN_FMT "(%31s"
struct proc_pid_stat_small {
int pid;
char comm[PROC_TASK_COMM_LEN];
char state;
};
struct proc_pid_stat {
int pid;
char comm[PROC_TASK_COMM_LEN];
@@ -78,7 +72,6 @@ struct proc_status_creds {
};
extern int parse_pid_stat(pid_t pid, int pid_dir, struct proc_pid_stat *s);
extern int parse_pid_stat_small(pid_t pid, int pid_dir, struct proc_pid_stat_small *s);
extern int parse_maps(pid_t pid, int pid_dir, struct list_head *vma_area_list, bool use_map_files);
extern int parse_pid_status(int pid_dir, struct proc_status_creds *);

View File

@@ -33,7 +33,7 @@
#define PTRACE_O_TRACEEXIT 0x00000040
extern int seize_task(pid_t pid);
extern int unseize_task(pid_t pid, enum cr_task_state st);
extern int unseize_task(pid_t pid);
extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
extern int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes);
extern int ptrace_show_area(pid_t pid, void *addr, long bytes);

View File

@@ -15,7 +15,6 @@
#include <sys/wait.h>
#include <sys/socket.h>
#include "crtools.h"
#include "compiler.h"
#include "syscall.h"
#include "types.h"

View File

@@ -175,36 +175,6 @@ err_bogus_mapping:
goto err;
}
int parse_pid_stat_small(pid_t pid, int pid_dir, struct proc_pid_stat_small *s)
{
FILE *f;
char *tok;
int n;
f = fopen_proc(pid_dir, "stat");
if (f == NULL) {
pr_perror("Can't open %d's stat", pid);
return -1;
}
memset(s, 0, sizeof(*s));
n = fscanf(f, "%d " PROC_TASK_COMM_LEN_FMT " %c",
&s->pid, s->comm, &s->state);
if (n < 3) {
pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
return -1;
}
s->comm[PROC_TASK_COMM_LEN-1] = '\0';
tok = strchr(s->comm, ')');
if (tok)
*tok = '\0';
fclose(f);
return 0;
}
int parse_pid_stat(pid_t pid, int pid_dir, struct proc_pid_stat *s)
{
FILE *f;

View File

@@ -13,22 +13,14 @@
#include <sys/resource.h>
#include <sys/wait.h>
#include "crtools.h"
#include "compiler.h"
#include "types.h"
#include "util.h"
#include "ptrace.h"
int unseize_task(pid_t pid, enum cr_task_state st)
int unseize_task(pid_t pid)
{
if (st == CR_TASK_STOP)
return ptrace(PTRACE_DETACH, pid, NULL, NULL);
else if (st == CR_TASK_KILL)
return ptrace(PTRACE_KILL, pid, NULL, NULL);
else {
BUG_ON(1);
return -1;
}
}
/*
@@ -38,61 +30,48 @@ int unseize_task(pid_t pid, enum cr_task_state st)
* of it so the task would not know if it was saddled
* up with someone else.
*/
int seize_task(pid_t pid)
{
siginfo_t si;
int status;
int ret;
int ret = 0;
ret = ptrace(PTRACE_SEIZE, pid, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
if (ret < 0)
return TASK_SHOULD_BE_DEAD; /* Caller should verify it's really dead */
if (ret < 0) {
pr_perror("Can't seize task");
goto err;
}
ret = ptrace(PTRACE_INTERRUPT, pid, NULL, NULL);
if (ret < 0) {
pr_perror("SEIZE %d: can't interrupt task", pid);
pr_perror("Can't interrupt task");
goto err;
}
ret = wait4(pid, &status, __WALL, NULL);
if (ret < 0) {
pr_perror("SEIZE %d: can't wait task", pid);
ret = -10;
if (wait4(pid, &status, __WALL, NULL) != pid)
goto err;
}
if (ret != pid) {
pr_err("SEIZE %d: wrong task attached (%d)\n", pid, ret);
ret = -20;
if (!WIFSTOPPED(status))
goto err;
}
if (!WIFSTOPPED(status)) {
pr_err("SEIZE %d: task not stopped after seize\n", pid);
goto err;
}
jerr_rc(ptrace(PTRACE_GETSIGINFO, pid, NULL, &si), ret, err_cont);
ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &si);
if (ret < 0) {
pr_perror("SEIZE %d: can't read signfo", pid);
goto err;
}
ret = -30;
if ((si.si_code >> 8) != PTRACE_EVENT_STOP)
goto err_cont;
if ((si.si_code >> 8) != PTRACE_EVENT_STOP) {
pr_err("SEIZE %d: wrong stop event received 0x%x\n", pid,
(unsigned int)si.si_code);
goto err;
}
jerr_rc(ptrace(PTRACE_SETOPTIONS, pid, NULL,
(void *)(unsigned long)PTRACE_O_TRACEEXIT), ret, err_cont);
if (si.si_signo == SIGTRAP)
return TASK_ALIVE;
else if (si.si_signo == SIGSTOP)
return TASK_STOPPED;
pr_err("SEIZE %d: unsupported stop signal %d\n", pid, si.si_signo);
err:
unseize_task(pid, CR_TASK_STOP);
return -1;
return ret;
err_cont:
kill(pid, SIGCONT);
goto err;
}
int ptrace_show_area_r(pid_t pid, void *addr, long bytes)