Commit 2c3871a8 authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] posix-timers: CPU clock support for POSIX timers

POSIX requires that when you claim _POSIX_CPUTIME and _POSIX_THREAD_CPUTIME,
not only the clock_* calls but also timer_* calls must support the thread and
process CPU time clocks.  This patch provides that support, building on my
recent additions to support these clocks in the POSIX clock_* interfaces.
This patch will not work without those changes, as well as the patch fixing
the timer lock-siglock deadlock problem.

The apparent pervasive changes to posix-timers.c are simply that some fields
of struct k_itimer have changed name and moved into a union.  This was
appropriate since the data structures required for the existing real-time
timer support and for the new thread/process CPU-time timers are quite
different.

The glibc patches to support CPU time clocks using the new kernel support is
in http://people.redhat.com/roland/glibc/kernel-cpuclocks.patch, and that
includes tests for the timer support (if you build glibc with NPTL).
From: Christoph Lameter <clameter@sgi.com>

  Your patch breaks the mmtimer driver because it used k_itimer values for
  its own purposes.  Here is a fix by defining an additional structure in
  k_itimer (same approach for mmtimer as the cpu timers):
From: Roland McGrath <roland@redhat.com>

Fix bug identified by Alexander Nyberg <alexn@dsv.su.se>

> The problem arises from code touching the union in alloc_posix_timer()
> which makes firing go non-zero. When firing is checked in
> posix_cpu_timer_set() it will be positive causing an infinite loop.
>
> So either the below fix or preferably move the INIT_LIST_HEAD(x) from
> alloc_posix_timer() to somewhere later where it doesn't disturb the other
> union members.

