cregit-Linux how code gets into the kernel

Release 4.7 kernel/time/tick-common.c

Directory: kernel/time
/*
 * 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

PersonTokensPropCommitsCommitProp
ingo molnaringo molnar19100.00%1100.00%
Total19100.00%1100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner5296.30%266.67%
christoph lameterchristoph lameter23.70%133.33%
Total54100.00%3100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner5691.80%133.33%
john stultzjohn stultz58.20%266.67%
Total61100.00%3100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner7372.28%225.00%
viresh kumarviresh kumar1716.83%337.50%
david s. millerdavid s. miller54.95%112.50%
john stultzjohn stultz54.95%112.50%
martin schwidefskymartin schwidefsky10.99%112.50%
Total101100.00%8100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner12096.00%457.14%
john stultzjohn stultz21.60%114.29%
viresh kumarviresh kumar21.60%114.29%
martin schwidefskymartin schwidefsky10.80%114.29%
Total125100.00%7100.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) { ktime_t next_event; void (*handler)(struct clock_event_device *) = NULL; /* * 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 = ktime_set(0, 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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner16586.39%550.00%
frederic weisbeckerfrederic weisbecker136.81%220.00%
venkatesh pallipadivenkatesh pallipadi84.19%110.00%
rusty russellrusty russell31.57%110.00%
mike travismike travis21.05%110.00%
Total191100.00%10100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner5996.72%150.00%
christoph lameterchristoph lameter23.28%150.00%
Total61100.00%2100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner8388.30%250.00%
mike travismike travis66.38%125.00%
rusty russellrusty russell55.32%125.00%
Total94100.00%4100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner6583.33%375.00%
stephen boydstephen boyd1316.67%125.00%
Total78100.00%4100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner3897.44%150.00%
viresh kumarviresh kumar12.56%150.00%
Total39100.00%2100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner14197.24%777.78%
mike travismike travis32.07%111.11%
rusty russellrusty russell10.69%111.11%
Total145100.00%9100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner42100.00%1100.00%
Total42100.00%1100.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

PersonTokensPropCommitsCommitProp
sebastien duguesebastien dugue3288.89%150.00%
thomas gleixnerthomas gleixner411.11%150.00%
Total36100.00%2100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner6897.14%480.00%
viresh kumarviresh kumar22.86%120.00%
Total70100.00%5100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner2392.00%375.00%
christoph lameterchristoph lameter28.00%125.00%
Total25100.00%4100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner5898.31%583.33%
viresh kumarviresh kumar11.69%116.67%
Total59100.00%6100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner13100.00%1100.00%
Total13100.00%1100.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

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner13100.00%1100.00%
Total13100.00%1100.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

PersonTokensPropCommitsCommitProp
rafael j. wysockirafael j. wysocki5298.11%266.67%
thomas gleixnerthomas gleixner11.89%133.33%
Total53100.00%3100.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

PersonTokensPropCommitsCommitProp
rafael j. wysockirafael j. wysocki53100.00%3100.00%
Total53100.00%3100.00%

#endif /* CONFIG_SUSPEND */ /** * tick_init - initialize the tick control */
void __init tick_init(void) { tick_broadcast_init(); tick_nohz_init(); }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner1178.57%266.67%
frederic weisbeckerfrederic weisbecker321.43%133.33%
Total14100.00%3100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner115980.04%2746.55%
rafael j. wysockirafael j. wysocki1278.77%46.90%
sebastien duguesebastien dugue322.21%11.72%
viresh kumarviresh kumar231.59%610.34%
ingo molnaringo molnar201.38%11.72%
frederic weisbeckerfrederic weisbecker161.10%35.17%
stephen boydstephen boyd130.90%11.72%
john stultzjohn stultz120.83%35.17%
mike travismike travis110.76%23.45%
rusty russellrusty russell90.62%35.17%
venkatesh pallipadivenkatesh pallipadi80.55%11.72%
christoph lameterchristoph lameter60.41%23.45%
david s. millerdavid s. miller50.35%11.72%
russell kingrussell king40.28%11.72%
martin schwidefskymartin schwidefsky20.14%11.72%
andrew mortonandrew morton10.07%11.72%
Total1448100.00%58100.00%
Directory: kernel/time
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}