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)
int n;
struct k_itimer *t = x->timer;
t->it_timer.magic = x->i;
t->it.mmtimer.clock = x->i;
t->it_overrun--;
n = 0;
do {
t->it_timer.expires += t->it_incr << n;
t->it.mmtimer.expires += t->it.mmtimer.incr << n;
t->it_overrun += 1 << n;
n++;
if (n > 20)
return 1;
} while (mmtimer_setup(x->i, t->it_timer.expires));
} while (mmtimer_setup(x->i, t->it.mmtimer.expires));
return 0;
}
......@@ -466,7 +466,7 @@ mmtimer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
spin_lock(&base[i].lock);
if (base[i].cpu == smp_processor_id()) {
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 */
if ((mmtimer_int_pending(i) > 0) ||
(expires && (expires < rtc_time()))) {
......@@ -503,7 +503,7 @@ void mmtimer_tasklet(unsigned long data) {
t->it_overrun++;
}
if(t->it_incr) {
if(t->it.mmtimer.incr) {
/* Periodic timer */
if (reschedule_periodic_timer(x)) {
printk(KERN_WARNING "mmtimer: unable to reschedule\n");
......@@ -511,7 +511,7 @@ void mmtimer_tasklet(unsigned long data) {
}
} else {
/* 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;
out:
......@@ -522,7 +522,7 @@ void mmtimer_tasklet(unsigned long data) {
static int sgi_timer_create(struct k_itimer *timer)
{
/* Insure that a newly created timer is off */
timer->it_timer.magic = TIMER_OFF;
timer->it.mmtimer.clock = TIMER_OFF;
return 0;
}
......@@ -533,8 +533,8 @@ static int sgi_timer_create(struct k_itimer *timer)
*/
static int sgi_timer_del(struct k_itimer *timr)
{
int i = timr->it_timer.magic;
cnodeid_t nodeid = timr->it_timer.data;
int i = timr->it.mmtimer.clock;
cnodeid_t nodeid = timr->it.mmtimer.node;
mmtimer_t *t = timers + nodeid * NUM_COMPARATORS +i;
unsigned long irqflags;
......@@ -542,8 +542,8 @@ static int sgi_timer_del(struct k_itimer *timr)
spin_lock_irqsave(&t->lock, irqflags);
mmtimer_disable_int(cnodeid_to_nasid(nodeid),i);
t->timer = NULL;
timr->it_timer.magic = TIMER_OFF;
timr->it_timer.expires = 0;
timr->it.mmtimer.clock = TIMER_OFF;
timr->it.mmtimer.expires = 0;
spin_unlock_irqrestore(&t->lock, irqflags);
}
return 0;
......@@ -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)
{
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_sec = 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)
return;
}
ns_to_timespec(cur_setting->it_interval, timr->it_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_interval, timr->it.mmtimer.incr * sgi_clock_period);
ns_to_timespec(cur_setting->it_value, (timr->it.mmtimer.expires - rtc_time())* sgi_clock_period);
return;
}
......@@ -638,19 +638,19 @@ static int sgi_timer_set(struct k_itimer *timr, int flags,
base[i].timer = timr;
base[i].cpu = smp_processor_id();
timr->it_timer.magic = i;
timr->it_timer.data = nodeid;
timr->it_incr = period;
timr->it_timer.expires = when;
timr->it.mmtimer.clock = i;
timr->it.mmtimer.node = nodeid;
timr->it.mmtimer.incr = period;
timr->it.mmtimer.expires = when;
if (period == 0) {
if (mmtimer_setup(i, when)) {
mmtimer_disable_int(-1, i);
posix_timer_event(timr, 0);
timr->it_timer.expires = 0;
timr->it.mmtimer.expires = 0;
}
} else {
timr->it_timer.expires -= period;
timr->it.mmtimer.expires -= period;
if (reschedule_periodic_timer(base+i))
err = -EINVAL;
}
......
......@@ -51,6 +51,7 @@
.list = LIST_HEAD_INIT(sig.shared_pending.list), \
.signal = {{0}}}, \
.posix_timers = LIST_HEAD_INIT(sig.posix_timers), \
.cpu_timers = INIT_CPU_TIMERS(sig.cpu_timers), \
.rlim = INIT_RLIMITS, \
}
......@@ -112,8 +113,16 @@ extern struct group_info init_groups;
.proc_lock = SPIN_LOCK_UNLOCKED, \
.switch_lock = SPIN_LOCK_UNLOCKED, \
.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
......@@ -3,6 +3,19 @@
#include <linux/spinlock.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_PERTHREAD(clock) \
......@@ -30,15 +43,27 @@ struct k_itimer {
int it_overrun; /* overrun on pending signal */
int it_overrun_last; /* overrun on last delivered signal */
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_signo; /* signo 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 timer_list it_timer;
struct sigqueue *sigq; /* signal queue entry. */
union {
struct {
struct timer_list timer;
struct list_head abs_timer_entry; /* clock abs_timer_list */
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 {
......@@ -57,6 +82,7 @@ struct k_clock {
struct itimerspec * new_setting,
struct itimerspec * old_setting);
int (*timer_del) (struct k_itimer * timr);
#define TIMER_RETRY 1
void (*timer_get) (struct k_itimer * timr,
struct itimerspec * cur_setting);
};
......@@ -82,10 +108,11 @@ struct now_struct {
#define posix_bump_timer(timr, now) \
do { \
long delta, orun; \
delta = now.jiffies - (timr)->it_timer.expires; \
delta = now.jiffies - (timr)->it.real.timer.expires; \
if (delta >= 0) { \
orun = 1 + (delta / (timr)->it_incr); \
(timr)->it_timer.expires += orun * (timr)->it_incr; \
orun = 1 + (delta / (timr)->it.real.incr); \
(timr)->it.real.timer.expires += \
orun * (timr)->it.real.incr; \
(timr)->it_overrun += orun; \
} \
}while (0)
......@@ -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_timer_create(struct k_itimer *);
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,
struct itimerspec *, struct itimerspec *);
int posix_cpu_timer_del(struct k_itimer *);
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
......@@ -338,6 +338,8 @@ struct signal_struct {
* have no need to disable irqs.
*/
struct rlimit rlim[RLIM_NLIMITS];
struct list_head cpu_timers[3];
};
/*
......@@ -612,6 +614,11 @@ struct task_struct {
struct timespec start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
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 */
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
......
......@@ -759,6 +759,9 @@ static void exit_notify(struct task_struct *tsk)
*/
tsk->it_virt_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);
......
......@@ -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->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 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);
memcpy(sig->rlim, current->signal->rlim, sizeof sig->rlim);
......@@ -885,6 +888,13 @@ static task_t *copy_process(unsigned long clone_flags,
p->syscw = 0; /* I/O counter: write syscalls */
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 */
do_posix_clock_monotonic_gettime(&p->start_time);
p->security = NULL;
......@@ -1017,6 +1027,16 @@ static task_t *copy_process(unsigned long clone_flags,
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);
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -22,6 +22,7 @@
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/ptrace.h>
#include <linux/posix-timers.h>
#include <asm/param.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
......@@ -347,7 +348,9 @@ void __exit_signal(struct task_struct *tsk)
if (!atomic_read(&sig->count))
BUG();
spin_lock(&sighand->siglock);
posix_cpu_timers_exit(tsk);
if (atomic_dec_and_test(&sig->count)) {
posix_cpu_timers_exit_group(tsk);
if (tsk == sig->curr_target)
sig->curr_target = next_thread(tsk);
tsk->signal = NULL;
......
......@@ -30,6 +30,7 @@
#include <linux/thread_info.h>
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/cpu.h>
#include <linux/syscalls.h>
......@@ -825,6 +826,7 @@ void update_process_times(int user_tick)
if (rcu_pending(cpu))
rcu_check_callbacks(cpu, user_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