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)
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);
if (ret) {
pr_err("Can't cure (pid: %d) from parasite\n", pid);
......
......@@ -1510,6 +1510,77 @@ err_or_found:
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)
{
long restore_code_len, restore_task_vma_len;
......@@ -1728,6 +1799,10 @@ static void sigreturn_restore(pid_t pstree_pid, pid_t pid)
task_args->logfd = get_logfd();
task_args->sigchld_act = sigchld_act;
ret = prepare_itimers(pid, task_args);
if (ret < 0)
goto err;
cr_mutex_init(&task_args->rst_lock);
if (pstree_entry.nr_threads) {
......
......@@ -227,6 +227,28 @@ out:
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)
{
struct pstree_entry e;
......@@ -449,6 +471,9 @@ static int cr_parse_file(struct cr_options *opts)
case INETSK_MAGIC:
show_inetsk(fd);
break;
case ITIMERS_MAGIC:
show_itimers(fd);
break;
default:
pr_err("Unknown magic %x on %s\n", magic, opts->show_dump_file);
goto err;
......@@ -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_itimers(cr_fdset->fds[CR_FD_ITIMERS]);
close_cr_fdset(&cr_fdset);
if (opts->leader_only)
......
......@@ -92,6 +92,11 @@ struct cr_fd_desc_tmpl fdset_template[CR_FD_MAX] = {
.magic = INETSK_MAGIC,
},
/* interval timers (itimers) */
[CR_FD_ITIMERS] = {
.fmt = FMT_FNAME_ITIMERS,
.magic = ITIMERS_MAGIC,
},
};
static struct cr_fdset *alloc_cr_fdset(void)
......
......@@ -25,6 +25,7 @@ enum {
CR_FD_SIGACT,
CR_FD_UNIXSK,
CR_FD_INETSK,
CR_FD_ITIMERS,
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_UNIXSK "unixsk-%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);
......
......@@ -14,6 +14,7 @@
#define SIGACT_MAGIC 0x60606060
#define UNIXSK_MAGIC 0x07070707
#define INETSK_MAGIC 0x08080808
#define ITIMERS_MAGIC 0x99009900
#define FDINFO_FD 1
#define FDINFO_MAP 2
......@@ -125,6 +126,13 @@ struct sa_entry {
u64 mask;
} __packed;
struct itimer_entry {
u64 isec;
u64 iusec;
u64 vsec;
u64 vusec;
} __packed;
#define HEADER_VERSION 1
#define HEADER_ARCH_X86_64 1
......
......@@ -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,
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 int parasite_cure_seized(struct parasite_ctl *ctl, struct list_head *vma_area_list);
......
......@@ -26,6 +26,7 @@
#define PARASITE_ERR_WRITE -1030
#define PARASITE_ERR_MPROTECT -1031
#define PARASITE_ERR_SIGACTION -1032
#define PARASITE_ERR_GETITIMER -1033
enum {
PARASITE_CMD_NONE,
......@@ -33,6 +34,7 @@ enum {
PARASITE_CMD_PINGME,
PARASITE_CMD_DUMPPAGES,
PARASITE_CMD_DUMP_SIGACTS,
PARASITE_CMD_DUMP_ITIMERS,
PARASITE_CMD_MAX,
};
......
......@@ -77,6 +77,8 @@ struct task_restore_core_args {
struct shmems *shmems;
struct task_entries *task_entries;
rt_sigaction_t sigchld_act;
struct itimerval itimers[3];
} __aligned(sizeof(long));
struct pt_regs {
......
......@@ -358,31 +358,41 @@ static int parasite_prep_file(int type, struct parasite_dump_file_args *fa,
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;
pr_info("\n");
pr_info("Dumping sigactions (pid: %d)\n", ctl->pid);
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)
goto out;
ret = parasite_execute(PARASITE_CMD_DUMP_SIGACTS, ctl,
(parasite_status_t *) &parasite_sigacts,
sizeof(parasite_sigacts));
ret = parasite_execute(cmd, ctl,
(parasite_status_t *)&args, sizeof(args));
err:
fchmod(cr_fdset->fds[CR_FD_SIGACT], CR_FD_PERM);
fchmod(cr_fdset->fds[type], CR_FD_PERM);
out:
pr_info("----------------------------------------\n");
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
* process) and tells it to dump pages into the file.
......
......@@ -4,6 +4,7 @@
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
......@@ -284,6 +285,69 @@ err_close:
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)
{
brk_init(brk);
......@@ -299,10 +363,10 @@ static int __used parasite_service(unsigned long cmd, void *args, void *brk)
break;
case PARASITE_CMD_DUMPPAGES:
return dump_pages((struct parasite_dump_pages_args *)args);
break;
case PARASITE_CMD_DUMP_SIGACTS:
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:
sys_write_msg("Unknown command to parasite\n");
break;
......
......@@ -6,6 +6,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sched.h>
......@@ -551,6 +552,22 @@ long restore_task(struct task_restore_core_args *args)
cr_wait_dec(&args->task_entries->nr_in_progress);
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);
if (ret < 0) {
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