Commit b7de83aa authored by Pavel Emelyanov's avatar Pavel Emelyanov Committed by Cyrill Gorcunov

crtools: Interval timers support

Timers are dumped from inside parasite code, the format is plain -- just
3 pairs of interval/value one-by-one.

The restoration occurs in two stages -- first prepare the timer values in
restorer (and check for sanity), then setup the timers in the latest stage
before actually calling the sigreturn.
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
parent 6ab01f7a
...@@ -1211,6 +1211,12 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset) ...@@ -1211,6 +1211,12 @@ static int dump_one_task(struct pstree_item *item, struct cr_fdset *cr_fdset)
goto err; goto err;
} }
ret = parasite_dump_itimers_seized(parasite_ctl, cr_fdset);
if (ret) {
pr_err("Can't dump itimers (pid: %d)\n", pid);
goto err;
}
ret = parasite_cure_seized(parasite_ctl, &vma_area_list); ret = parasite_cure_seized(parasite_ctl, &vma_area_list);
if (ret) { if (ret) {
pr_err("Can't cure (pid: %d) from parasite\n", pid); pr_err("Can't cure (pid: %d) from parasite\n", pid);
......
...@@ -1510,6 +1510,77 @@ err_or_found: ...@@ -1510,6 +1510,77 @@ err_or_found:
return hint; return hint;
} }
#define USEC_PER_SEC 1000000L
static inline int timeval_valid(struct timeval *tv)
{
return (tv->tv_sec >= 0) && ((unsigned long)tv->tv_usec < USEC_PER_SEC);
}
static inline int itimer_restore_and_fix(char *n, struct itimer_entry *ie,
struct itimerval *val)
{
if (ie->isec == 0 && ie->iusec == 0) {
memzero_p(val);
return 0;
}
val->it_interval.tv_sec = ie->isec;
val->it_interval.tv_usec = ie->iusec;
if (!timeval_valid(&val->it_interval)) {
pr_err("Invalid timer interval\n");
return -1;
}
if (ie->vsec == 0 && ie->vusec == 0) {
/*
* Remaining time was too short. Set it to
* interval to make the timer armed and work.
*/
val->it_value.tv_sec = ie->isec;
val->it_value.tv_usec = ie->iusec;
} else {
val->it_value.tv_sec = ie->vsec;
val->it_value.tv_usec = ie->vusec;
}
if (!timeval_valid(&val->it_value)) {
pr_err("Invalid timer value\n");
return -1;
}
pr_info("Restored %s timer to %ld.%ld -> %ld.%ld\n", n,
val->it_value.tv_sec, val->it_value.tv_usec,
val->it_interval.tv_sec, val->it_interval.tv_usec);
return 0;
}
static int prepare_itimers(int pid, struct task_restore_core_args *args)
{
int fd, ret = -1;
struct itimer_entry ie[3];
fd = open_image_ro(CR_FD_ITIMERS, pid);
if (fd < 0)
return fd;
if (read_img_buf(fd, ie, sizeof(ie)) > 0) {
ret = itimer_restore_and_fix("real",
&ie[0], &args->itimers[0]);
if (!ret)
ret = itimer_restore_and_fix("virt",
&ie[1], &args->itimers[1]);
if (!ret)
ret = itimer_restore_and_fix("prof",
&ie[2], &args->itimers[2]);
}
close(fd);
return ret;
}
static void sigreturn_restore(pid_t pstree_pid, pid_t pid) static void sigreturn_restore(pid_t pstree_pid, pid_t pid)
{ {
long restore_code_len, restore_task_vma_len; long restore_code_len, restore_task_vma_len;
...@@ -1728,6 +1799,10 @@ static void sigreturn_restore(pid_t pstree_pid, pid_t pid) ...@@ -1728,6 +1799,10 @@ static void sigreturn_restore(pid_t pstree_pid, pid_t pid)
task_args->logfd = get_logfd(); task_args->logfd = get_logfd();
task_args->sigchld_act = sigchld_act; task_args->sigchld_act = sigchld_act;
ret = prepare_itimers(pid, task_args);
if (ret < 0)
goto err;
cr_mutex_init(&task_args->rst_lock); cr_mutex_init(&task_args->rst_lock);
if (pstree_entry.nr_threads) { if (pstree_entry.nr_threads) {
......
...@@ -227,6 +227,28 @@ out: ...@@ -227,6 +227,28 @@ out:
pr_img_tail(CR_FD_SIGACT); pr_img_tail(CR_FD_SIGACT);
} }
static void show_itimer(char *n, struct itimer_entry *ie)
{
pr_info("%s: int %lu.%lu val %lu.%lu\n", n,
(unsigned long)ie->isec, (unsigned long)ie->iusec,
(unsigned long)ie->vsec, (unsigned long)ie->vusec);
}
static void show_itimers(int fd)
{
struct itimer_entry ie[3];
pr_img_head(CR_FD_ITIMERS);
if (read_img_buf(fd, ie, sizeof(ie)) < 0)
goto out;
show_itimer("real", &ie[0]);
show_itimer("real", &ie[1]);
show_itimer("real", &ie[2]);
out:
pr_img_tail(CR_FD_ITIMERS);
}
static int show_pstree(int fd_pstree, struct list_head *collect) static int show_pstree(int fd_pstree, struct list_head *collect)
{ {
struct pstree_entry e; struct pstree_entry e;
...@@ -449,6 +471,9 @@ static int cr_parse_file(struct cr_options *opts) ...@@ -449,6 +471,9 @@ static int cr_parse_file(struct cr_options *opts)
case INETSK_MAGIC: case INETSK_MAGIC:
show_inetsk(fd); show_inetsk(fd);
break; break;
case ITIMERS_MAGIC:
show_itimers(fd);
break;
default: default:
pr_err("Unknown magic %x on %s\n", magic, opts->show_dump_file); pr_err("Unknown magic %x on %s\n", magic, opts->show_dump_file);
goto err; goto err;
...@@ -522,6 +547,8 @@ static int cr_show_all(unsigned long pid, struct cr_options *opts) ...@@ -522,6 +547,8 @@ static int cr_show_all(unsigned long pid, struct cr_options *opts)
show_inetsk(cr_fdset->fds[CR_FD_INETSK]); show_inetsk(cr_fdset->fds[CR_FD_INETSK]);
show_itimers(cr_fdset->fds[CR_FD_ITIMERS]);
close_cr_fdset(&cr_fdset); close_cr_fdset(&cr_fdset);
if (opts->leader_only) if (opts->leader_only)
......
...@@ -92,6 +92,11 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = { ...@@ -92,6 +92,11 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = {
.magic = INETSK_MAGIC, .magic = INETSK_MAGIC,
}, },
/* interval timers (itimers) */
[CR_FD_ITIMERS] = {
.fmt = FMT_FNAME_ITIMERS,
.magic = ITIMERS_MAGIC,
},
}; };
static struct cr_fdset *alloc_cr_fdset(void) static struct cr_fdset *alloc_cr_fdset(void)
......
...@@ -25,6 +25,7 @@ enum { ...@@ -25,6 +25,7 @@ enum {
CR_FD_SIGACT, CR_FD_SIGACT,
CR_FD_UNIXSK, CR_FD_UNIXSK,
CR_FD_INETSK, CR_FD_INETSK,
CR_FD_ITIMERS,
CR_FD_MAX CR_FD_MAX
}; };
...@@ -63,6 +64,7 @@ extern struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX]; ...@@ -63,6 +64,7 @@ extern struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX];
#define FMT_FNAME_SIGACTS "sigacts-%d.img" #define FMT_FNAME_SIGACTS "sigacts-%d.img"
#define FMT_FNAME_UNIXSK "unixsk-%d.img" #define FMT_FNAME_UNIXSK "unixsk-%d.img"
#define FMT_FNAME_INETSK "inetsk-%d.img" #define FMT_FNAME_INETSK "inetsk-%d.img"
#define FMT_FNAME_ITIMERS "itimers-%d.img"
extern int get_image_path(char *path, int size, const char *fmt, int pid); extern int get_image_path(char *path, int size, const char *fmt, int pid);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define SIGACT_MAGIC 0x60606060 #define SIGACT_MAGIC 0x60606060
#define UNIXSK_MAGIC 0x07070707 #define UNIXSK_MAGIC 0x07070707
#define INETSK_MAGIC 0x08080808 #define INETSK_MAGIC 0x08080808
#define ITIMERS_MAGIC 0x99009900
#define FDINFO_FD 1 #define FDINFO_FD 1
#define FDINFO_MAP 2 #define FDINFO_MAP 2
...@@ -125,6 +126,13 @@ struct sa_entry { ...@@ -125,6 +126,13 @@ struct sa_entry {
u64 mask; u64 mask;
} __packed; } __packed;
struct itimer_entry {
u64 isec;
u64 iusec;
u64 vsec;
u64 vusec;
} __packed;
#define HEADER_VERSION 1 #define HEADER_VERSION 1
#define HEADER_ARCH_X86_64 1 #define HEADER_ARCH_X86_64 1
......
...@@ -39,6 +39,7 @@ extern int syscall_seized(pid_t pid, ...@@ -39,6 +39,7 @@ extern int syscall_seized(pid_t pid,
extern int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list, extern int parasite_dump_pages_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list,
struct cr_fdset *cr_fdset); struct cr_fdset *cr_fdset);
extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset); extern int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset);
extern int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset);
extern struct parasite_ctl *parasite_infect_seized(pid_t pid, struct list_head *vma_area_list); extern struct parasite_ctl *parasite_infect_seized(pid_t pid, struct list_head *vma_area_list);
extern int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list); extern int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#define PARASITE_ERR_WRITE -1030 #define PARASITE_ERR_WRITE -1030
#define PARASITE_ERR_MPROTECT -1031 #define PARASITE_ERR_MPROTECT -1031
#define PARASITE_ERR_SIGACTION -1032 #define PARASITE_ERR_SIGACTION -1032
#define PARASITE_ERR_GETITIMER -1033
enum { enum {
PARASITE_CMD_NONE, PARASITE_CMD_NONE,
...@@ -33,6 +34,7 @@ enum { ...@@ -33,6 +34,7 @@ enum {
PARASITE_CMD_PINGME, PARASITE_CMD_PINGME,
PARASITE_CMD_DUMPPAGES, PARASITE_CMD_DUMPPAGES,
PARASITE_CMD_DUMP_SIGACTS, PARASITE_CMD_DUMP_SIGACTS,
PARASITE_CMD_DUMP_ITIMERS,
PARASITE_CMD_MAX, PARASITE_CMD_MAX,
}; };
......
...@@ -77,6 +77,8 @@ struct task_restore_core_args { ...@@ -77,6 +77,8 @@ struct task_restore_core_args {
struct shmems *shmems; struct shmems *shmems;
struct task_entries *task_entries; struct task_entries *task_entries;
rt_sigaction_t sigchld_act; rt_sigaction_t sigchld_act;
struct itimerval itimers[3];
} __aligned(sizeof(long)); } __aligned(sizeof(long));
struct pt_regs { struct pt_regs {
......
...@@ -358,31 +358,41 @@ static int parasite_prep_file(int type, struct parasite_dump_file_args *fa, ...@@ -358,31 +358,41 @@ static int parasite_prep_file(int type, struct parasite_dump_file_args *fa,
return 0; return 0;
} }
int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset) static int parasite_file_cmd(int cmd, int type,
struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
{ {
struct parasite_dump_file_args parasite_sigacts = { }; struct parasite_dump_file_args args = { };
int status, ret = -1; int status, ret = -1;
pr_info("\n"); pr_info("\n");
pr_info("Dumping sigactions (pid: %d)\n", ctl->pid); pr_info("Dumping sigactions (pid: %d)\n", ctl->pid);
pr_info("----------------------------------------\n"); pr_info("----------------------------------------\n");
ret = parasite_prep_file(CR_FD_SIGACT, &parasite_sigacts, ctl, cr_fdset); ret = parasite_prep_file(type, &args, ctl, cr_fdset);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = parasite_execute(PARASITE_CMD_DUMP_SIGACTS, ctl, ret = parasite_execute(cmd, ctl,
(parasite_status_t *) &parasite_sigacts, (parasite_status_t *)&args, sizeof(args));
sizeof(parasite_sigacts));
err: err:
fchmod(cr_fdset->fds[CR_FD_SIGACT], CR_FD_PERM); fchmod(cr_fdset->fds[type], CR_FD_PERM);
out: out:
pr_info("----------------------------------------\n"); pr_info("----------------------------------------\n");
return ret; return ret;
} }
int parasite_dump_sigacts_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
{
return parasite_file_cmd(PARASITE_CMD_DUMP_SIGACTS, CR_FD_SIGACT, ctl, cr_fdset);
}
int parasite_dump_itimers_seized(struct parasite_ctl *ctl, struct cr_fdset *cr_fdset)
{
return parasite_file_cmd(PARASITE_CMD_DUMP_ITIMERS, CR_FD_ITIMERS, ctl, cr_fdset);
}
/* /*
* This routine drives parasite code (been previously injected into a victim * This routine drives parasite code (been previously injected into a victim
* process) and tells it to dump pages into the file. * process) and tells it to dump pages into the file.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
...@@ -284,6 +285,69 @@ err_close: ...@@ -284,6 +285,69 @@ err_close:
return ret; return ret;
} }
static int dump_itimer(int which, int fd, parasite_status_t *st)
{
struct itimerval val;
int ret;
struct itimer_entry ie;
ret = sys_getitimer(which, &val);
if (ret < 0) {
sys_write_msg("getitimer failed\n");
SET_PARASITE_STATUS(st, PARASITE_ERR_GETITIMER, ret);
return st->ret;
}
ie.isec = val.it_interval.tv_sec;
ie.iusec = val.it_interval.tv_usec;
ie.vsec = val.it_value.tv_sec;
ie.vusec = val.it_value.tv_sec;
ret = sys_write(fd, &ie, sizeof(ie));
if (ret != sizeof(ie)) {
sys_write_msg("sys_write failed\n");
SET_PARASITE_STATUS(st, PARASITE_ERR_WRITE, ret);
return st->ret;
}
return 0;
}
static int dump_itimers(struct parasite_dump_file_args *args)
{
parasite_status_t *st = &args->status;
rt_sigaction_t act;
struct sa_entry e;
int fd, sig;
int ret = PARASITE_ERR_FAIL;
fd = parasite_open_file(args);
if (fd < 0)
return fd;
sys_lseek(fd, MAGIC_OFFSET, SEEK_SET);
ret = dump_itimer(ITIMER_REAL, fd, st);
if (ret < 0)
goto err_close;
ret = dump_itimer(ITIMER_VIRTUAL, fd, st);
if (ret < 0)
goto err_close;
ret = dump_itimer(ITIMER_PROF, fd, st);
if (ret < 0)
goto err_close;
ret = 0;
SET_PARASITE_STATUS(st, 0, ret);
err_close:
sys_close(fd);
return ret;
}
static int __used parasite_service(unsigned long cmd, void *args, void *brk) static int __used parasite_service(unsigned long cmd, void *args, void *brk)
{ {
brk_init(brk); brk_init(brk);
...@@ -299,10 +363,10 @@ static int __used parasite_service(unsigned long cmd, void *args, void *brk) ...@@ -299,10 +363,10 @@ static int __used parasite_service(unsigned long cmd, void *args, void *brk)
break; break;
case PARASITE_CMD_DUMPPAGES: case PARASITE_CMD_DUMPPAGES:
return dump_pages((struct parasite_dump_pages_args *)args); return dump_pages((struct parasite_dump_pages_args *)args);
break;
case PARASITE_CMD_DUMP_SIGACTS: case PARASITE_CMD_DUMP_SIGACTS:
return dump_sigact((struct parasite_dump_file_args *)args); return dump_sigact((struct parasite_dump_file_args *)args);
break; case PARASITE_CMD_DUMP_ITIMERS:
return dump_itimers((struct parasite_dump_file_args *)args);
default: default:
sys_write_msg("Unknown command to parasite\n"); sys_write_msg("Unknown command to parasite\n");
break; break;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/time.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sched.h> #include <sched.h>
...@@ -551,6 +552,22 @@ long restore_task(struct task_restore_core_args *args) ...@@ -551,6 +552,22 @@ long restore_task(struct task_restore_core_args *args)
cr_wait_dec(&args->task_entries->nr_in_progress); cr_wait_dec(&args->task_entries->nr_in_progress);
cr_wait_while(&args->task_entries->start, CR_STATE_RESTORE_SIGCHLD); cr_wait_while(&args->task_entries->start, CR_STATE_RESTORE_SIGCHLD);
/*
* The code that prepared the itimers makes shure the
* code below doesn't fail due to bad timing values.
*/
#define itimer_armed(args, i) \
(args->itimers[i].it_interval.tv_sec || \
args->itimers[i].it_interval.tv_usec)
if (itimer_armed(args, 0))
sys_setitimer(ITIMER_REAL, &args->itimers[0], NULL);
if (itimer_armed(args, 1))
sys_setitimer(ITIMER_VIRTUAL, &args->itimers[1], NULL);
if (itimer_armed(args, 2))
sys_setitimer(ITIMER_PROF, &args->itimers[2], NULL);
ret = sys_munmap(args->task_entries, TASK_ENTRIES_SIZE); ret = sys_munmap(args->task_entries, TASK_ENTRIES_SIZE);
if (ret < 0) { if (ret < 0) {
write_num_n(__LINE__); write_num_n(__LINE__);
......
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