Release 4.15 kernel/time/tick-common.c
/*
* linux/kernel/time/tick-common.c
*
* This file contains the base functions to manage periodic tick
* related events.
*
* Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de>
* Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
* Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
*
* This code is licenced under the GPL version 2. For details see
* kernel-base/COPYING.
*/
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <trace/events/power.h>
#include <asm/irq_regs.h>
#include "tick-internal.h"
/*
* Tick devices
*/
DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
/*
* Tick next event: keeps track of the tick time
*/
ktime_t tick_next_period;
ktime_t tick_period;
/*
* tick_do_timer_cpu is a timer core internal variable which holds the CPU NR
* which is responsible for calling do_timer(), i.e. the timekeeping stuff. This
* variable has two functions:
*
* 1) Prevent a thundering herd issue of a gazillion of CPUs trying to grab the
* timekeeping lock all at once. Only the CPU which is assigned to do the
* update is handling it.
*
* 2) Hand off the duty in the NOHZ idle case by setting the value to
* TICK_DO_TIMER_NONE, i.e. a non existing CPU. So the next cpu which looks
* at it will take over and keep the time keeping alive. The handover
* procedure also covers cpu hotplug.
*/
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;
/*
* Debugging: see timer_list.c
*/
struct tick_device *tick_get_device(int cpu)
{
return &per_cpu(tick_cpu_device, cpu);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Ingo Molnar | 19 | 100.00% | 1 | 100.00% |
Total | 19 | 100.00% | 1 | 100.00% |
/**
* tick_is_oneshot_available - check for a oneshot capable event device
*/
int tick_is_oneshot_available(void)
{
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return 0;
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
return 1;
return tick_broadcast_oneshot_available();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 52 | 96.30% | 2 | 66.67% |
Christoph Lameter | 2 | 3.70% | 1 | 33.33% |
Total | 54 | 100.00% | 3 | 100.00% |
/*
* Periodic tick
*/
static void tick_periodic(int cpu)
{
if (tick_do_timer_cpu == cpu) {
write_seqlock(&jiffies_lock);
/* Keep track of the next tick event */
tick_next_period = ktime_add(tick_next_period, tick_period);
do_timer(1);
write_sequnlock(&jiffies_lock);
update_wall_time();
}
update_process_times(user_mode(get_irq_regs()));
profile_tick(CPU_PROFILING);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 56 | 91.80% | 1 | 33.33% |
John Stultz | 5 | 8.20% | 2 | 66.67% |
Total | 61 | 100.00% | 3 | 100.00% |
/*
* Event handler for periodic ticks
*/
void tick_handle_periodic(struct clock_event_device *dev)
{
int cpu = smp_processor_id();
ktime_t next = dev->next_event;
tick_periodic(cpu);
#if defined(CONFIG_HIGH_RES_TIMERS) || defined(CONFIG_NO_HZ_COMMON)
/*
* The cpu might have transitioned to HIGHRES or NOHZ mode via
* update_process_times() -> run_local_timers() ->
* hrtimer_run_queues().
*/
if (dev->event_handler != tick_handle_periodic)
return;
#endif
if (!clockevent_state_oneshot(dev))
return;
for (;;) {
/*
* Setup the next period for devices, which do not have
* periodic mode:
*/
next = ktime_add(next, tick_period);
if (!clockevents_program_event(dev, next, false))
return;
/*
* Have to be careful here. If we're in oneshot mode,
* before we call tick_periodic() in a loop, we need
* to be sure we're using a real hardware clocksource.
* Otherwise we could get trapped in an infinite
* loop, as the tick_periodic() increments jiffies,
* which then will increment time, possibly causing
* the loop to trigger again and again.
*/
if (timekeeping_valid_for_hres())
tick_periodic(cpu);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 73 | 72.28% | 2 | 25.00% |
Viresh Kumar | 17 | 16.83% | 3 | 37.50% |
John Stultz | 5 | 4.95% | 1 | 12.50% |
David S. Miller | 5 | 4.95% | 1 | 12.50% |
Martin Schwidefsky | 1 | 0.99% | 1 | 12.50% |
Total | 101 | 100.00% | 8 | 100.00% |
/*
* Setup the device for a periodic tick
*/
void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
tick_set_periodic_handler(dev, broadcast);
/* Broadcast setup ? */
if (!tick_device_is_functional(dev))
return;
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
!tick_broadcast_oneshot_active()) {
clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
} else {
unsigned long seq;
ktime_t next;
do {
seq = read_seqbegin(&jiffies_lock);
next = tick_next_period;
} while (read_seqretry(&jiffies_lock, seq));
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
for (;;) {
if (!clockevents_program_event(dev, next, false))
return;
next = ktime_add(next, tick_period);
}
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 120 | 96.00% | 4 | 57.14% |
John Stultz | 2 | 1.60% | 1 | 14.29% |
Viresh Kumar | 2 | 1.60% | 1 | 14.29% |
Martin Schwidefsky | 1 | 0.80% | 1 | 14.29% |
Total | 125 | 100.00% | 7 | 100.00% |
/*
* Setup the tick device
*/
static void tick_setup_device(struct tick_device *td,
struct clock_event_device *newdev, int cpu,
const struct cpumask *cpumask)
{
void (*handler)(struct clock_event_device *) = NULL;
ktime_t next_event = 0;
/*
* First device setup ?
*/
if (!td->evtdev) {
/*
* If no cpu took the do_timer update, assign it to
* this cpu:
*/
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
if (!tick_nohz_full_cpu(cpu))
tick_do_timer_cpu = cpu;
else
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
tick_next_period = ktime_get();
tick_period = NSEC_PER_SEC / HZ;
}
/*
* Startup in periodic mode first.
*/
td->mode = TICKDEV_MODE_PERIODIC;
} else {
handler = td->evtdev->event_handler;
next_event = td->evtdev->next_event;
td->evtdev->event_handler = clockevents_handle_noop;
}
td->evtdev = newdev;
/*
* When the device is not per cpu, pin the interrupt to the
* current cpu:
*/
if (!cpumask_equal(newdev->cpumask, cpumask))
irq_set_affinity(newdev->irq, cpumask);
/*
* When global broadcasting is active, check if the current
* device is registered as a placeholder for broadcast mode.
* This allows us to handle this x86 misfeature in a generic
* way. This function also returns !=0 when we keep the
* current active broadcast state for this CPU.
*/
if (tick_device_uses_broadcast(newdev, cpu))
return;
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(newdev, 0);
else
tick_setup_oneshot(newdev, handler, next_event);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 162 | 86.17% | 6 | 54.55% |
Frédéric Weisbecker | 13 | 6.91% | 2 | 18.18% |
Venkatesh Pallipadi | 8 | 4.26% | 1 | 9.09% |
Rusty Russell | 3 | 1.60% | 1 | 9.09% |
Mike Travis | 2 | 1.06% | 1 | 9.09% |
Total | 188 | 100.00% | 11 | 100.00% |
void tick_install_replacement(struct clock_event_device *newdev)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
int cpu = smp_processor_id();
clockevents_exchange_device(td->evtdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 59 | 96.72% | 1 | 50.00% |
Christoph Lameter | 2 | 3.28% | 1 | 50.00% |
Total | 61 | 100.00% | 2 | 100.00% |
static bool tick_check_percpu(struct clock_event_device *curdev,
struct clock_event_device *newdev, int cpu)
{
if (!cpumask_test_cpu(cpu, newdev->cpumask))
return false;
if (cpumask_equal(newdev->cpumask, cpumask_of(cpu)))
return true;
/* Check if irq affinity can be set */
if (newdev->irq >= 0 && !irq_can_set_affinity(newdev->irq))
return false;
/* Prefer an existing cpu local device */
if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))
return false;
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 83 | 88.30% | 2 | 50.00% |
Mike Travis | 6 | 6.38% | 1 | 25.00% |
Rusty Russell | 5 | 5.32% | 1 | 25.00% |
Total | 94 | 100.00% | 4 | 100.00% |
static bool tick_check_preferred(struct clock_event_device *curdev,
struct clock_event_device *newdev)
{
/* Prefer oneshot capable device */
if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) {
if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT))
return false;
if (tick_oneshot_mode_active())
return false;
}
/*
* Use the higher rated one, but prefer a CPU local device with a lower
* rating than a non-CPU local device
*/
return !curdev ||
newdev->rating > curdev->rating ||
!cpumask_equal(curdev->cpumask, newdev->cpumask);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 65 | 83.33% | 3 | 75.00% |
Stephen Boyd | 13 | 16.67% | 1 | 25.00% |
Total | 78 | 100.00% | 4 | 100.00% |
/*
* Check whether the new device is a better fit than curdev. curdev
* can be NULL !
*/
bool tick_check_replacement(struct clock_event_device *curdev,
struct clock_event_device *newdev)
{
if (!tick_check_percpu(curdev, newdev, smp_processor_id()))
return false;
return tick_check_preferred(curdev, newdev);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 38 | 97.44% | 1 | 50.00% |
Viresh Kumar | 1 | 2.56% | 1 | 50.00% |
Total | 39 | 100.00% | 2 | 100.00% |
/*
* Check, if the new registered device should be used. Called with
* clockevents_lock held and interrupts disabled.
*/
void tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu;
cpu = smp_processor_id();
td = &per_cpu(tick_cpu_device, cpu);
curdev = td->evtdev;
/* cpu local device ? */
if (!tick_check_percpu(curdev, newdev, cpu))
goto out_bc;
/* Preference decision */
if (!tick_check_preferred(curdev, newdev))
goto out_bc;
if (!try_module_get(newdev->owner))
return;
/*
* Replace the eventually existing device by the new
* device. If the current device is the broadcast device, do
* not give it back to the clockevents layer !
*/
if (tick_is_broadcast_device(curdev)) {
clockevents_shutdown(curdev);
curdev = NULL;
}
clockevents_exchange_device(curdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
return;
out_bc:
/*
* Can the new device be used as a broadcast device ?
*/
tick_install_broadcast_device(newdev);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 141 | 97.24% | 7 | 77.78% |
Mike Travis | 3 | 2.07% | 1 | 11.11% |
Rusty Russell | 1 | 0.69% | 1 | 11.11% |
Total | 145 | 100.00% | 9 | 100.00% |
/**
* tick_broadcast_oneshot_control - Enter/exit broadcast oneshot mode
* @state: The target state (enter/exit)
*
* The system enters/leaves a state, where affected devices might stop
* Returns 0 on success, -EBUSY if the cpu is used to broadcast wakeups.
*
* Called with interrupts disabled, so clockevents_lock is not
* required here because the local clock event device cannot go away
* under us.
*/
int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
if (!(td->evtdev->features & CLOCK_EVT_FEAT_C3STOP))
return 0;
return __tick_broadcast_oneshot_control(state);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 42 | 100.00% | 1 | 100.00% |
Total | 42 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL_GPL(tick_broadcast_oneshot_control);
#ifdef CONFIG_HOTPLUG_CPU
/*
* Transfer the do_timer job away from a dying cpu.
*
* Called with interrupts disabled. Not locking required. If
* tick_do_timer_cpu is owned by this cpu, nothing can change it.
*/
void tick_handover_do_timer(void)
{
if (tick_do_timer_cpu == smp_processor_id()) {
int cpu = cpumask_first(cpu_online_mask);
tick_do_timer_cpu = (cpu < nr_cpu_ids) ? cpu :
TICK_DO_TIMER_NONE;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sébastien Dugué | 32 | 88.89% | 1 | 50.00% |
Thomas Gleixner | 4 | 11.11% | 1 | 50.00% |
Total | 36 | 100.00% | 2 | 100.00% |
/*
* Shutdown an event device on a given cpu:
*
* This is called on a life CPU, when a CPU is dead. So we cannot
* access the hardware device itself.
* We just set the mode and remove it from the lists.
*/
void tick_shutdown(unsigned int cpu)
{
struct tick_device *td = &per_cpu(tick_cpu_device, cpu);
struct clock_event_device *dev = td->evtdev;
td->mode = TICKDEV_MODE_PERIODIC;
if (dev) {
/*
* Prevent that the clock events layer tries to call
* the set mode function!
*/
clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
clockevents_exchange_device(dev, NULL);
dev->event_handler = clockevents_handle_noop;
td->evtdev = NULL;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 68 | 97.14% | 4 | 80.00% |
Viresh Kumar | 2 | 2.86% | 1 | 20.00% |
Total | 70 | 100.00% | 5 | 100.00% |
#endif
/**
* tick_suspend_local - Suspend the local tick device
*
* Called from the local cpu for freeze with interrupts disabled.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_suspend_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
clockevents_shutdown(td->evtdev);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 23 | 92.00% | 3 | 75.00% |
Christoph Lameter | 2 | 8.00% | 1 | 25.00% |
Total | 25 | 100.00% | 4 | 100.00% |
/**
* tick_resume_local - Resume the local tick device
*
* Called from the local CPU for unfreeze or XEN resume magic.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_resume_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
bool broadcast = tick_resume_check_broadcast();
clockevents_tick_resume(td->evtdev);
if (!broadcast) {
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(td->evtdev, 0);
else
tick_resume_oneshot();
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 58 | 98.31% | 5 | 83.33% |
Viresh Kumar | 1 | 1.69% | 1 | 16.67% |
Total | 59 | 100.00% | 6 | 100.00% |
/**
* tick_suspend - Suspend the tick and the broadcast device
*
* Called from syscore_suspend() via timekeeping_suspend with only one
* CPU online and interrupts disabled or from tick_unfreeze() under
* tick_freeze_lock.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_suspend(void)
{
tick_suspend_local();
tick_suspend_broadcast();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 13 | 100.00% | 1 | 100.00% |
Total | 13 | 100.00% | 1 | 100.00% |
/**
* tick_resume - Resume the tick and the broadcast device
*
* Called from syscore_resume() via timekeeping_resume with only one
* CPU online and interrupts disabled.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_resume(void)
{
tick_resume_broadcast();
tick_resume_local();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 13 | 100.00% | 1 | 100.00% |
Total | 13 | 100.00% | 1 | 100.00% |
#ifdef CONFIG_SUSPEND
static DEFINE_RAW_SPINLOCK(tick_freeze_lock);
static unsigned int tick_freeze_depth;
/**
* tick_freeze - Suspend the local tick and (possibly) timekeeping.
*
* Check if this is the last online CPU executing the function and if so,
* suspend timekeeping. Otherwise suspend the local tick.
*
* Call with interrupts disabled. Must be balanced with %tick_unfreeze().
* Interrupts must not be enabled before the subsequent %tick_unfreeze().
*/
void tick_freeze(void)
{
raw_spin_lock(&tick_freeze_lock);
tick_freeze_depth++;
if (tick_freeze_depth == num_online_cpus()) {
trace_suspend_resume(TPS("timekeeping_freeze"),
smp_processor_id(), true);
timekeeping_suspend();
} else {
tick_suspend_local();
}
raw_spin_unlock(&tick_freeze_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Rafael J. Wysocki | 52 | 98.11% | 2 | 66.67% |
Thomas Gleixner | 1 | 1.89% | 1 | 33.33% |
Total | 53 | 100.00% | 3 | 100.00% |
/**
* tick_unfreeze - Resume the local tick and (possibly) timekeeping.
*
* Check if this is the first CPU executing the function and if so, resume
* timekeeping. Otherwise resume the local tick.
*
* Call with interrupts disabled. Must be balanced with %tick_freeze().
* Interrupts must not be enabled after the preceding %tick_freeze().
*/
void tick_unfreeze(void)
{
raw_spin_lock(&tick_freeze_lock);
if (tick_freeze_depth == num_online_cpus()) {
timekeeping_resume();
trace_suspend_resume(TPS("timekeeping_freeze"),
smp_processor_id(), false);
} else {
tick_resume_local();
}
tick_freeze_depth--;
raw_spin_unlock(&tick_freeze_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Rafael J. Wysocki | 53 | 100.00% | 3 | 100.00% |
Total | 53 | 100.00% | 3 | 100.00% |
#endif /* CONFIG_SUSPEND */
/**
* tick_init - initialize the tick control
*/
void __init tick_init(void)
{
tick_broadcast_init();
tick_nohz_init();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 11 | 78.57% | 2 | 66.67% |
Frédéric Weisbecker | 3 | 21.43% | 1 | 33.33% |
Total | 14 | 100.00% | 3 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Thomas Gleixner | 1156 | 80.00% | 28 | 47.46% |
Rafael J. Wysocki | 127 | 8.79% | 4 | 6.78% |
Sébastien Dugué | 32 | 2.21% | 1 | 1.69% |
Viresh Kumar | 23 | 1.59% | 6 | 10.17% |
Ingo Molnar | 20 | 1.38% | 1 | 1.69% |
Frédéric Weisbecker | 16 | 1.11% | 3 | 5.08% |
Stephen Boyd | 13 | 0.90% | 1 | 1.69% |
John Stultz | 12 | 0.83% | 3 | 5.08% |
Mike Travis | 11 | 0.76% | 2 | 3.39% |
Rusty Russell | 9 | 0.62% | 3 | 5.08% |
Venkatesh Pallipadi | 8 | 0.55% | 1 | 1.69% |
Christoph Lameter | 6 | 0.42% | 2 | 3.39% |
David S. Miller | 5 | 0.35% | 1 | 1.69% |
Russell King | 4 | 0.28% | 1 | 1.69% |
Martin Schwidefsky | 2 | 0.14% | 1 | 1.69% |
Andrew Morton | 1 | 0.07% | 1 | 1.69% |
Total | 1445 | 100.00% | 59 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.