Thanks for finding this problem.  The latter is what I think is the right
solution.  This patch does that, and also removes some superfluous rezeroing.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarChristoph Lameter <clameter@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 68f35a6b
...@@ -418,19 +418,19 @@ static int inline reschedule_periodic_timer(mmtimer_t *x) ...@@ -418,19 +418,19 @@ static int inline reschedule_periodic_timer(mmtimer_t *x)
int n; int n;
struct k_itimer *t = x->timer; struct k_itimer *t = x->timer;
t->it_timer.magic = x->i; t->it.mmtimer.clock = x->i;
t->it_overrun--; t->it_overrun--;
n = 0; n = 0;
do { do {
t->it_timer.expires += t->it_incr << n; t->it.mmtimer.expires += t->it.mmtimer.incr << n;
t->it_overrun += 1 << n; t->it_overrun += 1 << n;
n++; n++;
if (n > 20) if (n > 20)
return 1; return 1;
} while (mmtimer_setup(x->i, t->it_timer.expires)); } while (mmtimer_setup(x->i, t->it.mmtimer.expires));
return 0; return 0;
} }
...@@ -466,7 +466,7 @@ mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -466,7 +466,7 @@ mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
spin_lock(&base[i].lock); spin_lock(&base[i].lock);
if (base[i].cpu == smp_processor_id()) { if (base[i].cpu == smp_processor_id()) {
if (base[i].timer) if (base[i].timer)
expires = base[i].timer->it_timer.expires; expires = base[i].timer->it.mmtimer.expires;
/* expires test won't work with shared irqs */ /* expires test won't work with shared irqs */
if ((mmtimer_int_pending(i) > 0) || if ((mmtimer_int_pending(i) > 0) ||
(expires && (expires < rtc_time()))) { (expires && (expires < rtc_time()))) {
...@@ -503,7 +503,7 @@ void mmtimer_tasklet(unsigned long data) { ...@@ -503,7 +503,7 @@ void mmtimer_tasklet(unsigned long data) {
t->it_overrun++; t->it_overrun++;
} }
if(t->it_incr) { if(t->it.mmtimer.incr) {
/* Periodic timer */ /* Periodic timer */
if (reschedule_periodic_timer(x)) { if (reschedule_periodic_timer(x)) {
printk(KERN_WARNING "mmtimer: unable to reschedule\n"); printk(KERN_WARNING "mmtimer: unable to reschedule\n");
...@@ -511,7 +511,7 @@ void mmtimer_tasklet(unsigned long data) { ...@@ -511,7 +511,7 @@ void mmtimer_tasklet(unsigned long data) {
} }
} else { } else {
/* Ensure we don't false trigger in mmtimer_interrupt */ /* Ensure we don't false trigger in mmtimer_interrupt */
t->it_timer.expires = 0; t->it.mmtimer.expires = 0;
} }
t->it_overrun_last = t->it_overrun; t->it_overrun_last = t->it_overrun;
out: out:
...@@ -522,7 +522,7 @@ void mmtimer_tasklet(unsigned long data) { ...@@ -522,7 +522,7 @@ void mmtimer_tasklet(unsigned long data) {
static int sgi_timer_create(struct k_itimer *timer) static int sgi_timer_create(struct k_itimer *timer)
{ {
/* Insure that a newly created timer is off */ /* Insure that a newly created timer is off */
timer->it_timer.magic = TIMER_OFF; timer->it.mmtimer.clock = TIMER_OFF;
return 0; return 0;
} }
...@@ -533,8 +533,8 @@ static int sgi_timer_create(struct k_itimer *timer) ...@@ -533,8 +533,8 @@ static int sgi_timer_create(struct k_itimer *timer)
*/ */
static int sgi_timer_del(struct k_itimer *timr) static int sgi_timer_del(struct k_itimer *timr)
{ {
int i = timr->it_timer.magic; int i = timr->it.mmtimer.clock;
cnodeid_t nodeid = timr->it_timer.data; cnodeid_t nodeid = timr->it.mmtimer.node;
mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i; mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i;
unsigned long irqflags; unsigned long irqflags;
...@@ -542,8 +542,8 @@ static int sgi_timer_del(struct k_itimer *timr) ...@@ -542,8 +542,8 @@ static int sgi_timer_del(struct k_itimer *timr)
spin_lock_irqsave(&t->lock, irqflags); spin_lock_irqsave(&t->lock, irqflags);
mmtimer_disable_int(cnodeid_to_nasid(nodeid),i); mmtimer_disable_int(cnodeid_to_nasid(nodeid),i);
t->timer = NULL; t->timer = NULL;
timr->it_timer.magic = TIMER_OFF; timr->it.mmtimer.clock = TIMER_OFF;
timr->it_timer.expires = 0; timr->it.mmtimer.expires = 0;
spin_unlock_irqrestore(&t->lock, irqflags); spin_unlock_irqrestore(&t->lock, irqflags);
} }
return 0; return 0;
...@@ -556,7 +556,7 @@ static int sgi_timer_del(struct k_itimer *timr) ...@@ -556,7 +556,7 @@ static int sgi_timer_del(struct k_itimer *timr)
static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
{ {
if (timr->it_timer.magic == TIMER_OFF) { if (timr->it.mmtimer.clock == TIMER_OFF) {
cur_setting->it_interval.tv_nsec = 0; cur_setting->it_interval.tv_nsec = 0;
cur_setting->it_interval.tv_sec = 0; cur_setting->it_interval.tv_sec = 0;
cur_setting->it_value.tv_nsec = 0; cur_setting->it_value.tv_nsec = 0;
...@@ -564,8 +564,8 @@ static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) ...@@ -564,8 +564,8 @@ static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
return; return;
} }
ns_to_timespec(cur_setting->it_interval, timr->it_incr * sgi_clock_period); ns_to_timespec(cur_setting->it_interval, timr->it.mmtimer.incr * sgi_clock_period);
ns_to_timespec(cur_setting->it_value, (timr->it_timer.expires - rtc_time())* sgi_clock_period); ns_to_timespec(cur_setting->it_value, (timr->it.mmtimer.expires - rtc_time())* sgi_clock_period);
return; return;
} }
...@@ -638,19 +638,19 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, ...@@ -638,19 +638,19 @@ static int sgi_timer_set(struct k_itimer *timr, int flags,
base[i].timer = timr; base[i].timer = timr;
base[i].cpu = smp_processor_id(); base[i].cpu = smp_processor_id();
timr->it_timer.magic = i; timr->it.mmtimer.clock = i;
timr->it_timer.data = nodeid; timr->it.mmtimer.node = nodeid;
timr->it_incr = period; timr->it.mmtimer.incr = period;
timr->it_timer.expires = when; timr->it.mmtimer.expires = when;
if (period == 0) { if (period == 0) {
if (mmtimer_setup(i, when)) { if (mmtimer_setup(i, when)) {
mmtimer_disable_int(-1, i); mmtimer_disable_int(-1, i);
posix_timer_event(timr, 0); posix_timer_event(timr, 0);
timr->it_timer.expires = 0; timr->it.mmtimer.expires = 0;
} }
} else { } else {
timr->it_timer.expires -= period; timr->it.mmtimer.expires -= period;
if (reschedule_periodic_timer(base+i)) if (reschedule_periodic_timer(base+i))
err = -EINVAL; err = -EINVAL;
} }
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
.list = LIST_HEAD_INIT(sig.shared_pending.list), \ .list = LIST_HEAD_INIT(sig.shared_pending.list), \
.signal = {{0}}}, \ .signal = {{0}}}, \
.posix_timers = LIST_HEAD_INIT(sig.posix_timers), \ .posix_timers = LIST_HEAD_INIT(sig.posix_timers), \
.cpu_timers = INIT_CPU_TIMERS(sig.cpu_timers), \
.rlim = INIT_RLIMITS, \ .rlim = INIT_RLIMITS, \
} }
...@@ -112,8 +113,16 @@ extern struct group_info init_groups; ...@@ -112,8 +113,16 @@ extern struct group_info init_groups;
.proc_lock = SPIN_LOCK_UNLOCKED, \ .proc_lock = SPIN_LOCK_UNLOCKED, \
.switch_lock = SPIN_LOCK_UNLOCKED, \ .switch_lock = SPIN_LOCK_UNLOCKED, \
.journal_info = NULL, \ .journal_info = NULL, \
.cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
} }
#define INIT_CPU_TIMERS(cpu_timers) \
{ \
LIST_HEAD_INIT(cpu_timers[0]), \
LIST_HEAD_INIT(cpu_timers[1]), \
LIST_HEAD_INIT(cpu_timers[2]), \
}
#endif #endif
...@@ -3,6 +3,19 @@ ...@@ -3,6 +3,19 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/sched.h>
union cpu_time_count {
cputime_t cpu;
unsigned long long sched;
};
struct cpu_timer_list {
struct list_head entry;
union cpu_time_count expires, incr;
struct task_struct *task;
int firing;
};
#define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3)) #define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3))
#define CPUCLOCK_PERTHREAD(clock) \ #define CPUCLOCK_PERTHREAD(clock) \
...@@ -30,15 +43,27 @@ struct k_itimer { ...@@ -30,15 +43,27 @@ struct k_itimer {
int it_overrun; /* overrun on pending signal */ int it_overrun; /* overrun on pending signal */
int it_overrun_last; /* overrun on last delivered signal */ int it_overrun_last; /* overrun on last delivered signal */
int it_requeue_pending; /* waiting to requeue this timer */ int it_requeue_pending; /* waiting to requeue this timer */
#define REQUEUE_PENDING 1
int it_sigev_notify; /* notify word of sigevent struct */ int it_sigev_notify; /* notify word of sigevent struct */
int it_sigev_signo; /* signo word of sigevent struct */ int it_sigev_signo; /* signo word of sigevent struct */
sigval_t it_sigev_value; /* value word of sigevent struct */ sigval_t it_sigev_value; /* value word of sigevent struct */
unsigned long it_incr; /* interval specified in jiffies */
struct task_struct *it_process; /* process to send signal to */ struct task_struct *it_process; /* process to send signal to */
struct timer_list it_timer;
struct sigqueue *sigq; /* signal queue entry. */ struct sigqueue *sigq; /* signal queue entry. */
union {
struct {
struct timer_list timer;
struct list_head abs_timer_entry; /* clock abs_timer_list */ struct list_head abs_timer_entry; /* clock abs_timer_list */
struct timespec wall_to_prev; /* wall_to_monotonic used when set */ struct timespec wall_to_prev; /* wall_to_monotonic used when set */
unsigned long incr; /* interval in jiffies */
} real;
struct cpu_timer_list cpu;
struct {
unsigned int clock;
unsigned int node;
unsigned long incr;
unsigned long expires;
} mmtimer;
} it;
}; };
struct k_clock_abs { struct k_clock_abs {
...@@ -57,6 +82,7 @@ struct k_clock { ...@@ -57,6 +82,7 @@ struct k_clock {
struct itimerspec * new_setting, struct itimerspec * new_setting,
struct itimerspec * old_setting); struct itimerspec * old_setting);
int (*timer_del) (struct k_itimer * timr); int (*timer_del) (struct k_itimer * timr);
#define TIMER_RETRY 1
void (*timer_get) (struct k_itimer * timr, void (*timer_get) (struct k_itimer * timr,
struct itimerspec * cur_setting); struct itimerspec * cur_setting);
}; };
...@@ -82,10 +108,11 @@ struct now_struct { ...@@ -82,10 +108,11 @@ struct now_struct {
#define posix_bump_timer(timr, now) \ #define posix_bump_timer(timr, now) \
do { \ do { \
long delta, orun; \ long delta, orun; \
delta = now.jiffies - (timr)->it_timer.expires; \ delta = now.jiffies - (timr)->it.real.timer.expires; \
if (delta >= 0) { \ if (delta >= 0) { \
orun = 1 + (delta / (timr)->it_incr); \ orun = 1 + (delta / (timr)->it.real.incr); \
(timr)->it_timer.expires += orun * (timr)->it_incr; \ (timr)->it.real.timer.expires += \
orun * (timr)->it.real.incr; \
(timr)->it_overrun += orun; \ (timr)->it_overrun += orun; \
} \ } \
}while (0) }while (0)
...@@ -95,12 +122,16 @@ int posix_cpu_clock_get(clockid_t which_clock, struct timespec *); ...@@ -95,12 +122,16 @@ int posix_cpu_clock_get(clockid_t which_clock, struct timespec *);
int posix_cpu_clock_set(clockid_t which_clock, const struct timespec *tp); int posix_cpu_clock_set(clockid_t which_clock, const struct timespec *tp);
int posix_cpu_timer_create(struct k_itimer *); int posix_cpu_timer_create(struct k_itimer *);
int posix_cpu_nsleep(clockid_t, int, struct timespec *); int posix_cpu_nsleep(clockid_t, int, struct timespec *);
#define posix_cpu_timer_create do_posix_clock_notimer_create
#define posix_cpu_nsleep do_posix_clock_nonanosleep
int posix_cpu_timer_set(struct k_itimer *, int, int posix_cpu_timer_set(struct k_itimer *, int,
struct itimerspec *, struct itimerspec *); struct itimerspec *, struct itimerspec *);
int posix_cpu_timer_del(struct k_itimer *); int posix_cpu_timer_del(struct k_itimer *);
void posix_cpu_timer_get(struct k_itimer *, struct itimerspec *); void posix_cpu_timer_get(struct k_itimer *, struct itimerspec *);
void posix_cpu_timer_schedule(struct k_itimer *);
void run_posix_cpu_timers(struct task_struct *);
void posix_cpu_timers_exit(struct task_struct *);
void posix_cpu_timers_exit_group(struct task_struct *);
#endif #endif
...@@ -338,6 +338,8 @@ struct signal_struct { ...@@ -338,6 +338,8 @@ struct signal_struct {
* have no need to disable irqs. * have no need to disable irqs.
*/ */
struct rlimit rlim[RLIM_NLIMITS]; struct rlimit rlim[RLIM_NLIMITS];
struct list_head cpu_timers[3];
}; };
/* /*
...@@ -612,6 +614,11 @@ struct task_struct { ...@@ -612,6 +614,11 @@ struct task_struct {
struct timespec start_time; struct timespec start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
unsigned long min_flt, maj_flt; unsigned long min_flt, maj_flt;
cputime_t it_prof_expires, it_virt_expires;
unsigned long long it_sched_expires;
struct list_head cpu_timers[3];
/* process credentials */ /* process credentials */
uid_t uid,euid,suid,fsuid; uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid; gid_t gid,egid,sgid,fsgid;
......
...@@ -759,6 +759,9 @@ static void exit_notify(struct task_struct *tsk) ...@@ -759,6 +759,9 @@ static void exit_notify(struct task_struct *tsk)
*/ */
tsk->it_virt_value = cputime_zero; tsk->it_virt_value = cputime_zero;
tsk->it_prof_value = cputime_zero; tsk->it_prof_value = cputime_zero;
tsk->it_virt_expires = cputime_zero;
tsk->it_prof_expires = cputime_zero;
tsk->it_sched_expires = 0;
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
......
...@@ -750,6 +750,9 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts ...@@ -750,6 +750,9 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts
sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0;
sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0;
sig->sched_time = 0; sig->sched_time = 0;
INIT_LIST_HEAD(&sig->cpu_timers[0]);
INIT_LIST_HEAD(&sig->cpu_timers[1]);
INIT_LIST_HEAD(&sig->cpu_timers[2]);
task_lock(current->group_leader); task_lock(current->group_leader);
memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim); memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
...@@ -885,6 +888,13 @@ static task_t *copy_process(unsigned long clone_flags, ...@@ -885,6 +888,13 @@ static task_t *copy_process(unsigned long clone_flags,
p->syscw = 0; /* I/O counter: write syscalls */ p->syscw = 0; /* I/O counter: write syscalls */
acct_clear_integrals(p); acct_clear_integrals(p);
p->it_virt_expires = cputime_zero;
p->it_prof_expires = cputime_zero;
p->it_sched_expires = 0;
INIT_LIST_HEAD(&p->cpu_timers[0]);
INIT_LIST_HEAD(&p->cpu_timers[1]);
INIT_LIST_HEAD(&p->cpu_timers[2]);
p->lock_depth = -1; /* -1 = no lock */ p->lock_depth = -1; /* -1 = no lock */
do_posix_clock_monotonic_gettime(&p->start_time); do_posix_clock_monotonic_gettime(&p->start_time);
p->security = NULL; p->security = NULL;
...@@ -1017,6 +1027,16 @@ static task_t *copy_process(unsigned long clone_flags, ...@@ -1017,6 +1027,16 @@ static task_t *copy_process(unsigned long clone_flags,
set_tsk_thread_flag(p, TIF_SIGPENDING); set_tsk_thread_flag(p, TIF_SIGPENDING);
} }
if (!list_empty(&current->signal->cpu_timers[0]) ||
!list_empty(&current->signal->cpu_timers[1]) ||
!list_empty(&current->signal->cpu_timers[2])) {
/*
* Have child wake up on its first tick to check
* for process CPU timers.
*/
p->it_prof_expires = jiffies_to_cputime(1);
}
spin_unlock(&current->sighand->siglock); spin_unlock(&current->sighand->siglock);
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/security.h> #include <linux/security.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/posix-timers.h>
#include <asm/param.h> #include <asm/param.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/unistd.h> #include <asm/unistd.h>
...@@ -347,7 +348,9 @@ void __exit_signal(struct task_struct *tsk) ...@@ -347,7 +348,9 @@ void __exit_signal(struct task_struct *tsk)
if (!atomic_read(&sig->count)) if (!atomic_read(&sig->count))
BUG(); BUG();
spin_lock(&sighand->siglock); spin_lock(&sighand->siglock);
posix_cpu_timers_exit(tsk);
if (atomic_dec_and_test(&sig->count)) { if (atomic_dec_and_test(&sig->count)) {
posix_cpu_timers_exit_group(tsk);
if (tsk == sig->curr_target) if (tsk == sig->curr_target)
sig->curr_target = next_thread(tsk); sig->curr_target = next_thread(tsk);
tsk->signal = NULL; tsk->signal = NULL;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
...@@ -825,6 +826,7 @@ void update_process_times(int user_tick) ...@@ -825,6 +826,7 @@ void update_process_times(int user_tick)
if (rcu_pending(cpu)) if (rcu_pending(cpu))
rcu_check_callbacks(cpu, user_tick); rcu_check_callbacks(cpu, user_tick);
scheduler_tick(); scheduler_tick();
run_posix_cpu_timers(p);
} }
/* /*
......
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