[Devel] [PATCH 07/12] Move checkpoint/restart of signals into kernel/signal.c
Matt Helsley
matthltc at us.ibm.com
Fri Feb 26 00:45:08 PST 2010
Signed-off-by: Matt Helsley <matthltc at us.ibm.com>
---
checkpoint/Makefile | 1 -
checkpoint/signal.c | 732 ------------------------------------------------
include/linux/signal.h | 17 +-
kernel/signal.c | 723 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 738 insertions(+), 735 deletions(-)
delete mode 100644 checkpoint/signal.c
diff --git a/checkpoint/Makefile b/checkpoint/Makefile
index 02e66b6..5bc8468 100644
--- a/checkpoint/Makefile
+++ b/checkpoint/Makefile
@@ -10,4 +10,3 @@ obj-$(CONFIG_CHECKPOINT) += \
process.o \
namespace.o \
memory.o \
- signal.o
diff --git a/checkpoint/signal.c b/checkpoint/signal.c
deleted file mode 100644
index 9d0e9c3..0000000
--- a/checkpoint/signal.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * Checkpoint task signals
- *
- * Copyright (C) 2009 Oren Laadan
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of the Linux
- * distribution for more details.
- */
-
-/* default debug level for output */
-#define CKPT_DFLAG CKPT_DSYS
-
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/resource.h>
-#include <linux/timer.h>
-#include <linux/posix-timers.h>
-#include <linux/checkpoint.h>
-#include <linux/checkpoint_hdr.h>
-
-static inline void fill_sigset(struct ckpt_sigset *h, sigset_t *sigset)
-{
- memcpy(&h->sigset, sigset, sizeof(*sigset));
-}
-
-static inline void load_sigset(sigset_t *sigset, struct ckpt_sigset *h)
-{
- memcpy(sigset, &h->sigset, sizeof(*sigset));
-}
-
-/***********************************************************************
- * sighand checkpoint/collect/restart
- */
-
-static int do_checkpoint_sighand(struct ckpt_ctx *ctx,
- struct sighand_struct *sighand)
-{
- struct ckpt_hdr_sighand *h;
- struct ckpt_sigaction *hh;
- struct sigaction *sa;
- int i, ret;
-
- h = ckpt_hdr_get_type(ctx, _NSIG * sizeof(*hh) + sizeof(*h),
- CKPT_HDR_SIGHAND);
- if (!h)
- return -ENOMEM;
-
- hh = h->action;
- spin_lock_irq(&sighand->siglock);
- for (i = 0; i < _NSIG; i++) {
- sa = &sighand->action[i].sa;
- hh[i]._sa_handler = (unsigned long) sa->sa_handler;
- hh[i].sa_flags = sa->sa_flags;
- hh[i].sa_restorer = (unsigned long) sa->sa_restorer;
- fill_sigset(&hh[i].sa_mask, &sa->sa_mask);
- }
- spin_unlock_irq(&sighand->siglock);
-
- ret = ckpt_write_obj(ctx, &h->h);
- ckpt_hdr_put(ctx, h);
-
- return ret;
-}
-
-int checkpoint_sighand(struct ckpt_ctx *ctx, void *ptr)
-{
- return do_checkpoint_sighand(ctx, (struct sighand_struct *) ptr);
-}
-
-int checkpoint_obj_sighand(struct ckpt_ctx *ctx, struct task_struct *t)
-{
- struct sighand_struct *sighand;
- int objref;
-
- read_lock(&tasklist_lock);
- sighand = rcu_dereference(t->sighand);
- atomic_inc(&sighand->count);
- read_unlock(&tasklist_lock);
-
- objref = checkpoint_obj(ctx, sighand, CKPT_OBJ_SIGHAND);
- __cleanup_sighand(sighand);
-
- return objref;
-}
-
-int ckpt_collect_sighand(struct ckpt_ctx *ctx, struct task_struct *t)
-{
- struct sighand_struct *sighand;
- int ret;
-
- read_lock(&tasklist_lock);
- sighand = rcu_dereference(t->sighand);
- atomic_inc(&sighand->count);
- read_unlock(&tasklist_lock);
-
- ret = ckpt_obj_collect(ctx, sighand, CKPT_OBJ_SIGHAND);
- __cleanup_sighand(sighand);
-
- return ret;
-}
-
-static struct sighand_struct *do_restore_sighand(struct ckpt_ctx *ctx)
-{
- struct ckpt_hdr_sighand *h;
- struct ckpt_sigaction *hh;
- struct sighand_struct *sighand;
- struct sigaction *sa;
- int i;
-
- h = ckpt_read_obj_type(ctx, _NSIG * sizeof(*hh) + sizeof(*h),
- CKPT_HDR_SIGHAND);
- if (IS_ERR(h))
- return ERR_PTR(PTR_ERR(h));
-
- sighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
- if (!sighand) {
- sighand = ERR_PTR(-ENOMEM);
- goto out;
- }
- atomic_set(&sighand->count, 1);
-
- hh = h->action;
- for (i = 0; i < _NSIG; i++) {
- sa = &sighand->action[i].sa;
- sa->sa_handler = (void *) (unsigned long) hh[i]._sa_handler;
- sa->sa_flags = hh[i].sa_flags;
- sa->sa_restorer = (void *) (unsigned long) hh[i].sa_restorer;
- load_sigset(&sa->sa_mask, &hh[i].sa_mask);
- }
- out:
- ckpt_hdr_put(ctx, h);
- return sighand;
-}
-
-void *restore_sighand(struct ckpt_ctx *ctx)
-{
- return (void *) do_restore_sighand(ctx);
-}
-
-int restore_obj_sighand(struct ckpt_ctx *ctx, int sighand_objref)
-{
- struct sighand_struct *sighand;
- struct sighand_struct *old_sighand;
-
- sighand = ckpt_obj_fetch(ctx, sighand_objref, CKPT_OBJ_SIGHAND);
- if (IS_ERR(sighand))
- return PTR_ERR(sighand);
-
- if (sighand == current->sighand)
- return 0;
-
- atomic_inc(&sighand->count);
-
- /* manipulate tsk->sighand with tasklist lock write-held */
- write_lock_irq(&tasklist_lock);
- old_sighand = rcu_dereference(current->sighand);
- spin_lock(&old_sighand->siglock);
- rcu_assign_pointer(current->sighand, sighand);
- spin_unlock(&old_sighand->siglock);
- write_unlock_irq(&tasklist_lock);
- __cleanup_sighand(old_sighand);
-
- return 0;
-}
-
-/***********************************************************************
- * signal checkpoint/restart
- */
-
-static void fill_siginfo(struct ckpt_siginfo *si, siginfo_t *info)
-{
- si->signo = info->si_signo;
- si->_errno = info->si_errno;
- si->code = info->si_code;
-
- /* TODO: convert info->si_uid to uid_objref */
-
- switch (info->si_code & __SI_MASK) {
- case __SI_TIMER:
- si->pid = info->si_tid;
- si->uid = info->si_overrun;
- si->sigval_int = info->si_int;
- si->utime = info->si_sys_private;
- break;
- case __SI_POLL:
- si->pid = info->si_band;
- si->sigval_int = info->si_fd;
- break;
- case __SI_FAULT:
- si->sigval_ptr = (unsigned long) info->si_addr;
-#ifdef __ARCH_SI_TRAPNO
- si->sigval_int = info->si_trapno;
-#endif
- break;
- case __SI_CHLD:
- si->pid = info->si_pid;
- si->uid = info->si_uid;
- si->sigval_int = info->si_status;
- si->stime = info->si_stime;
- si->utime = info->si_utime;
- break;
- case __SI_KILL:
- case __SI_RT:
- case __SI_MESGQ:
- si->pid = info->si_pid;
- si->uid = info->si_uid;
- si->sigval_ptr = (unsigned long) info->si_ptr;
- break;
- default:
- BUG();
- }
-}
-
-static int load_siginfo(siginfo_t *info, struct ckpt_siginfo *si)
-{
- if (!valid_signal(si->signo))
- return -EINVAL;
- if (!ckpt_validate_errno(si->_errno))
- return -EINVAL;
-
- info->si_signo = si->signo;
- info->si_errno = si->_errno;
- info->si_code = si->code;
-
- /* TODO: validate remaining signal fields */
-
- switch (info->si_code & __SI_MASK) {
- case __SI_TIMER:
- info->si_tid = si->pid;
- info->si_overrun = si->uid;
- info->si_int = si->sigval_int;
- info->si_sys_private = si->utime;
- break;
- case __SI_POLL:
- info->si_band = si->pid;
- info->si_fd = si->sigval_int;
- break;
- case __SI_FAULT:
- info->si_addr = (void __user *) (unsigned long) si->sigval_ptr;
-#ifdef __ARCH_SI_TRAPNO
- info->si_trapno = si->sigval_int;
-#endif
- break;
- case __SI_CHLD:
- info->si_pid = si->pid;
- info->si_uid = si->uid;
- info->si_status = si->sigval_int;
- info->si_stime = si->stime;
- info->si_utime = si->utime;
- break;
- case __SI_KILL:
- case __SI_RT:
- case __SI_MESGQ:
- info->si_pid = si->pid;
- info->si_uid = si->uid;
- info->si_ptr = (void __user *) (unsigned long) si->sigval_ptr;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * To checkpoint pending signals (private/shared) the caller moves the
- * signal queue (and copies the mask) to a separate struct sigpending,
- * therefore we can iterate through it without locking.
- * After we return, the caller re-attaches (prepends) the original
- * signal queue to the original struct sigpending. Thus, signals that
- * arrive(d) in the meantime will be suitably queued after these.
- * Finally, repeated non-realtime signals will not be queued because
- * they will already be marked in the pending mask, that remains as is.
- * This is the expected behavior of non-realtime signals.
- */
-static int checkpoint_sigpending(struct ckpt_ctx *ctx,
- struct sigpending *pending)
-{
- struct ckpt_hdr_sigpending *h;
- struct ckpt_siginfo *si;
- struct sigqueue *q;
- int nr_pending = 0;
- int ret;
-
- list_for_each_entry(q, &pending->list, list) {
- /* TODO: remove after adding support for posix-timers */
- if ((q->info.si_code & __SI_MASK) == __SI_TIMER) {
- ckpt_err(ctx, -ENOTSUPP, "%(T)signal SI_TIMER\n");
- return -ENOTSUPP;
- }
- nr_pending++;
- }
-
- h = ckpt_hdr_get_type(ctx, nr_pending * sizeof(*si) + sizeof(*h),
- CKPT_HDR_SIGPENDING);
- if (!h)
- return -ENOMEM;
-
- h->nr_pending = nr_pending;
- fill_sigset(&h->signal, &pending->signal);
-
- si = h->siginfo;
- list_for_each_entry(q, &pending->list, list)
- fill_siginfo(si++, &q->info);
-
- ret = ckpt_write_obj(ctx, &h->h);
- ckpt_hdr_put(ctx, h);
-
- return ret;
-}
-
-static int checkpoint_signal(struct ckpt_ctx *ctx, struct task_struct *t)
-{
- struct ckpt_hdr_signal *h;
- struct signal_struct *signal;
- struct sigpending shared_pending;
- struct tty_struct *tty = NULL;
- struct rlimit *rlim;
- struct timeval tval;
- struct cpu_itimer *it;
- cputime_t cputime;
- unsigned long flags;
- int i, ret = 0;
-
- h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL);
- if (!h)
- return -ENOMEM;
-
- signal = t->signal;
- rlim = signal->rlim;
-
- INIT_LIST_HEAD(&shared_pending.list);
-
- /* temporarily borrow signal queue - see chekcpoint_sigpending() */
- if (!lock_task_sighand(t, &flags)) {
- ckpt_err(ctx, -EBUSY, "%(T)c/r: [pid %d] without sighand\n",
- task_pid_vnr(t));
- ret = -EBUSY;
- goto out;
- }
-
- /* TODO: remove after adding support for posix-timers */
- if (!list_empty(&signal->posix_timers)) {
- unlock_task_sighand(t, &flags);
- ckpt_err(ctx, -ENOTSUPP, "%(T)%(P)posix-timers\n", signal);
- ret = -ENOTSUPP;
- goto out;
- }
-
- list_splice_init(&signal->shared_pending.list, &shared_pending.list);
- shared_pending.signal = signal->shared_pending.signal;
-
- /* rlimit */
- for (i = 0; i < RLIM_NLIMITS; i++) {
- h->rlim[i].rlim_cur = rlim[i].rlim_cur;
- h->rlim[i].rlim_max = rlim[i].rlim_max;
- }
-
- /* real/virt/prof itimers */
- if (hrtimer_active(&signal->real_timer)) {
- /* For an active timer compute the time delta */
- ktime_t delta = hrtimer_get_remaining(&signal->real_timer);
- /*
- * If the timer expired after the the test above, then
- * set the expire to the minimum possible (because by
- * now the pending signal have been saved already, but
- * the signal from this very expiry won't be sent before
- * we release t->sighand->siglock).
- */
- ckpt_debug("active ! %lld\n", delta.tv64);
- if (delta.tv64 <= 0)
- delta.tv64 = NSEC_PER_USEC;
- h->it_real_value = ktime_to_ns(delta);
- } else {
- /*
- * Timer is inactive; if @it_real_incr is 0 the timer
- * will not be re-armed. Beacuse we hold siglock, if
- * @it_real_incr > 0, the timer must have just expired
- * but not yet re-armed, and we have a SIGALRM pending
- * - that will trigger timer re-arm after restart.
- */
- h->it_real_value = 0;
- }
- h->it_real_incr = ktime_to_ns(signal->it_real_incr);
-
- /* for prof/virt, ignore error and incr_error */
- it = &signal->it[CPUCLOCK_VIRT];
- cputime = it->expires;
- if (!cputime_eq(cputime, cputime_zero))
- cputime = cputime_sub(it->expires, virt_ticks(t));
- cputime_to_timeval(cputime, &tval);
- h->it_virt_value = timeval_to_ns(&tval);
- cputime_to_timeval(it->incr, &tval);
- h->it_virt_incr = timeval_to_ns(&tval);
-
- it = &signal->it[CPUCLOCK_PROF];
- cputime = it->expires;
- if (!cputime_eq(cputime, cputime_zero))
- cputime = cputime_sub(it->expires, prof_ticks(t));
- cputime_to_timeval(cputime, &tval);
- h->it_prof_value = timeval_to_ns(&tval);
- cputime_to_timeval(it->incr, &tval);
- h->it_prof_incr = timeval_to_ns(&tval);
-
- /* tty */
- if (signal->leader) {
- h->tty_old_pgrp = ckpt_pid_nr(ctx, signal->tty_old_pgrp);
- tty = tty_kref_get(signal->tty);
- if (tty) {
- /* irq is already disabled */
- spin_lock(&tty->ctrl_lock);
- h->tty_pgrp = ckpt_pid_nr(ctx, tty->pgrp);
- spin_unlock(&tty->ctrl_lock);
- tty_kref_put(tty);
- }
- }
-
- unlock_task_sighand(t, &flags);
-
- /*
- * If the session is in an ancestor namespace, skip this tty
- * and set tty_objref = 0. It will not be explicitly restored,
- * but rather inherited from parent pid-ns at restart time.
- */
- if (tty && ckpt_pid_nr(ctx, tty->session) > 0) {
- h->tty_objref = checkpoint_obj(ctx, tty, CKPT_OBJ_TTY);
- if (h->tty_objref < 0)
- ret = h->tty_objref;
- }
-
- if (!ret)
- ret = ckpt_write_obj(ctx, &h->h);
- if (!ret)
- ret = checkpoint_sigpending(ctx, &shared_pending);
-
- /* return the borrowed queue */
- if (!lock_task_sighand(t, &flags)) {
- pr_warning("c/r: [%d] sighand disappeared\n", task_pid_vnr(t));
- goto out;
- }
- list_splice(&shared_pending.list, &signal->shared_pending.list);
- unlock_task_sighand(t, &flags);
- out:
- ckpt_hdr_put(ctx, h);
- return ret;
-}
-
-int checkpoint_obj_signal(struct ckpt_ctx *ctx, struct task_struct *t)
-{
- BUG_ON(t->flags & PF_EXITING);
- return checkpoint_signal(ctx, t);
-}
-
-static int restore_sigpending(struct ckpt_ctx *ctx, struct sigpending *pending)
-{
- struct ckpt_hdr_sigpending *h;
- struct ckpt_siginfo *si;
- struct sigqueue *q;
- int ret = 0;
-
- h = ckpt_read_buf_type(ctx, 0, CKPT_HDR_SIGPENDING);
- if (IS_ERR(h))
- return PTR_ERR(h);
-
- if (h->h.len != h->nr_pending * sizeof(*si) + sizeof(*h)) {
- ret = -EINVAL;
- goto out;
- }
-
- INIT_LIST_HEAD(&pending->list);
- load_sigset(&pending->signal, &h->signal);
-
- si = h->siginfo;
- while (h->nr_pending--) {
- q = sigqueue_alloc();
- if (!q) {
- ret = -ENOMEM;
- break;
- }
-
- ret = load_siginfo(&q->info, si++);
- if (ret < 0) {
- sigqueue_free(q);
- break;
- }
-
- q->flags &= ~SIGQUEUE_PREALLOC;
- list_add_tail(&pending->list, &q->list);
- }
-
- if (ret < 0)
- flush_sigqueue(pending);
- out:
- ckpt_hdr_put(ctx, h);
- return ret;
-}
-
-static int restore_signal(struct ckpt_ctx *ctx)
-{
- struct ckpt_hdr_signal *h;
- struct sigpending new_pending;
- struct sigpending *pending;
- struct tty_struct *tty = NULL;
- struct itimerval itimer;
- struct rlimit rlim;
- struct pid *pgrp = NULL;
- int i, ret;
-
- h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL);
- if (IS_ERR(h))
- return PTR_ERR(h);
-
- /* rlimit */
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim.rlim_cur = h->rlim[i].rlim_cur;
- rlim.rlim_max = h->rlim[i].rlim_max;
- ret = do_setrlimit(i, &rlim);
- if (ret < 0)
- goto out;
- }
-
- ret = restore_sigpending(ctx, &new_pending);
- if (ret < 0)
- goto out;
-
- /* tty - session */
- if (h->tty_objref) {
- tty = ckpt_obj_fetch(ctx, h->tty_objref, CKPT_OBJ_TTY);
- if (IS_ERR(tty)) {
- ret = PTR_ERR(tty);
- goto out;
- }
- /* this will fail unless we're the session leader */
- ret = tiocsctty(tty, 0);
- if (ret < 0)
- goto out;
- /* now restore the foreground group (job control) */
- if (h->tty_pgrp) {
- /*
- * If tty_pgrp == CKPT_PID_NULL, below will
- * fail, so no need for explicit test
- */
- ret = do_tiocspgrp(tty, tty_pair_get_tty(tty),
- h->tty_pgrp);
- if (ret < 0)
- goto out;
- }
- } else {
- /*
- * If tty_objref isn't set, we _keep_ whatever tty we
- * already have as a ctty. Why does this make sense ?
- * - If our session is "within" the restart context,
- * then that session has no controlling terminal.
- * - If out session is "outside" the restart context,
- * then we're like to keep whatever we inherit from
- * the parent pid-ns.
- */
- }
-
- /*
- * Reset real/virt/prof itimer (in case they were set), to
- * prevent unwanted signals after flushing current signals
- * and before restoring original real/virt/prof itimer.
- */
- itimer.it_value = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
- itimer.it_interval = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
- do_setitimer(ITIMER_REAL, &itimer, NULL);
- do_setitimer(ITIMER_VIRTUAL, &itimer, NULL);
- do_setitimer(ITIMER_PROF, &itimer, NULL);
-
- /* tty - tty_old_pgrp */
- if (current->signal->leader && h->tty_old_pgrp != CKPT_PID_NULL) {
- rcu_read_lock();
- pgrp = get_pid(_ckpt_find_pgrp(ctx, h->tty_old_pgrp));
- rcu_read_unlock();
- if (!pgrp)
- goto out;
- }
-
- spin_lock_irq(¤t->sighand->siglock);
- /* tty - tty_old_pgrp */
- put_pid(current->signal->tty_old_pgrp);
- current->signal->tty_old_pgrp = pgrp;
- /* pending signals */
- pending = ¤t->signal->shared_pending;
- flush_sigqueue(pending);
- pending->signal = new_pending.signal;
- list_splice_init(&new_pending.list, &pending->list);
- spin_unlock_irq(¤t->sighand->siglock);
-
- /* real/virt/prof itimers */
- itimer.it_value = ns_to_timeval(h->it_real_value);
- itimer.it_interval = ns_to_timeval(h->it_real_incr);
- ret = do_setitimer(ITIMER_REAL, &itimer, NULL);
- if (ret < 0)
- goto out;
- /*
- * If expire is 0 but incr > 0 then we have a SIGALRM pending.
- * It should re-arm the timer when handled. But do_setitimer()
- * above already ignored @it_real_incr because @it_real_value
- * that was zero. So we set it manually. (This is safe against
- * malicious input, because in the worst case will generate an
- * unexpected SIGALRM to this process).
- */
- if (!h->it_real_value && h->it_real_incr)
- current->signal->it_real_incr = ns_to_ktime(h->it_real_incr);
-
- itimer.it_value = ns_to_timeval(h->it_virt_value);
- itimer.it_interval = ns_to_timeval(h->it_virt_incr);
- ret = do_setitimer(ITIMER_VIRTUAL, &itimer, NULL);
- if (ret < 0)
- goto out;
- itimer.it_value = ns_to_timeval(h->it_prof_value);
- itimer.it_interval = ns_to_timeval(h->it_prof_incr);
- ret = do_setitimer(ITIMER_PROF, &itimer, NULL);
- out:
- ckpt_hdr_put(ctx, h);
- return ret;
-}
-
-int restore_obj_signal(struct ckpt_ctx *ctx, int signal_objref)
-{
- struct signal_struct *signal;
- int ret = 0;
-
- signal = ckpt_obj_try_fetch(ctx, signal_objref, CKPT_OBJ_SIGNAL);
- if (!IS_ERR(signal)) {
- /*
- * signal_struct is already shared properly as it is
- * tied to thread groups. Since thread relationships
- * are already restore now, t->signal must match.
- */
- if (signal != current->signal)
- ret = -EINVAL;
- } else if (PTR_ERR(signal) == -EINVAL) {
- /* first timer: add to hash and restore our t->signal */
- ret = ckpt_obj_insert(ctx, current->signal,
- signal_objref, CKPT_OBJ_SIGNAL);
- if (ret >= 0)
- ret = restore_signal(ctx);
- } else {
- ret = PTR_ERR(signal);
- }
-
- return ret;
-}
-
-int checkpoint_task_signal(struct ckpt_ctx *ctx, struct task_struct *t)
-{
- struct ckpt_hdr_signal_task *h;
- struct sigpending pending;
- unsigned long flags;
- int ret;
-
- INIT_LIST_HEAD(&pending.list);
-
- /* temporarily borrow signal queue - see chekcpoint_sigpending() */
- if (!lock_task_sighand(t, &flags)) {
- ckpt_err(ctx, -EBUSY, "%(T)signand missing\n");
- return -EBUSY;
- }
- list_splice_init(&t->pending.list, &pending.list);
- pending.signal = t->pending.signal;
- unlock_task_sighand(t, &flags);
-
- ret = checkpoint_sigpending(ctx, &pending);
-
- /* re-attach the borrowed queue */
- if (!lock_task_sighand(t, &flags)) {
- ckpt_err(ctx, -EBUSY, "%(T)signand missing\n");
- return -EBUSY;
- }
- list_splice(&pending.list, &t->pending.list);
- unlock_task_sighand(t, &flags);
-
- if (ret < 0)
- return ret;
-
- h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL_TASK);
- if (!h)
- return -ENOMEM;
-
- if (task_has_saved_sigmask(t))
- fill_sigset(&h->blocked, &t->saved_sigmask);
- else
- fill_sigset(&h->blocked, &t->blocked);
-
- ret = ckpt_write_obj(ctx, &h->h);
- ckpt_hdr_put(ctx, h);
- return ret;
-}
-
-int restore_task_signal(struct ckpt_ctx *ctx)
-{
- struct ckpt_hdr_signal_task *h;
- struct sigpending new_pending;
- struct sigpending *pending;
- sigset_t blocked;
- int ret;
-
- ret = restore_sigpending(ctx, &new_pending);
- if (ret < 0)
- return ret;
-
- spin_lock_irq(¤t->sighand->siglock);
- pending = ¤t->pending;
- flush_sigqueue(pending);
- pending->signal = new_pending.signal;
- list_splice_init(&new_pending.list, &pending->list);
- spin_unlock_irq(¤t->sighand->siglock);
-
- h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL_TASK);
- if (IS_ERR(h))
- return PTR_ERR(h);
-
- load_sigset(&blocked, &h->blocked);
- /* silently remove SIGKILL, SIGSTOP */
- sigdelset(&blocked, SIGKILL);
- sigdelset(&blocked, SIGSTOP);
-
- /*
- * Unblocking signals now may affect us in wait_task_sync().
- * Instead, save blocked mask in current->saved_sigmaks for
- * post_restore_task().
- */
- current->saved_sigmask = blocked;
-
- ckpt_hdr_put(ctx, h);
- return 0;
-}
diff --git a/include/linux/signal.h b/include/linux/signal.h
index af6cac3..4a19f62 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -374,11 +374,24 @@ int unhandled_signal(struct task_struct *tsk, int sig);
(!siginmask(signr, SIG_KERNEL_IGNORE_MASK|SIG_KERNEL_STOP_MASK) && \
(t)->sighand->action[(signr)-1].sa.sa_handler == SIG_DFL)
-void signals_init(void);
-
+#ifdef CONFIG_CHECKPOINT
/* [arch] checkpoint: should saved_sigmask be used in place of blocked */
int task_has_saved_sigmask(struct task_struct *task);
+struct ckpt_ctx;
+int ckpt_collect_sighand(struct ckpt_ctx *ctx, struct task_struct *t);
+int checkpoint_sighand(struct ckpt_ctx *ctx, void *ptr);
+int checkpoint_obj_sighand(struct ckpt_ctx *ctx, struct task_struct *t);
+int checkpoint_obj_signal(struct ckpt_ctx *ctx, struct task_struct *t);
+int checkpoint_task_signal(struct ckpt_ctx *ctx, struct task_struct *t);
+void *restore_sighand(struct ckpt_ctx *ctx);
+int restore_obj_sighand(struct ckpt_ctx *ctx, int sighand_objref);
+int restore_obj_signal(struct ckpt_ctx *ctx, int signal_objref);
+int restore_task_signal(struct ckpt_ctx *ctx);
+#endif /* CONFIG_CHECKPOINT */
+
+void signals_init(void);
+
#endif /* __KERNEL__ */
#endif /* _LINUX_SIGNAL_H */
diff --git a/kernel/signal.c b/kernel/signal.c
index ce8d404..b5b5abb 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -31,6 +31,13 @@
#define CREATE_TRACE_POINTS
#include <trace/events/signal.h>
+#include <linux/errno.h>
+#include <linux/resource.h>
+#include <linux/timer.h>
+#include <linux/posix-timers.h>
+#include <linux/checkpoint.h>
+#include <linux/checkpoint_hdr.h>
+
#include <asm/param.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
@@ -2718,6 +2725,722 @@ __attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
+#ifdef CONFIG_CHECKPOINT
+/* default debug level for output */
+#define CKPT_DFLAG CKPT_DSYS
+
+static inline void fill_sigset(struct ckpt_sigset *h, sigset_t *sigset)
+{
+ memcpy(&h->sigset, sigset, sizeof(*sigset));
+}
+
+static inline void load_sigset(sigset_t *sigset, struct ckpt_sigset *h)
+{
+ memcpy(sigset, &h->sigset, sizeof(*sigset));
+}
+
+/***********************************************************************
+ * sighand checkpoint/collect/restart
+ */
+
+static int do_checkpoint_sighand(struct ckpt_ctx *ctx,
+ struct sighand_struct *sighand)
+{
+ struct ckpt_hdr_sighand *h;
+ struct ckpt_sigaction *hh;
+ struct sigaction *sa;
+ int i, ret;
+
+ h = ckpt_hdr_get_type(ctx, _NSIG * sizeof(*hh) + sizeof(*h),
+ CKPT_HDR_SIGHAND);
+ if (!h)
+ return -ENOMEM;
+
+ hh = h->action;
+ spin_lock_irq(&sighand->siglock);
+ for (i = 0; i < _NSIG; i++) {
+ sa = &sighand->action[i].sa;
+ hh[i]._sa_handler = (unsigned long) sa->sa_handler;
+ hh[i].sa_flags = sa->sa_flags;
+ hh[i].sa_restorer = (unsigned long) sa->sa_restorer;
+ fill_sigset(&hh[i].sa_mask, &sa->sa_mask);
+ }
+ spin_unlock_irq(&sighand->siglock);
+
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+int checkpoint_sighand(struct ckpt_ctx *ctx, void *ptr)
+{
+ return do_checkpoint_sighand(ctx, (struct sighand_struct *) ptr);
+}
+
+int checkpoint_obj_sighand(struct ckpt_ctx *ctx, struct task_struct *t)
+{
+ struct sighand_struct *sighand;
+ int objref;
+
+ read_lock(&tasklist_lock);
+ sighand = rcu_dereference(t->sighand);
+ atomic_inc(&sighand->count);
+ read_unlock(&tasklist_lock);
+
+ objref = checkpoint_obj(ctx, sighand, CKPT_OBJ_SIGHAND);
+ __cleanup_sighand(sighand);
+
+ return objref;
+}
+
+int ckpt_collect_sighand(struct ckpt_ctx *ctx, struct task_struct *t)
+{
+ struct sighand_struct *sighand;
+ int ret;
+
+ read_lock(&tasklist_lock);
+ sighand = rcu_dereference(t->sighand);
+ atomic_inc(&sighand->count);
+ read_unlock(&tasklist_lock);
+
+ ret = ckpt_obj_collect(ctx, sighand, CKPT_OBJ_SIGHAND);
+ __cleanup_sighand(sighand);
+
+ return ret;
+}
+
+static struct sighand_struct *do_restore_sighand(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_sighand *h;
+ struct ckpt_sigaction *hh;
+ struct sighand_struct *sighand;
+ struct sigaction *sa;
+ int i;
+
+ h = ckpt_read_obj_type(ctx, _NSIG * sizeof(*hh) + sizeof(*h),
+ CKPT_HDR_SIGHAND);
+ if (IS_ERR(h))
+ return ERR_PTR(PTR_ERR(h));
+
+ sighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
+ if (!sighand) {
+ sighand = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ atomic_set(&sighand->count, 1);
+
+ hh = h->action;
+ for (i = 0; i < _NSIG; i++) {
+ sa = &sighand->action[i].sa;
+ sa->sa_handler = (void *) (unsigned long) hh[i]._sa_handler;
+ sa->sa_flags = hh[i].sa_flags;
+ sa->sa_restorer = (void *) (unsigned long) hh[i].sa_restorer;
+ load_sigset(&sa->sa_mask, &hh[i].sa_mask);
+ }
+ out:
+ ckpt_hdr_put(ctx, h);
+ return sighand;
+}
+
+void *restore_sighand(struct ckpt_ctx *ctx)
+{
+ return (void *) do_restore_sighand(ctx);
+}
+
+int restore_obj_sighand(struct ckpt_ctx *ctx, int sighand_objref)
+{
+ struct sighand_struct *sighand;
+ struct sighand_struct *old_sighand;
+
+ sighand = ckpt_obj_fetch(ctx, sighand_objref, CKPT_OBJ_SIGHAND);
+ if (IS_ERR(sighand))
+ return PTR_ERR(sighand);
+
+ if (sighand == current->sighand)
+ return 0;
+
+ atomic_inc(&sighand->count);
+
+ /* manipulate tsk->sighand with tasklist lock write-held */
+ write_lock_irq(&tasklist_lock);
+ old_sighand = rcu_dereference(current->sighand);
+ spin_lock(&old_sighand->siglock);
+ rcu_assign_pointer(current->sighand, sighand);
+ spin_unlock(&old_sighand->siglock);
+ write_unlock_irq(&tasklist_lock);
+ __cleanup_sighand(old_sighand);
+
+ return 0;
+}
+
+/***********************************************************************
+ * signal checkpoint/restart
+ */
+
+static void fill_siginfo(struct ckpt_siginfo *si, siginfo_t *info)
+{
+ si->signo = info->si_signo;
+ si->_errno = info->si_errno;
+ si->code = info->si_code;
+
+ /* TODO: convert info->si_uid to uid_objref */
+
+ switch (info->si_code & __SI_MASK) {
+ case __SI_TIMER:
+ si->pid = info->si_tid;
+ si->uid = info->si_overrun;
+ si->sigval_int = info->si_int;
+ si->utime = info->si_sys_private;
+ break;
+ case __SI_POLL:
+ si->pid = info->si_band;
+ si->sigval_int = info->si_fd;
+ break;
+ case __SI_FAULT:
+ si->sigval_ptr = (unsigned long) info->si_addr;
+#ifdef __ARCH_SI_TRAPNO
+ si->sigval_int = info->si_trapno;
+#endif
+ break;
+ case __SI_CHLD:
+ si->pid = info->si_pid;
+ si->uid = info->si_uid;
+ si->sigval_int = info->si_status;
+ si->stime = info->si_stime;
+ si->utime = info->si_utime;
+ break;
+ case __SI_KILL:
+ case __SI_RT:
+ case __SI_MESGQ:
+ si->pid = info->si_pid;
+ si->uid = info->si_uid;
+ si->sigval_ptr = (unsigned long) info->si_ptr;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static int load_siginfo(siginfo_t *info, struct ckpt_siginfo *si)
+{
+ if (!valid_signal(si->signo))
+ return -EINVAL;
+ if (!ckpt_validate_errno(si->_errno))
+ return -EINVAL;
+
+ info->si_signo = si->signo;
+ info->si_errno = si->_errno;
+ info->si_code = si->code;
+
+ /* TODO: validate remaining signal fields */
+
+ switch (info->si_code & __SI_MASK) {
+ case __SI_TIMER:
+ info->si_tid = si->pid;
+ info->si_overrun = si->uid;
+ info->si_int = si->sigval_int;
+ info->si_sys_private = si->utime;
+ break;
+ case __SI_POLL:
+ info->si_band = si->pid;
+ info->si_fd = si->sigval_int;
+ break;
+ case __SI_FAULT:
+ info->si_addr = (void __user *) (unsigned long) si->sigval_ptr;
+#ifdef __ARCH_SI_TRAPNO
+ info->si_trapno = si->sigval_int;
+#endif
+ break;
+ case __SI_CHLD:
+ info->si_pid = si->pid;
+ info->si_uid = si->uid;
+ info->si_status = si->sigval_int;
+ info->si_stime = si->stime;
+ info->si_utime = si->utime;
+ break;
+ case __SI_KILL:
+ case __SI_RT:
+ case __SI_MESGQ:
+ info->si_pid = si->pid;
+ info->si_uid = si->uid;
+ info->si_ptr = (void __user *) (unsigned long) si->sigval_ptr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * To checkpoint pending signals (private/shared) the caller moves the
+ * signal queue (and copies the mask) to a separate struct sigpending,
+ * therefore we can iterate through it without locking.
+ * After we return, the caller re-attaches (prepends) the original
+ * signal queue to the original struct sigpending. Thus, signals that
+ * arrive(d) in the meantime will be suitably queued after these.
+ * Finally, repeated non-realtime signals will not be queued because
+ * they will already be marked in the pending mask, that remains as is.
+ * This is the expected behavior of non-realtime signals.
+ */
+static int checkpoint_sigpending(struct ckpt_ctx *ctx,
+ struct sigpending *pending)
+{
+ struct ckpt_hdr_sigpending *h;
+ struct ckpt_siginfo *si;
+ struct sigqueue *q;
+ int nr_pending = 0;
+ int ret;
+
+ list_for_each_entry(q, &pending->list, list) {
+ /* TODO: remove after adding support for posix-timers */
+ if ((q->info.si_code & __SI_MASK) == __SI_TIMER) {
+ ckpt_err(ctx, -ENOTSUPP, "%(T)signal SI_TIMER\n");
+ return -ENOTSUPP;
+ }
+ nr_pending++;
+ }
+
+ h = ckpt_hdr_get_type(ctx, nr_pending * sizeof(*si) + sizeof(*h),
+ CKPT_HDR_SIGPENDING);
+ if (!h)
+ return -ENOMEM;
+
+ h->nr_pending = nr_pending;
+ fill_sigset(&h->signal, &pending->signal);
+
+ si = h->siginfo;
+ list_for_each_entry(q, &pending->list, list)
+ fill_siginfo(si++, &q->info);
+
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+
+ return ret;
+}
+
+static int checkpoint_signal(struct ckpt_ctx *ctx, struct task_struct *t)
+{
+ struct ckpt_hdr_signal *h;
+ struct signal_struct *signal;
+ struct sigpending shared_pending;
+ struct tty_struct *tty = NULL;
+ struct rlimit *rlim;
+ struct timeval tval;
+ struct cpu_itimer *it;
+ cputime_t cputime;
+ unsigned long flags;
+ int i, ret = 0;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL);
+ if (!h)
+ return -ENOMEM;
+
+ signal = t->signal;
+ rlim = signal->rlim;
+
+ INIT_LIST_HEAD(&shared_pending.list);
+
+ /* temporarily borrow signal queue - see chekcpoint_sigpending() */
+ if (!lock_task_sighand(t, &flags)) {
+ ckpt_err(ctx, -EBUSY, "%(T)c/r: [pid %d] without sighand\n",
+ task_pid_vnr(t));
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* TODO: remove after adding support for posix-timers */
+ if (!list_empty(&signal->posix_timers)) {
+ unlock_task_sighand(t, &flags);
+ ckpt_err(ctx, -ENOTSUPP, "%(T)%(P)posix-timers\n", signal);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ list_splice_init(&signal->shared_pending.list, &shared_pending.list);
+ shared_pending.signal = signal->shared_pending.signal;
+
+ /* rlimit */
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ h->rlim[i].rlim_cur = rlim[i].rlim_cur;
+ h->rlim[i].rlim_max = rlim[i].rlim_max;
+ }
+
+ /* real/virt/prof itimers */
+ if (hrtimer_active(&signal->real_timer)) {
+ /* For an active timer compute the time delta */
+ ktime_t delta = hrtimer_get_remaining(&signal->real_timer);
+ /*
+ * If the timer expired after the the test above, then
+ * set the expire to the minimum possible (because by
+ * now the pending signal have been saved already, but
+ * the signal from this very expiry won't be sent before
+ * we release t->sighand->siglock).
+ */
+ ckpt_debug("active ! %lld\n", delta.tv64);
+ if (delta.tv64 <= 0)
+ delta.tv64 = NSEC_PER_USEC;
+ h->it_real_value = ktime_to_ns(delta);
+ } else {
+ /*
+ * Timer is inactive; if @it_real_incr is 0 the timer
+ * will not be re-armed. Beacuse we hold siglock, if
+ * @it_real_incr > 0, the timer must have just expired
+ * but not yet re-armed, and we have a SIGALRM pending
+ * - that will trigger timer re-arm after restart.
+ */
+ h->it_real_value = 0;
+ }
+ h->it_real_incr = ktime_to_ns(signal->it_real_incr);
+
+ /* for prof/virt, ignore error and incr_error */
+ it = &signal->it[CPUCLOCK_VIRT];
+ cputime = it->expires;
+ if (!cputime_eq(cputime, cputime_zero))
+ cputime = cputime_sub(it->expires, virt_ticks(t));
+ cputime_to_timeval(cputime, &tval);
+ h->it_virt_value = timeval_to_ns(&tval);
+ cputime_to_timeval(it->incr, &tval);
+ h->it_virt_incr = timeval_to_ns(&tval);
+
+ it = &signal->it[CPUCLOCK_PROF];
+ cputime = it->expires;
+ if (!cputime_eq(cputime, cputime_zero))
+ cputime = cputime_sub(it->expires, prof_ticks(t));
+ cputime_to_timeval(cputime, &tval);
+ h->it_prof_value = timeval_to_ns(&tval);
+ cputime_to_timeval(it->incr, &tval);
+ h->it_prof_incr = timeval_to_ns(&tval);
+
+ /* tty */
+ if (signal->leader) {
+ h->tty_old_pgrp = ckpt_pid_nr(ctx, signal->tty_old_pgrp);
+ tty = tty_kref_get(signal->tty);
+ if (tty) {
+ /* irq is already disabled */
+ spin_lock(&tty->ctrl_lock);
+ h->tty_pgrp = ckpt_pid_nr(ctx, tty->pgrp);
+ spin_unlock(&tty->ctrl_lock);
+ tty_kref_put(tty);
+ }
+ }
+
+ unlock_task_sighand(t, &flags);
+
+ /*
+ * If the session is in an ancestor namespace, skip this tty
+ * and set tty_objref = 0. It will not be explicitly restored,
+ * but rather inherited from parent pid-ns at restart time.
+ */
+ if (tty && ckpt_pid_nr(ctx, tty->session) > 0) {
+ h->tty_objref = checkpoint_obj(ctx, tty, CKPT_OBJ_TTY);
+ if (h->tty_objref < 0)
+ ret = h->tty_objref;
+ }
+
+ if (!ret)
+ ret = ckpt_write_obj(ctx, &h->h);
+ if (!ret)
+ ret = checkpoint_sigpending(ctx, &shared_pending);
+
+ /* return the borrowed queue */
+ if (!lock_task_sighand(t, &flags)) {
+ pr_warning("c/r: [%d] sighand disappeared\n", task_pid_vnr(t));
+ goto out;
+ }
+ list_splice(&shared_pending.list, &signal->shared_pending.list);
+ unlock_task_sighand(t, &flags);
+ out:
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+int checkpoint_obj_signal(struct ckpt_ctx *ctx, struct task_struct *t)
+{
+ BUG_ON(t->flags & PF_EXITING);
+ return checkpoint_signal(ctx, t);
+}
+
+static int restore_sigpending(struct ckpt_ctx *ctx, struct sigpending *pending)
+{
+ struct ckpt_hdr_sigpending *h;
+ struct ckpt_siginfo *si;
+ struct sigqueue *q;
+ int ret = 0;
+
+ h = ckpt_read_buf_type(ctx, 0, CKPT_HDR_SIGPENDING);
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ if (h->h.len != h->nr_pending * sizeof(*si) + sizeof(*h)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&pending->list);
+ load_sigset(&pending->signal, &h->signal);
+
+ si = h->siginfo;
+ while (h->nr_pending--) {
+ q = sigqueue_alloc();
+ if (!q) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = load_siginfo(&q->info, si++);
+ if (ret < 0) {
+ sigqueue_free(q);
+ break;
+ }
+
+ q->flags &= ~SIGQUEUE_PREALLOC;
+ list_add_tail(&pending->list, &q->list);
+ }
+
+ if (ret < 0)
+ flush_sigqueue(pending);
+ out:
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+static int restore_signal(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_signal *h;
+ struct sigpending new_pending;
+ struct sigpending *pending;
+ struct tty_struct *tty = NULL;
+ struct itimerval itimer;
+ struct rlimit rlim;
+ struct pid *pgrp = NULL;
+ int i, ret;
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL);
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ /* rlimit */
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim.rlim_cur = h->rlim[i].rlim_cur;
+ rlim.rlim_max = h->rlim[i].rlim_max;
+ ret = do_setrlimit(i, &rlim);
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = restore_sigpending(ctx, &new_pending);
+ if (ret < 0)
+ goto out;
+
+ /* tty - session */
+ if (h->tty_objref) {
+ tty = ckpt_obj_fetch(ctx, h->tty_objref, CKPT_OBJ_TTY);
+ if (IS_ERR(tty)) {
+ ret = PTR_ERR(tty);
+ goto out;
+ }
+ /* this will fail unless we're the session leader */
+ ret = tiocsctty(tty, 0);
+ if (ret < 0)
+ goto out;
+ /* now restore the foreground group (job control) */
+ if (h->tty_pgrp) {
+ /*
+ * If tty_pgrp == CKPT_PID_NULL, below will
+ * fail, so no need for explicit test
+ */
+ ret = do_tiocspgrp(tty, tty_pair_get_tty(tty),
+ h->tty_pgrp);
+ if (ret < 0)
+ goto out;
+ }
+ } else {
+ /*
+ * If tty_objref isn't set, we _keep_ whatever tty we
+ * already have as a ctty. Why does this make sense ?
+ * - If our session is "within" the restart context,
+ * then that session has no controlling terminal.
+ * - If out session is "outside" the restart context,
+ * then we're like to keep whatever we inherit from
+ * the parent pid-ns.
+ */
+ }
+
+ /*
+ * Reset real/virt/prof itimer (in case they were set), to
+ * prevent unwanted signals after flushing current signals
+ * and before restoring original real/virt/prof itimer.
+ */
+ itimer.it_value = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
+ itimer.it_interval = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
+ do_setitimer(ITIMER_REAL, &itimer, NULL);
+ do_setitimer(ITIMER_VIRTUAL, &itimer, NULL);
+ do_setitimer(ITIMER_PROF, &itimer, NULL);
+
+ /* tty - tty_old_pgrp */
+ if (current->signal->leader && h->tty_old_pgrp != CKPT_PID_NULL) {
+ rcu_read_lock();
+ pgrp = get_pid(_ckpt_find_pgrp(ctx, h->tty_old_pgrp));
+ rcu_read_unlock();
+ if (!pgrp)
+ goto out;
+ }
+
+ spin_lock_irq(¤t->sighand->siglock);
+ /* tty - tty_old_pgrp */
+ put_pid(current->signal->tty_old_pgrp);
+ current->signal->tty_old_pgrp = pgrp;
+ /* pending signals */
+ pending = ¤t->signal->shared_pending;
+ flush_sigqueue(pending);
+ pending->signal = new_pending.signal;
+ list_splice_init(&new_pending.list, &pending->list);
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ /* real/virt/prof itimers */
+ itimer.it_value = ns_to_timeval(h->it_real_value);
+ itimer.it_interval = ns_to_timeval(h->it_real_incr);
+ ret = do_setitimer(ITIMER_REAL, &itimer, NULL);
+ if (ret < 0)
+ goto out;
+ /*
+ * If expire is 0 but incr > 0 then we have a SIGALRM pending.
+ * It should re-arm the timer when handled. But do_setitimer()
+ * above already ignored @it_real_incr because @it_real_value
+ * that was zero. So we set it manually. (This is safe against
+ * malicious input, because in the worst case will generate an
+ * unexpected SIGALRM to this process).
+ */
+ if (!h->it_real_value && h->it_real_incr)
+ current->signal->it_real_incr = ns_to_ktime(h->it_real_incr);
+
+ itimer.it_value = ns_to_timeval(h->it_virt_value);
+ itimer.it_interval = ns_to_timeval(h->it_virt_incr);
+ ret = do_setitimer(ITIMER_VIRTUAL, &itimer, NULL);
+ if (ret < 0)
+ goto out;
+ itimer.it_value = ns_to_timeval(h->it_prof_value);
+ itimer.it_interval = ns_to_timeval(h->it_prof_incr);
+ ret = do_setitimer(ITIMER_PROF, &itimer, NULL);
+ out:
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+int restore_obj_signal(struct ckpt_ctx *ctx, int signal_objref)
+{
+ struct signal_struct *signal;
+ int ret = 0;
+
+ signal = ckpt_obj_try_fetch(ctx, signal_objref, CKPT_OBJ_SIGNAL);
+ if (!IS_ERR(signal)) {
+ /*
+ * signal_struct is already shared properly as it is
+ * tied to thread groups. Since thread relationships
+ * are already restore now, t->signal must match.
+ */
+ if (signal != current->signal)
+ ret = -EINVAL;
+ } else if (PTR_ERR(signal) == -EINVAL) {
+ /* first timer: add to hash and restore our t->signal */
+ ret = ckpt_obj_insert(ctx, current->signal,
+ signal_objref, CKPT_OBJ_SIGNAL);
+ if (ret >= 0)
+ ret = restore_signal(ctx);
+ } else {
+ ret = PTR_ERR(signal);
+ }
+
+ return ret;
+}
+
+int checkpoint_task_signal(struct ckpt_ctx *ctx, struct task_struct *t)
+{
+ struct ckpt_hdr_signal_task *h;
+ struct sigpending pending;
+ unsigned long flags;
+ int ret;
+
+ INIT_LIST_HEAD(&pending.list);
+
+ /* temporarily borrow signal queue - see chekcpoint_sigpending() */
+ if (!lock_task_sighand(t, &flags)) {
+ ckpt_err(ctx, -EBUSY, "%(T)signand missing\n");
+ return -EBUSY;
+ }
+ list_splice_init(&t->pending.list, &pending.list);
+ pending.signal = t->pending.signal;
+ unlock_task_sighand(t, &flags);
+
+ ret = checkpoint_sigpending(ctx, &pending);
+
+ /* re-attach the borrowed queue */
+ if (!lock_task_sighand(t, &flags)) {
+ ckpt_err(ctx, -EBUSY, "%(T)signand missing\n");
+ return -EBUSY;
+ }
+ list_splice(&pending.list, &t->pending.list);
+ unlock_task_sighand(t, &flags);
+
+ if (ret < 0)
+ return ret;
+
+ h = ckpt_hdr_get_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL_TASK);
+ if (!h)
+ return -ENOMEM;
+
+ if (task_has_saved_sigmask(t))
+ fill_sigset(&h->blocked, &t->saved_sigmask);
+ else
+ fill_sigset(&h->blocked, &t->blocked);
+
+ ret = ckpt_write_obj(ctx, &h->h);
+ ckpt_hdr_put(ctx, h);
+ return ret;
+}
+
+int restore_task_signal(struct ckpt_ctx *ctx)
+{
+ struct ckpt_hdr_signal_task *h;
+ struct sigpending new_pending;
+ struct sigpending *pending;
+ sigset_t blocked;
+ int ret;
+
+ ret = restore_sigpending(ctx, &new_pending);
+ if (ret < 0)
+ return ret;
+
+ spin_lock_irq(¤t->sighand->siglock);
+ pending = ¤t->pending;
+ flush_sigqueue(pending);
+ pending->signal = new_pending.signal;
+ list_splice_init(&new_pending.list, &pending->list);
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ h = ckpt_read_obj_type(ctx, sizeof(*h), CKPT_HDR_SIGNAL_TASK);
+ if (IS_ERR(h))
+ return PTR_ERR(h);
+
+ load_sigset(&blocked, &h->blocked);
+ /* silently remove SIGKILL, SIGSTOP */
+ sigdelset(&blocked, SIGKILL);
+ sigdelset(&blocked, SIGSTOP);
+
+ /*
+ * Unblocking signals now may affect us in wait_task_sync().
+ * Instead, save blocked mask in current->saved_sigmaks for
+ * post_restore_task().
+ */
+ current->saved_sigmask = blocked;
+
+ ckpt_hdr_put(ctx, h);
+ return 0;
+}
+#endif /* CONFIG_CHECKPOINT */
+
void __init signals_init(void)
{
sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC);
--
1.6.3.3
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list