2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-09-02 15:25:21 +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

254
cr-dump.c
View File

@@ -724,7 +724,7 @@ err:
return ret; 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; struct dirent *de;
DIR *dir; DIR *dir;
@@ -733,7 +733,7 @@ static int parse_threads(struct pstree_item *item, int pid_dir)
dir = opendir_proc(pid_dir, "task"); dir = opendir_proc(pid_dir, "task");
if (!dir) { if (!dir) {
pr_perror("Can't open %d/task", item->pid); pr_perror("Can't open %d/task", pid);
return -1; return -1;
} }
@@ -762,7 +762,7 @@ static int parse_threads(struct pstree_item *item, int pid_dir)
return 0; 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; FILE *file;
char *tok; 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]); file = fopen_proc(pid_dir, "task/%d/children", item->threads[i]);
if (!file) { if (!file) {
pr_perror("Can't open %d children %d", pr_perror("Can't open %d children %d",
item->pid, item->threads[i]); pid, item->threads[i]);
goto err; goto err;
} }
@@ -806,154 +806,91 @@ err:
return -1; 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; struct pstree_item *item;
item = xzalloc(sizeof(*item)); item = xzalloc(sizeof(*item));
if (!item) if (!item)
goto err; goto err;
ret = seize_task(pid); if (parse_threads(pid, pid_dir, item))
if (ret < 0) goto err_free;
if (parse_children(pid, pid_dir, item))
goto err_free; goto err_free;
pr_info("Seized task %d, state %d\n", pid, ret);
item->pid = pid; 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); list_add_tail(&item->list, list);
pr_info("Collected %d in %d state\n", item->pid, item->state);
return item; return item;
err_close:
close(pid_dir);
err_free: err_free:
xfree(item->children);
xfree(item->threads); xfree(item->threads);
xfree(item->children);
xfree(item); xfree(item);
err: err:
return NULL; 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; struct pstree_item *item;
int i;
pr_info("Collecting tasks starting from %d\n", pid); /*
item = collect_task(pid, pstree_list); * Since ptrace-seize doesn't work on frozen tasks
if (item == NULL) * we stick with explicit tasks stopping via stop
return -1; * signal, but in future it's aimed to switch to
* kernel freezer.
*/
if (leader_only) list_for_each_entry(item, list, list) {
return 0; kill(item->pid, state_sigs[state]);
if (leader_only)
break;
}
}
for (i = 0; i < item->nr_children; i++) static int collect_pstree(pid_t pid, struct list_head *pstree_list)
if (collect_pstree(item->children[i], pstree_list, 0) < 0) {
return -1; 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) 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) if (!core)
goto err; 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 ... "); pr_info("Dumping GP/FPU registers ... ");
ret = get_task_regs(pid, core); ret = get_task_regs(pid, core);
if (ret) if (ret)
goto err_free; goto err_free;
pr_info("OK\n"); 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.task_state = TASK_ALIVE;
core->tc.exit_code = 0; 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; 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); cr_fdset = cr_fdset_open(item->pid, CR_FD_DESC_CORE, cr_fdset);
if (cr_fdset == NULL) if (cr_fdset == NULL)
return -1; 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("Dumping task (pid: %d)\n", pid);
pr_info("========================================\n"); pr_info("========================================\n");
if (item->state == TASK_STOPPED) {
pr_err("Stopped tasks are not supported\n");
goto err;
}
pid_dir = open_pid_proc(pid); pid_dir = open_pid_proc(pid);
if (pid_dir < 0) { if (pid_dir < 0) {
pr_perror("Can't open %d proc dir", pid); 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) if (ret < 0)
goto err; goto err;
if (item->state == TASK_DEAD) switch (pps_buf.state) {
case 'Z':
return dump_one_zombie(item, &pps_buf, cr_fdset); 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; ret = -1;
if (!cr_fdset_open(item->pid, CR_FD_DESC_TASK, cr_fdset)) 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; 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); ret = dump_task_core_seized(pid, pid_dir, &pps_buf, cr_fdset);
if (ret) { if (ret) {
pr_err("Dump core (pid: %d) failed with %d\n", pid, 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; 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); ret = dump_task_files(pid, pid_dir, cr_fdset);
if (ret) { if (ret) {
pr_err("Dump files (pid: %d) failed with %d\n", pid, 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("Dumping process (pid: %d)\n", pid);
pr_info("========================================\n"); pr_info("========================================\n");
if (collect_pstree(pid, &pstree_list, opts->leader_only)) if (collect_pstree(pid, &pstree_list))
goto err; goto err;
if (opts->namespaces_flags) { if (opts->namespaces_flags) {
@@ -1390,8 +1367,17 @@ int cr_dump_tasks(pid_t pid, struct cr_options *opts)
ret = 0; ret = 0;
err: 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); free_pstree(&pstree_list);
close_cr_fdset(&cr_fdset); close_cr_fdset(&cr_fdset);
return ret; return ret;

View File

@@ -289,7 +289,7 @@ int main(int argc, char *argv[])
int action = -1; int action = -1;
int log_inited = 0; 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); 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; 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)) { opt = getopt_long(argc - 1, argv + 1, short_opts, NULL, &idx)) {
switch (opt) { switch (opt) {
case 's':
opts.final_state = CR_TASK_STOP;
break;
case 'p': case 'p':
pid = atoi(optarg); pid = atoi(optarg);
opts.leader_only = true; opts.leader_only = true;
@@ -319,6 +316,7 @@ int main(int argc, char *argv[])
break; break;
case 'c': case 'c':
opts.show_pages_content = true; opts.show_pages_content = true;
opts.final_state = CR_TASK_RUN;
break; break;
case 'f': case 'f':
opts.show_dump_file = optarg; opts.show_dump_file = optarg;
@@ -400,9 +398,10 @@ usage:
printk(" -p checkpoint/restore only a single process identified by pid\n"); 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(" -t checkpoint/restore the whole process tree identified by pid\n");
printk(" -f show contents of a checkpoint file\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(" -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(" -n checkpoint/restore namespaces - values must be separated by comma\n");
printk(" supported: uts, ipc\n"); printk(" supported: uts, ipc\n");

View File

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

View File

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

View File

@@ -4,12 +4,6 @@
#define PROC_TASK_COMM_LEN 32 #define PROC_TASK_COMM_LEN 32
#define PROC_TASK_COMM_LEN_FMT "(%31s" #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 { struct proc_pid_stat {
int pid; int pid;
char comm[PROC_TASK_COMM_LEN]; 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(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_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 *); extern int parse_pid_status(int pid_dir, struct proc_status_creds *);

View File

@@ -33,7 +33,7 @@
#define PTRACE_O_TRACEEXIT 0x00000040 #define PTRACE_O_TRACEEXIT 0x00000040
extern int seize_task(pid_t pid); 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_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_poke_area(pid_t pid, void *src, void *addr, long bytes);
extern int ptrace_show_area(pid_t pid, 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/wait.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "crtools.h"
#include "compiler.h" #include "compiler.h"
#include "syscall.h" #include "syscall.h"
#include "types.h" #include "types.h"

View File

@@ -175,36 +175,6 @@ err_bogus_mapping:
goto err; 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) int parse_pid_stat(pid_t pid, int pid_dir, struct proc_pid_stat *s)
{ {
FILE *f; FILE *f;

View File

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