Merge branches 'timers/clocksource', 'timers/hrtimers', 'timers/nohz', 'timers/ntp', 'timers/posixtimers' and 'timers/debug' into v28-timers-for-linus
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#
|
||||
config TICK_ONESHOT
|
||||
bool
|
||||
default n
|
||||
|
||||
config NO_HZ
|
||||
bool "Tickless System (Dynamic Ticks)"
|
||||
|
@@ -71,6 +71,16 @@ void clockevents_set_mode(struct clock_event_device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clockevents_shutdown - shutdown the device and clear next_event
|
||||
* @dev: device to shutdown
|
||||
*/
|
||||
void clockevents_shutdown(struct clock_event_device *dev)
|
||||
{
|
||||
clockevents_set_mode(dev, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
dev->next_event.tv64 = KTIME_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* clockevents_program_event - Reprogram the clock event device.
|
||||
* @expires: absolute expiry time (monotonic clock)
|
||||
@@ -177,7 +187,7 @@ void clockevents_register_device(struct clock_event_device *dev)
|
||||
/*
|
||||
* Noop handler when we shut down an event device
|
||||
*/
|
||||
static void clockevents_handle_noop(struct clock_event_device *dev)
|
||||
void clockevents_handle_noop(struct clock_event_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -199,7 +209,6 @@ void clockevents_exchange_device(struct clock_event_device *old,
|
||||
* released list and do a notify add later.
|
||||
*/
|
||||
if (old) {
|
||||
old->event_handler = clockevents_handle_noop;
|
||||
clockevents_set_mode(old, CLOCK_EVT_MODE_UNUSED);
|
||||
list_del(&old->list);
|
||||
list_add(&old->list, &clockevents_released);
|
||||
@@ -207,7 +216,7 @@ void clockevents_exchange_device(struct clock_event_device *old,
|
||||
|
||||
if (new) {
|
||||
BUG_ON(new->mode != CLOCK_EVT_MODE_UNUSED);
|
||||
clockevents_set_mode(new, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
clockevents_shutdown(new);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
@@ -10,13 +10,13 @@
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/timex.h>
|
||||
|
||||
/*
|
||||
@@ -218,11 +218,11 @@ void second_overflow(void)
|
||||
/* Disable the cmos update - used by virtualization and embedded */
|
||||
int no_sync_cmos_clock __read_mostly;
|
||||
|
||||
static void sync_cmos_clock(unsigned long dummy);
|
||||
static void sync_cmos_clock(struct work_struct *work);
|
||||
|
||||
static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
|
||||
static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
|
||||
|
||||
static void sync_cmos_clock(unsigned long dummy)
|
||||
static void sync_cmos_clock(struct work_struct *work)
|
||||
{
|
||||
struct timespec now, next;
|
||||
int fail = 1;
|
||||
@@ -245,7 +245,7 @@ static void sync_cmos_clock(unsigned long dummy)
|
||||
if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2)
|
||||
fail = update_persistent_clock(now);
|
||||
|
||||
next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec;
|
||||
next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
|
||||
if (next.tv_nsec <= 0)
|
||||
next.tv_nsec += NSEC_PER_SEC;
|
||||
|
||||
@@ -258,13 +258,13 @@ static void sync_cmos_clock(unsigned long dummy)
|
||||
next.tv_sec++;
|
||||
next.tv_nsec -= NSEC_PER_SEC;
|
||||
}
|
||||
mod_timer(&sync_cmos_timer, jiffies + timespec_to_jiffies(&next));
|
||||
schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next));
|
||||
}
|
||||
|
||||
static void notify_cmos_timer(void)
|
||||
{
|
||||
if (!no_sync_cmos_clock)
|
||||
mod_timer(&sync_cmos_timer, jiffies + 1);
|
||||
schedule_delayed_work(&sync_cmos_work, 0);
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -277,38 +277,50 @@ static inline void notify_cmos_timer(void) { }
|
||||
int do_adjtimex(struct timex *txc)
|
||||
{
|
||||
struct timespec ts;
|
||||
long save_adjust, sec;
|
||||
int result;
|
||||
|
||||
/* In order to modify anything, you gotta be super-user! */
|
||||
if (txc->modes && !capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
||||
/* Now we validate the data before disabling interrupts */
|
||||
|
||||
if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) {
|
||||
/* Validate the data before disabling interrupts */
|
||||
if (txc->modes & ADJ_ADJTIME) {
|
||||
/* singleshot must not be used with any other mode bits */
|
||||
if (txc->modes & ~ADJ_OFFSET_SS_READ)
|
||||
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
|
||||
return -EINVAL;
|
||||
if (!(txc->modes & ADJ_OFFSET_READONLY) &&
|
||||
!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
} else {
|
||||
/* In order to modify anything, you gotta be super-user! */
|
||||
if (txc->modes && !capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
||||
/* if the quartz is off by more than 10% something is VERY wrong! */
|
||||
if (txc->modes & ADJ_TICK &&
|
||||
(txc->tick < 900000/USER_HZ ||
|
||||
txc->tick > 1100000/USER_HZ))
|
||||
return -EINVAL;
|
||||
|
||||
if (txc->modes & ADJ_STATUS && time_state != TIME_OK)
|
||||
hrtimer_cancel(&leap_timer);
|
||||
}
|
||||
|
||||
/* if the quartz is off by more than 10% something is VERY wrong ! */
|
||||
if (txc->modes & ADJ_TICK)
|
||||
if (txc->tick < 900000/USER_HZ ||
|
||||
txc->tick > 1100000/USER_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
if (time_state != TIME_OK && txc->modes & ADJ_STATUS)
|
||||
hrtimer_cancel(&leap_timer);
|
||||
getnstimeofday(&ts);
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
|
||||
/* Save for later - semantics of adjtime is to return old value */
|
||||
save_adjust = time_adjust;
|
||||
|
||||
/* If there are input parameters, then process them */
|
||||
if (txc->modes & ADJ_ADJTIME) {
|
||||
long save_adjust = time_adjust;
|
||||
|
||||
if (!(txc->modes & ADJ_OFFSET_READONLY)) {
|
||||
/* adjtime() is independent from ntp_adjtime() */
|
||||
time_adjust = txc->offset;
|
||||
ntp_update_frequency();
|
||||
}
|
||||
txc->offset = save_adjust;
|
||||
goto adj_done;
|
||||
}
|
||||
if (txc->modes) {
|
||||
long sec;
|
||||
|
||||
if (txc->modes & ADJ_STATUS) {
|
||||
if ((time_status & STA_PLL) &&
|
||||
!(txc->status & STA_PLL)) {
|
||||
@@ -375,13 +387,8 @@ int do_adjtimex(struct timex *txc)
|
||||
if (txc->modes & ADJ_TAI && txc->constant > 0)
|
||||
time_tai = txc->constant;
|
||||
|
||||
if (txc->modes & ADJ_OFFSET) {
|
||||
if (txc->modes == ADJ_OFFSET_SINGLESHOT)
|
||||
/* adjtime() is independent from ntp_adjtime() */
|
||||
time_adjust = txc->offset;
|
||||
else
|
||||
ntp_update_offset(txc->offset);
|
||||
}
|
||||
if (txc->modes & ADJ_OFFSET)
|
||||
ntp_update_offset(txc->offset);
|
||||
if (txc->modes & ADJ_TICK)
|
||||
tick_usec = txc->tick;
|
||||
|
||||
@@ -389,22 +396,18 @@ int do_adjtimex(struct timex *txc)
|
||||
ntp_update_frequency();
|
||||
}
|
||||
|
||||
txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,
|
||||
NTP_SCALE_SHIFT);
|
||||
if (!(time_status & STA_NANO))
|
||||
txc->offset /= NSEC_PER_USEC;
|
||||
|
||||
adj_done:
|
||||
result = time_state; /* mostly `TIME_OK' */
|
||||
if (time_status & (STA_UNSYNC|STA_CLOCKERR))
|
||||
result = TIME_ERROR;
|
||||
|
||||
if ((txc->modes == ADJ_OFFSET_SINGLESHOT) ||
|
||||
(txc->modes == ADJ_OFFSET_SS_READ))
|
||||
txc->offset = save_adjust;
|
||||
else {
|
||||
txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ,
|
||||
NTP_SCALE_SHIFT);
|
||||
if (!(time_status & STA_NANO))
|
||||
txc->offset /= NSEC_PER_USEC;
|
||||
}
|
||||
txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) *
|
||||
(s64)PPM_SCALE_INV,
|
||||
NTP_SCALE_SHIFT);
|
||||
txc->freq = shift_right((time_freq >> PPM_SCALE_INV_SHIFT) *
|
||||
(s64)PPM_SCALE_INV, NTP_SCALE_SHIFT);
|
||||
txc->maxerror = time_maxerror;
|
||||
txc->esterror = time_esterror;
|
||||
txc->status = time_status;
|
||||
|
@@ -175,6 +175,8 @@ static void tick_do_periodic_broadcast(void)
|
||||
*/
|
||||
static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
|
||||
{
|
||||
ktime_t next;
|
||||
|
||||
tick_do_periodic_broadcast();
|
||||
|
||||
/*
|
||||
@@ -185,10 +187,13 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
|
||||
|
||||
/*
|
||||
* Setup the next period for devices, which do not have
|
||||
* periodic mode:
|
||||
* periodic mode. We read dev->next_event first and add to it
|
||||
* when the event alrady expired. clockevents_program_event()
|
||||
* sets dev->next_event only when the event is really
|
||||
* programmed to the device.
|
||||
*/
|
||||
for (;;) {
|
||||
ktime_t next = ktime_add(dev->next_event, tick_period);
|
||||
for (next = dev->next_event; ;) {
|
||||
next = ktime_add(next, tick_period);
|
||||
|
||||
if (!clockevents_program_event(dev, next, ktime_get()))
|
||||
return;
|
||||
@@ -205,7 +210,7 @@ static void tick_do_broadcast_on_off(void *why)
|
||||
struct clock_event_device *bc, *dev;
|
||||
struct tick_device *td;
|
||||
unsigned long flags, *reason = why;
|
||||
int cpu;
|
||||
int cpu, bc_stopped;
|
||||
|
||||
spin_lock_irqsave(&tick_broadcast_lock, flags);
|
||||
|
||||
@@ -223,14 +228,16 @@ static void tick_do_broadcast_on_off(void *why)
|
||||
if (!tick_device_is_functional(dev))
|
||||
goto out;
|
||||
|
||||
bc_stopped = cpus_empty(tick_broadcast_mask);
|
||||
|
||||
switch (*reason) {
|
||||
case CLOCK_EVT_NOTIFY_BROADCAST_ON:
|
||||
case CLOCK_EVT_NOTIFY_BROADCAST_FORCE:
|
||||
if (!cpu_isset(cpu, tick_broadcast_mask)) {
|
||||
cpu_set(cpu, tick_broadcast_mask);
|
||||
if (td->mode == TICKDEV_MODE_PERIODIC)
|
||||
clockevents_set_mode(dev,
|
||||
CLOCK_EVT_MODE_SHUTDOWN);
|
||||
if (tick_broadcast_device.mode ==
|
||||
TICKDEV_MODE_PERIODIC)
|
||||
clockevents_shutdown(dev);
|
||||
}
|
||||
if (*reason == CLOCK_EVT_NOTIFY_BROADCAST_FORCE)
|
||||
tick_broadcast_force = 1;
|
||||
@@ -239,15 +246,17 @@ static void tick_do_broadcast_on_off(void *why)
|
||||
if (!tick_broadcast_force &&
|
||||
cpu_isset(cpu, tick_broadcast_mask)) {
|
||||
cpu_clear(cpu, tick_broadcast_mask);
|
||||
if (td->mode == TICKDEV_MODE_PERIODIC)
|
||||
if (tick_broadcast_device.mode ==
|
||||
TICKDEV_MODE_PERIODIC)
|
||||
tick_setup_periodic(dev, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpus_empty(tick_broadcast_mask))
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
else {
|
||||
if (cpus_empty(tick_broadcast_mask)) {
|
||||
if (!bc_stopped)
|
||||
clockevents_shutdown(bc);
|
||||
} else if (bc_stopped) {
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC)
|
||||
tick_broadcast_start_periodic(bc);
|
||||
else
|
||||
@@ -298,7 +307,7 @@ void tick_shutdown_broadcast(unsigned int *cpup)
|
||||
|
||||
if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) {
|
||||
if (bc && cpus_empty(tick_broadcast_mask))
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
clockevents_shutdown(bc);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tick_broadcast_lock, flags);
|
||||
@@ -313,7 +322,7 @@ void tick_suspend_broadcast(void)
|
||||
|
||||
bc = tick_broadcast_device.evtdev;
|
||||
if (bc)
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
clockevents_shutdown(bc);
|
||||
|
||||
spin_unlock_irqrestore(&tick_broadcast_lock, flags);
|
||||
}
|
||||
@@ -364,16 +373,8 @@ cpumask_t *tick_get_broadcast_oneshot_mask(void)
|
||||
static int tick_broadcast_set_event(ktime_t expires, int force)
|
||||
{
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
ktime_t now = ktime_get();
|
||||
int res;
|
||||
|
||||
for(;;) {
|
||||
res = clockevents_program_event(bc, expires, now);
|
||||
if (!res || !force)
|
||||
return res;
|
||||
now = ktime_get();
|
||||
expires = ktime_add(now, ktime_set(0, bc->min_delta_ns));
|
||||
}
|
||||
return tick_dev_program_event(bc, expires, force);
|
||||
}
|
||||
|
||||
int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
|
||||
@@ -382,6 +383,19 @@ int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from irq_enter() when idle was interrupted to reenable the
|
||||
* per cpu device.
|
||||
*/
|
||||
void tick_check_oneshot_broadcast(int cpu)
|
||||
{
|
||||
if (cpu_isset(cpu, tick_broadcast_oneshot_mask)) {
|
||||
struct tick_device *td = &per_cpu(tick_cpu_device, cpu);
|
||||
|
||||
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle oneshot mode broadcasting
|
||||
*/
|
||||
@@ -491,14 +505,52 @@ static void tick_broadcast_clear_oneshot(int cpu)
|
||||
cpu_clear(cpu, tick_broadcast_oneshot_mask);
|
||||
}
|
||||
|
||||
static void tick_broadcast_init_next_event(cpumask_t *mask, ktime_t expires)
|
||||
{
|
||||
struct tick_device *td;
|
||||
int cpu;
|
||||
|
||||
for_each_cpu_mask_nr(cpu, *mask) {
|
||||
td = &per_cpu(tick_cpu_device, cpu);
|
||||
if (td->evtdev)
|
||||
td->evtdev->next_event = expires;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_broadcast_setup_oneshot - setup the broadcast device
|
||||
*/
|
||||
void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
|
||||
{
|
||||
bc->event_handler = tick_handle_oneshot_broadcast;
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
|
||||
bc->next_event.tv64 = KTIME_MAX;
|
||||
/* Set it up only once ! */
|
||||
if (bc->event_handler != tick_handle_oneshot_broadcast) {
|
||||
int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC;
|
||||
int cpu = smp_processor_id();
|
||||
cpumask_t mask;
|
||||
|
||||
bc->event_handler = tick_handle_oneshot_broadcast;
|
||||
clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT);
|
||||
|
||||
/* Take the do_timer update */
|
||||
tick_do_timer_cpu = cpu;
|
||||
|
||||
/*
|
||||
* We must be careful here. There might be other CPUs
|
||||
* waiting for periodic broadcast. We need to set the
|
||||
* oneshot_mask bits for those and program the
|
||||
* broadcast device to fire.
|
||||
*/
|
||||
mask = tick_broadcast_mask;
|
||||
cpu_clear(cpu, mask);
|
||||
cpus_or(tick_broadcast_oneshot_mask,
|
||||
tick_broadcast_oneshot_mask, mask);
|
||||
|
||||
if (was_periodic && !cpus_empty(mask)) {
|
||||
tick_broadcast_init_next_event(&mask, tick_next_period);
|
||||
tick_broadcast_set_event(tick_next_period, 1);
|
||||
} else
|
||||
bc->next_event.tv64 = KTIME_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -538,4 +590,12 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup)
|
||||
spin_unlock_irqrestore(&tick_broadcast_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check, whether the broadcast device is in one shot mode
|
||||
*/
|
||||
int tick_broadcast_oneshot_active(void)
|
||||
{
|
||||
return tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -33,7 +33,7 @@ DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
|
||||
*/
|
||||
ktime_t tick_next_period;
|
||||
ktime_t tick_period;
|
||||
int tick_do_timer_cpu __read_mostly = -1;
|
||||
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;
|
||||
DEFINE_SPINLOCK(tick_device_lock);
|
||||
|
||||
/*
|
||||
@@ -109,7 +109,8 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
|
||||
if (!tick_device_is_functional(dev))
|
||||
return;
|
||||
|
||||
if (dev->features & CLOCK_EVT_FEAT_PERIODIC) {
|
||||
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
|
||||
!tick_broadcast_oneshot_active()) {
|
||||
clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
|
||||
} else {
|
||||
unsigned long seq;
|
||||
@@ -148,7 +149,7 @@ static void tick_setup_device(struct tick_device *td,
|
||||
* If no cpu took the do_timer update, assign it to
|
||||
* this cpu:
|
||||
*/
|
||||
if (tick_do_timer_cpu == -1) {
|
||||
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
|
||||
tick_do_timer_cpu = cpu;
|
||||
tick_next_period = ktime_get();
|
||||
tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
|
||||
@@ -161,6 +162,7 @@ static void tick_setup_device(struct tick_device *td,
|
||||
} else {
|
||||
handler = td->evtdev->event_handler;
|
||||
next_event = td->evtdev->next_event;
|
||||
td->evtdev->event_handler = clockevents_handle_noop;
|
||||
}
|
||||
|
||||
td->evtdev = newdev;
|
||||
@@ -248,7 +250,7 @@ static int tick_check_new_device(struct clock_event_device *newdev)
|
||||
* not give it back to the clockevents layer !
|
||||
*/
|
||||
if (tick_is_broadcast_device(curdev)) {
|
||||
clockevents_set_mode(curdev, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
clockevents_shutdown(curdev);
|
||||
curdev = NULL;
|
||||
}
|
||||
clockevents_exchange_device(curdev, newdev);
|
||||
@@ -299,7 +301,8 @@ static void tick_shutdown(unsigned int *cpup)
|
||||
if (*cpup == tick_do_timer_cpu) {
|
||||
int cpu = first_cpu(cpu_online_map);
|
||||
|
||||
tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu : -1;
|
||||
tick_do_timer_cpu = (cpu != NR_CPUS) ? cpu :
|
||||
TICK_DO_TIMER_NONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&tick_device_lock, flags);
|
||||
}
|
||||
@@ -310,7 +313,7 @@ static void tick_suspend(void)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tick_device_lock, flags);
|
||||
clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_SHUTDOWN);
|
||||
clockevents_shutdown(td->evtdev);
|
||||
spin_unlock_irqrestore(&tick_device_lock, flags);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,10 @@
|
||||
/*
|
||||
* tick internal variable and functions used by low/high res code
|
||||
*/
|
||||
|
||||
#define TICK_DO_TIMER_NONE -1
|
||||
#define TICK_DO_TIMER_BOOT -2
|
||||
|
||||
DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
|
||||
extern spinlock_t tick_device_lock;
|
||||
extern ktime_t tick_next_period;
|
||||
@@ -10,6 +14,8 @@ extern int tick_do_timer_cpu __read_mostly;
|
||||
extern void tick_setup_periodic(struct clock_event_device *dev, int broadcast);
|
||||
extern void tick_handle_periodic(struct clock_event_device *dev);
|
||||
|
||||
extern void clockevents_shutdown(struct clock_event_device *dev);
|
||||
|
||||
/*
|
||||
* NO_HZ / high resolution timer shared code
|
||||
*/
|
||||
@@ -17,6 +23,8 @@ extern void tick_handle_periodic(struct clock_event_device *dev);
|
||||
extern void tick_setup_oneshot(struct clock_event_device *newdev,
|
||||
void (*handler)(struct clock_event_device *),
|
||||
ktime_t nextevt);
|
||||
extern int tick_dev_program_event(struct clock_event_device *dev,
|
||||
ktime_t expires, int force);
|
||||
extern int tick_program_event(ktime_t expires, int force);
|
||||
extern void tick_oneshot_notify(void);
|
||||
extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));
|
||||
@@ -27,6 +35,8 @@ extern void tick_broadcast_oneshot_control(unsigned long reason);
|
||||
extern void tick_broadcast_switch_to_oneshot(void);
|
||||
extern void tick_shutdown_broadcast_oneshot(unsigned int *cpup);
|
||||
extern int tick_resume_broadcast_oneshot(struct clock_event_device *bc);
|
||||
extern int tick_broadcast_oneshot_active(void);
|
||||
extern void tick_check_oneshot_broadcast(int cpu);
|
||||
# else /* BROADCAST */
|
||||
static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
|
||||
{
|
||||
@@ -35,6 +45,8 @@ static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
|
||||
static inline void tick_broadcast_oneshot_control(unsigned long reason) { }
|
||||
static inline void tick_broadcast_switch_to_oneshot(void) { }
|
||||
static inline void tick_shutdown_broadcast_oneshot(unsigned int *cpup) { }
|
||||
static inline int tick_broadcast_oneshot_active(void) { return 0; }
|
||||
static inline void tick_check_oneshot_broadcast(int cpu) { }
|
||||
# endif /* !BROADCAST */
|
||||
|
||||
#else /* !ONESHOT */
|
||||
@@ -64,6 +76,7 @@ static inline int tick_resume_broadcast_oneshot(struct clock_event_device *bc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int tick_broadcast_oneshot_active(void) { return 0; }
|
||||
#endif /* !TICK_ONESHOT */
|
||||
|
||||
/*
|
||||
|
@@ -22,22 +22,54 @@
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
/**
|
||||
* tick_program_event internal worker function
|
||||
*/
|
||||
int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
|
||||
int force)
|
||||
{
|
||||
ktime_t now = ktime_get();
|
||||
int i;
|
||||
|
||||
for (i = 0;;) {
|
||||
int ret = clockevents_program_event(dev, expires, now);
|
||||
|
||||
if (!ret || !force)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We tried 2 times to program the device with the given
|
||||
* min_delta_ns. If that's not working then we double it
|
||||
* and emit a warning.
|
||||
*/
|
||||
if (++i > 2) {
|
||||
/* Increase the min. delta and try again */
|
||||
if (!dev->min_delta_ns)
|
||||
dev->min_delta_ns = 5000;
|
||||
else
|
||||
dev->min_delta_ns += dev->min_delta_ns >> 1;
|
||||
|
||||
printk(KERN_WARNING
|
||||
"CE: %s increasing min_delta_ns to %lu nsec\n",
|
||||
dev->name ? dev->name : "?",
|
||||
dev->min_delta_ns << 1);
|
||||
|
||||
i = 0;
|
||||
}
|
||||
|
||||
now = ktime_get();
|
||||
expires = ktime_add_ns(now, dev->min_delta_ns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_program_event
|
||||
*/
|
||||
int tick_program_event(ktime_t expires, int force)
|
||||
{
|
||||
struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
while (1) {
|
||||
int ret = clockevents_program_event(dev, expires, now);
|
||||
|
||||
if (!ret || !force)
|
||||
return ret;
|
||||
now = ktime_get();
|
||||
expires = ktime_add(now, ktime_set(0, dev->min_delta_ns));
|
||||
}
|
||||
return tick_dev_program_event(dev, expires, force);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +93,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
|
||||
{
|
||||
newdev->event_handler = handler;
|
||||
clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
|
||||
clockevents_program_event(newdev, next_event, ktime_get());
|
||||
tick_dev_program_event(newdev, next_event, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include <linux/profile.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/irq_regs.h>
|
||||
|
||||
@@ -75,6 +76,9 @@ static void tick_do_update_jiffies64(ktime_t now)
|
||||
incr * ticks);
|
||||
}
|
||||
do_timer(++ticks);
|
||||
|
||||
/* Keep the tick_next_period variable up to date */
|
||||
tick_next_period = ktime_add(last_jiffies_update, tick_period);
|
||||
}
|
||||
write_sequnlock(&xtime_lock);
|
||||
}
|
||||
@@ -151,7 +155,7 @@ void tick_nohz_update_jiffies(void)
|
||||
touch_softlockup_watchdog();
|
||||
}
|
||||
|
||||
void tick_nohz_stop_idle(int cpu)
|
||||
static void tick_nohz_stop_idle(int cpu)
|
||||
{
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
|
||||
@@ -162,6 +166,8 @@ void tick_nohz_stop_idle(int cpu)
|
||||
ts->idle_lastupdate = now;
|
||||
ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
|
||||
ts->idle_active = 0;
|
||||
|
||||
sched_clock_idle_wakeup_event(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +183,7 @@ static ktime_t tick_nohz_start_idle(struct tick_sched *ts)
|
||||
}
|
||||
ts->idle_entrytime = now;
|
||||
ts->idle_active = 1;
|
||||
sched_clock_idle_sleep_event();
|
||||
return now;
|
||||
}
|
||||
|
||||
@@ -184,9 +191,17 @@ u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
|
||||
{
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
|
||||
*last_update_time = ktime_to_us(ts->idle_lastupdate);
|
||||
if (!tick_nohz_enabled)
|
||||
return -1;
|
||||
|
||||
if (ts->idle_active)
|
||||
*last_update_time = ktime_to_us(ts->idle_lastupdate);
|
||||
else
|
||||
*last_update_time = ktime_to_us(ktime_get());
|
||||
|
||||
return ktime_to_us(ts->idle_sleeptime);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
|
||||
|
||||
/**
|
||||
* tick_nohz_stop_sched_tick - stop the idle tick from the idle task
|
||||
@@ -218,7 +233,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
||||
*/
|
||||
if (unlikely(!cpu_online(cpu))) {
|
||||
if (cpu == tick_do_timer_cpu)
|
||||
tick_do_timer_cpu = -1;
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
}
|
||||
|
||||
if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
|
||||
@@ -255,7 +270,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
||||
next_jiffies = get_next_timer_interrupt(last_jiffies);
|
||||
delta_jiffies = next_jiffies - last_jiffies;
|
||||
|
||||
if (rcu_needs_cpu(cpu))
|
||||
if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu))
|
||||
delta_jiffies = 1;
|
||||
/*
|
||||
* Do not stop the tick, if we are only one off
|
||||
@@ -300,7 +315,7 @@ void tick_nohz_stop_sched_tick(int inidle)
|
||||
* invoked.
|
||||
*/
|
||||
if (cpu == tick_do_timer_cpu)
|
||||
tick_do_timer_cpu = -1;
|
||||
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
|
||||
|
||||
ts->idle_sleeps++;
|
||||
|
||||
@@ -362,6 +377,32 @@ ktime_t tick_nohz_get_sleep_length(void)
|
||||
return ts->sleep_length;
|
||||
}
|
||||
|
||||
static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
|
||||
{
|
||||
hrtimer_cancel(&ts->sched_timer);
|
||||
ts->sched_timer.expires = ts->idle_tick;
|
||||
|
||||
while (1) {
|
||||
/* Forward the time to expire in the future */
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start(&ts->sched_timer,
|
||||
ts->sched_timer.expires,
|
||||
HRTIMER_MODE_ABS);
|
||||
/* Check, if the timer was already in the past */
|
||||
if (hrtimer_active(&ts->sched_timer))
|
||||
break;
|
||||
} else {
|
||||
if (!tick_program_event(ts->sched_timer.expires, 0))
|
||||
break;
|
||||
}
|
||||
/* Update jiffies and reread time */
|
||||
tick_do_update_jiffies64(now);
|
||||
now = ktime_get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_nohz_restart_sched_tick - restart the idle tick from the idle task
|
||||
*
|
||||
@@ -415,28 +456,7 @@ void tick_nohz_restart_sched_tick(void)
|
||||
*/
|
||||
ts->tick_stopped = 0;
|
||||
ts->idle_exittime = now;
|
||||
hrtimer_cancel(&ts->sched_timer);
|
||||
ts->sched_timer.expires = ts->idle_tick;
|
||||
|
||||
while (1) {
|
||||
/* Forward the time to expire in the future */
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start(&ts->sched_timer,
|
||||
ts->sched_timer.expires,
|
||||
HRTIMER_MODE_ABS);
|
||||
/* Check, if the timer was already in the past */
|
||||
if (hrtimer_active(&ts->sched_timer))
|
||||
break;
|
||||
} else {
|
||||
if (!tick_program_event(ts->sched_timer.expires, 0))
|
||||
break;
|
||||
}
|
||||
/* Update jiffies and reread time */
|
||||
tick_do_update_jiffies64(now);
|
||||
now = ktime_get();
|
||||
}
|
||||
tick_nohz_restart(ts, now);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
@@ -465,7 +485,7 @@ static void tick_nohz_handler(struct clock_event_device *dev)
|
||||
* this duty, then the jiffies update is still serialized by
|
||||
* xtime_lock.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == -1))
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
tick_do_timer_cpu = cpu;
|
||||
|
||||
/* Check, if the jiffies need an update */
|
||||
@@ -488,10 +508,6 @@ static void tick_nohz_handler(struct clock_event_device *dev)
|
||||
update_process_times(user_mode(regs));
|
||||
profile_tick(CPU_PROFILING);
|
||||
|
||||
/* Do not restart, when we are in the idle loop */
|
||||
if (ts->tick_stopped)
|
||||
return;
|
||||
|
||||
while (tick_nohz_reprogram(ts, now)) {
|
||||
now = ktime_get();
|
||||
tick_do_update_jiffies64(now);
|
||||
@@ -537,12 +553,46 @@ static void tick_nohz_switch_to_nohz(void)
|
||||
smp_processor_id());
|
||||
}
|
||||
|
||||
/*
|
||||
* When NOHZ is enabled and the tick is stopped, we need to kick the
|
||||
* tick timer from irq_enter() so that the jiffies update is kept
|
||||
* alive during long running softirqs. That's ugly as hell, but
|
||||
* correctness is key even if we need to fix the offending softirq in
|
||||
* the first place.
|
||||
*
|
||||
* Note, this is different to tick_nohz_restart. We just kick the
|
||||
* timer and do not touch the other magic bits which need to be done
|
||||
* when idle is left.
|
||||
*/
|
||||
static void tick_nohz_kick_tick(int cpu)
|
||||
{
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
|
||||
if (!ts->tick_stopped)
|
||||
return;
|
||||
|
||||
tick_nohz_restart(ts, ktime_get());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void tick_nohz_switch_to_nohz(void) { }
|
||||
|
||||
#endif /* NO_HZ */
|
||||
|
||||
/*
|
||||
* Called from irq_enter to notify about the possible interruption of idle()
|
||||
*/
|
||||
void tick_check_idle(int cpu)
|
||||
{
|
||||
tick_check_oneshot_broadcast(cpu);
|
||||
#ifdef CONFIG_NO_HZ
|
||||
tick_nohz_stop_idle(cpu);
|
||||
tick_nohz_update_jiffies();
|
||||
tick_nohz_kick_tick(cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* High resolution timer specific code
|
||||
*/
|
||||
@@ -567,7 +617,7 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
|
||||
* this duty, then the jiffies update is still serialized by
|
||||
* xtime_lock.
|
||||
*/
|
||||
if (unlikely(tick_do_timer_cpu == -1))
|
||||
if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
|
||||
tick_do_timer_cpu = cpu;
|
||||
#endif
|
||||
|
||||
@@ -596,10 +646,6 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
|
||||
profile_tick(CPU_PROFILING);
|
||||
}
|
||||
|
||||
/* Do not restart, when we are in the idle loop */
|
||||
if (ts->tick_stopped)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
hrtimer_forward(timer, now, tick_period);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
@@ -619,7 +665,7 @@ void tick_setup_sched_timer(void)
|
||||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
ts->sched_timer.function = tick_sched_timer;
|
||||
ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
|
||||
ts->sched_timer.cb_mode = HRTIMER_CB_IRQSAFE_PERCPU;
|
||||
|
||||
/* Get the next period (per cpu) */
|
||||
ts->sched_timer.expires = tick_init_jiffy_update();
|
||||
@@ -643,17 +689,21 @@ void tick_setup_sched_timer(void)
|
||||
ts->nohz_mode = NOHZ_MODE_HIGHRES;
|
||||
#endif
|
||||
}
|
||||
#endif /* HIGH_RES_TIMERS */
|
||||
|
||||
#if defined CONFIG_NO_HZ || defined CONFIG_HIGH_RES_TIMERS
|
||||
void tick_cancel_sched_timer(int cpu)
|
||||
{
|
||||
struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
|
||||
|
||||
# ifdef CONFIG_HIGH_RES_TIMERS
|
||||
if (ts->sched_timer.base)
|
||||
hrtimer_cancel(&ts->sched_timer);
|
||||
# endif
|
||||
|
||||
ts->nohz_mode = NOHZ_MODE_INACTIVE;
|
||||
}
|
||||
#endif /* HIGH_RES_TIMERS */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Async notification about clocksource changes
|
||||
|
@@ -487,7 +487,7 @@ void update_wall_time(void)
|
||||
#else
|
||||
offset = clock->cycle_interval;
|
||||
#endif
|
||||
clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
|
||||
clock->xtime_nsec = (s64)xtime.tv_nsec << clock->shift;
|
||||
|
||||
/* normally this loop will run just once, however in the
|
||||
* case of lost or late ticks, it will accumulate correctly.
|
||||
@@ -518,9 +518,12 @@ void update_wall_time(void)
|
||||
/* correct the clock when NTP error is too big */
|
||||
clocksource_adjust(offset);
|
||||
|
||||
/* store full nanoseconds into xtime */
|
||||
xtime.tv_nsec = (s64)clock->xtime_nsec >> clock->shift;
|
||||
/* store full nanoseconds into xtime after rounding it up and
|
||||
* add the remainder to the error difference.
|
||||
*/
|
||||
xtime.tv_nsec = ((s64)clock->xtime_nsec >> clock->shift) + 1;
|
||||
clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift;
|
||||
clock->error += clock->xtime_nsec << (NTP_SCALE_SHIFT - clock->shift);
|
||||
|
||||
update_xtime_cache(cyc2ns(clock, offset));
|
||||
|
||||
|
@@ -47,13 +47,14 @@ static void print_name_offset(struct seq_file *m, void *sym)
|
||||
}
|
||||
|
||||
static void
|
||||
print_timer(struct seq_file *m, struct hrtimer *timer, int idx, u64 now)
|
||||
print_timer(struct seq_file *m, struct hrtimer *taddr, struct hrtimer *timer,
|
||||
int idx, u64 now)
|
||||
{
|
||||
#ifdef CONFIG_TIMER_STATS
|
||||
char tmp[TASK_COMM_LEN + 1];
|
||||
#endif
|
||||
SEQ_printf(m, " #%d: ", idx);
|
||||
print_name_offset(m, timer);
|
||||
print_name_offset(m, taddr);
|
||||
SEQ_printf(m, ", ");
|
||||
print_name_offset(m, timer->function);
|
||||
SEQ_printf(m, ", S:%02lx", timer->state);
|
||||
@@ -99,7 +100,7 @@ next_one:
|
||||
tmp = *timer;
|
||||
spin_unlock_irqrestore(&base->cpu_base->lock, flags);
|
||||
|
||||
print_timer(m, &tmp, i, now);
|
||||
print_timer(m, timer, &tmp, i, now);
|
||||
next++;
|
||||
goto next_one;
|
||||
}
|
||||
@@ -109,6 +110,7 @@ next_one:
|
||||
static void
|
||||
print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now)
|
||||
{
|
||||
SEQ_printf(m, " .base: %p\n", base);
|
||||
SEQ_printf(m, " .index: %d\n",
|
||||
base->index);
|
||||
SEQ_printf(m, " .resolution: %Lu nsecs\n",
|
||||
@@ -183,12 +185,16 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
|
||||
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS
|
||||
static void
|
||||
print_tickdevice(struct seq_file *m, struct tick_device *td)
|
||||
print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
|
||||
{
|
||||
struct clock_event_device *dev = td->evtdev;
|
||||
|
||||
SEQ_printf(m, "\n");
|
||||
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
|
||||
if (cpu < 0)
|
||||
SEQ_printf(m, "Broadcast device\n");
|
||||
else
|
||||
SEQ_printf(m, "Per CPU device: %d\n", cpu);
|
||||
|
||||
SEQ_printf(m, "Clock Event Device: ");
|
||||
if (!dev) {
|
||||
@@ -222,7 +228,7 @@ static void timer_list_show_tickdevices(struct seq_file *m)
|
||||
int cpu;
|
||||
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
|
||||
print_tickdevice(m, tick_get_broadcast_device());
|
||||
print_tickdevice(m, tick_get_broadcast_device(), -1);
|
||||
SEQ_printf(m, "tick_broadcast_mask: %08lx\n",
|
||||
tick_get_broadcast_mask()->bits[0]);
|
||||
#ifdef CONFIG_TICK_ONESHOT
|
||||
@@ -232,7 +238,7 @@ static void timer_list_show_tickdevices(struct seq_file *m)
|
||||
SEQ_printf(m, "\n");
|
||||
#endif
|
||||
for_each_online_cpu(cpu)
|
||||
print_tickdevice(m, tick_get_device(cpu));
|
||||
print_tickdevice(m, tick_get_device(cpu), cpu);
|
||||
SEQ_printf(m, "\n");
|
||||
}
|
||||
#else
|
||||
@@ -244,7 +250,7 @@ static int timer_list_show(struct seq_file *m, void *v)
|
||||
u64 now = ktime_to_ns(ktime_get());
|
||||
int cpu;
|
||||
|
||||
SEQ_printf(m, "Timer List Version: v0.3\n");
|
||||
SEQ_printf(m, "Timer List Version: v0.4\n");
|
||||
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
|
||||
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
|
||||
|
||||
|
Reference in New Issue
Block a user