cregit-Linux how code gets into the kernel

Release 4.15 kernel/time/tick-broadcast.c

Directory: kernel/time
/*
 * linux/kernel/time/tick-broadcast.c
 *
 * This file contains functions which emulate a local clock-event
 * device via a broadcast event source.
 *
 * 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/smp.h>
#include <linux/module.h>

#include "tick-internal.h"

/*
 * Broadcast support for broken x86 hardware, where the local apic
 * timer stops in C3 state.
 */


static struct tick_device tick_broadcast_device;

static cpumask_var_t tick_broadcast_mask __cpumask_var_read_mostly;

static cpumask_var_t tick_broadcast_on __cpumask_var_read_mostly;

static cpumask_var_t tmpmask __cpumask_var_read_mostly;

static int tick_broadcast_forced;

static __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(tick_broadcast_lock);

#ifdef CONFIG_TICK_ONESHOT
static void tick_broadcast_setup_oneshot(struct clock_event_device *bc);
static void tick_broadcast_clear_oneshot(int cpu);
static void tick_resume_broadcast_oneshot(struct clock_event_device *bc);
#else

static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { BUG(); }

Contributors

PersonTokensPropCommitsCommitProp
Stephen Boyd15100.00%1100.00%
Total15100.00%1100.00%


static inline void tick_broadcast_clear_oneshot(int cpu) { }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner9100.00%1100.00%
Total9100.00%1100.00%


static inline void tick_resume_broadcast_oneshot(struct clock_event_device *bc) { }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner11100.00%1100.00%
Total11100.00%1100.00%

#endif /* * Debugging: see timer_list.c */
struct tick_device *tick_get_broadcast_device(void) { return &tick_broadcast_device; }

Contributors

PersonTokensPropCommitsCommitProp
Ingo Molnar13100.00%1100.00%
Total13100.00%1100.00%


struct cpumask *tick_get_broadcast_mask(void) { return tick_broadcast_mask; }

Contributors

PersonTokensPropCommitsCommitProp
Ingo Molnar1083.33%150.00%
Rusty Russell216.67%150.00%
Total12100.00%2100.00%

/* * Start the device in periodic mode */
static void tick_broadcast_start_periodic(struct clock_event_device *bc) { if (bc) tick_setup_periodic(bc, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner22100.00%1100.00%
Total22100.00%1100.00%

/* * Check, if the device can be utilized as broadcast device: */
static bool tick_check_broadcast_device(struct clock_event_device *curdev, struct clock_event_device *newdev) { if ((newdev->features & CLOCK_EVT_FEAT_DUMMY) || (newdev->features & CLOCK_EVT_FEAT_PERCPU) || (newdev->features & CLOCK_EVT_FEAT_C3STOP)) return false; if (tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT && !(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) return false; return !curdev || newdev->rating > curdev->rating; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner5875.32%350.00%
Sören Brinkmann810.39%116.67%
Mark Rutland79.09%116.67%
Venkatesh Pallipadi45.19%116.67%
Total77100.00%6100.00%

/* * Conditionally install/replace broadcast device */
void tick_install_broadcast_device(struct clock_event_device *dev) { struct clock_event_device *cur = tick_broadcast_device.evtdev; if (!tick_check_broadcast_device(cur, dev)) return; if (!try_module_get(dev->owner)) return; clockevents_exchange_device(cur, dev); if (cur) cur->event_handler = clockevents_handle_noop; tick_broadcast_device.evtdev = dev; if (!cpumask_empty(tick_broadcast_mask)) tick_broadcast_start_periodic(dev); /* * Inform all cpus about this. We might be in a situation * where we did not switch to oneshot mode because the per cpu * devices are affected by CLOCK_EVT_FEAT_C3STOP and the lack * of a oneshot capable broadcast device. Without that * notification the systems stays stuck in periodic mode * forever. */ if (dev->features & CLOCK_EVT_FEAT_ONESHOT) tick_clock_notify(); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner7685.39%675.00%
Stephen Boyd1213.48%112.50%
Rusty Russell11.12%112.50%
Total89100.00%8100.00%

/* * Check, if the device is the broadcast device */
int tick_is_broadcast_device(struct clock_event_device *dev) { return (dev && tick_broadcast_device.evtdev == dev); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner21100.00%1100.00%
Total21100.00%1100.00%


int tick_broadcast_update_freq(struct clock_event_device *dev, u32 freq) { int ret = -ENODEV; if (tick_is_broadcast_device(dev)) { raw_spin_lock(&tick_broadcast_lock); ret = __clockevents_update_freq(dev, freq); raw_spin_unlock(&tick_broadcast_lock); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner52100.00%1100.00%
Total52100.00%1100.00%


static void err_broadcast(const struct cpumask *mask) { pr_crit_once("Failed to broadcast timer tick. Some CPUs may be unresponsive.\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland17100.00%1100.00%
Total17100.00%1100.00%


static void tick_device_setup_broadcast_func(struct clock_event_device *dev) { if (!dev->broadcast) dev->broadcast = tick_broadcast; if (!dev->broadcast) { pr_warn_once("%s depends on broadcast, but no broadcast function available\n", dev->name); dev->broadcast = err_broadcast; } }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland48100.00%1100.00%
Total48100.00%1100.00%

/* * Check, if the device is disfunctional and a place holder, which * needs to be handled by the broadcast device. */
int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) { struct clock_event_device *bc = tick_broadcast_device.evtdev; unsigned long flags; int ret = 0; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); /* * Devices might be registered with both periodic and oneshot * mode disabled. This signals, that the device needs to be * operated from the broadcast device and is a placeholder for * the cpu local device. */ if (!tick_device_is_functional(dev)) { dev->event_handler = tick_handle_periodic; tick_device_setup_broadcast_func(dev); cpumask_set_cpu(cpu, tick_broadcast_mask); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_broadcast_start_periodic(bc); else tick_broadcast_setup_oneshot(bc); ret = 1; } else { /* * Clear the broadcast bit for this cpu if the * device is not power state affected. */ if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) cpumask_clear_cpu(cpu, tick_broadcast_mask); else tick_device_setup_broadcast_func(dev); /* * Clear the broadcast bit if the CPU is not in * periodic broadcast on state. */ if (!cpumask_test_cpu(cpu, tick_broadcast_on)) cpumask_clear_cpu(cpu, tick_broadcast_mask); switch (tick_broadcast_device.mode) { case TICKDEV_MODE_ONESHOT: /* * If the system is in oneshot mode we can * unconditionally clear the oneshot mask bit, * because the CPU is running and therefore * not in an idle state which causes the power * state affected device to stop. Let the * caller initialize the device. */ tick_broadcast_clear_oneshot(cpu); ret = 0; break; case TICKDEV_MODE_PERIODIC: /* * If the system is in periodic mode, check * whether the broadcast device can be * switched off now. */ if (cpumask_empty(tick_broadcast_mask) && bc) clockevents_shutdown(bc); /* * If we kept the cpu in the broadcast mask, * tell the caller to leave the per cpu device * in shutdown state. The periodic interrupt * is delivered by the broadcast device, if * the broadcast device exists and is not * hrtimer based. */ if (bc && !(bc->features & CLOCK_EVT_FEAT_HRTIMER)) ret = cpumask_test_cpu(cpu, tick_broadcast_mask); break; default: break; } } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner18988.32%660.00%
Stephen Boyd146.54%110.00%
Mark Rutland94.21%220.00%
Rusty Russell20.93%110.00%
Total214100.00%10100.00%

#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
int tick_receive_broadcast(void) { struct tick_device *td = this_cpu_ptr(&tick_cpu_device); struct clock_event_device *evt = td->evtdev; if (!evt) return -ENODEV; if (!evt->event_handler) return -EINVAL; evt->event_handler(evt); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Mark Rutland57100.00%1100.00%
Total57100.00%1100.00%

#endif /* * Broadcast the event to the cpus, which are set in the mask (mangled). */
static bool tick_do_broadcast(struct cpumask *mask) { int cpu = smp_processor_id(); struct tick_device *td; bool local = false; /* * Check, if the current cpu is in the mask */ if (cpumask_test_cpu(cpu, mask)) { struct clock_event_device *bc = tick_broadcast_device.evtdev; cpumask_clear_cpu(cpu, mask); /* * We only run the local handler, if the broadcast * device is not hrtimer based. Otherwise we run into * a hrtimer recursion. * * local timer_interrupt() * local_handler() * expire_hrtimers() * bc_handler() * local_handler() * expire_hrtimers() */ local = !(bc->features & CLOCK_EVT_FEAT_HRTIMER); } if (!cpumask_empty(mask)) { /* * It might be necessary to actually check whether the devices * have different broadcast functions. For now, just use the * one of the first device. This works as long as we have this * misfeature only on x86 (lapic) */ td = &per_cpu(tick_cpu_device, cpumask_first(mask)); td->evtdev->broadcast(mask); } return local; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner9390.29%480.00%
Rusty Russell109.71%120.00%
Total103100.00%5100.00%

/* * Periodic broadcast: * - invoke the broadcast handlers */
static bool tick_do_periodic_broadcast(void) { cpumask_and(tmpmask, cpu_online_mask, tick_broadcast_mask); return tick_do_broadcast(tmpmask); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner1878.26%375.00%
Rusty Russell521.74%125.00%
Total23100.00%4100.00%

/* * Event handler for periodic broadcast ticks */
static void tick_handle_periodic_broadcast(struct clock_event_device *dev) { struct tick_device *td = this_cpu_ptr(&tick_cpu_device); bool bc_local; raw_spin_lock(&tick_broadcast_lock); /* Handle spurious interrupts gracefully */ if (clockevent_state_shutdown(tick_broadcast_device.evtdev)) { raw_spin_unlock(&tick_broadcast_lock); return; } bc_local = tick_do_periodic_broadcast(); if (clockevent_state_oneshot(dev)) { ktime_t next = ktime_add(dev->next_event, tick_period); clockevents_program_event(dev, next, true); } raw_spin_unlock(&tick_broadcast_lock); /* * We run the handler of the local cpu after dropping * tick_broadcast_lock because the handler might deadlock when * trying to switch to oneshot mode. */ if (bc_local) td->evtdev->event_handler(td->evtdev); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner10497.20%583.33%
Viresh Kumar32.80%116.67%
Total107100.00%6100.00%

/** * tick_broadcast_control - Enable/disable or force broadcast mode * @mode: The selected broadcast mode * * Called when the system enters a state where affected tick devices * might stop. Note: TICK_BROADCAST_FORCE cannot be undone. */
void tick_broadcast_control(enum tick_broadcast_mode mode) { struct clock_event_device *bc, *dev; struct tick_device *td; int cpu, bc_stopped; unsigned long flags; /* Protects also the local clockevent device. */ raw_spin_lock_irqsave(&tick_broadcast_lock, flags); td = this_cpu_ptr(&tick_cpu_device); dev = td->evtdev; /* * Is the device not affected by the powerstate ? */ if (!dev || !(dev->features & CLOCK_EVT_FEAT_C3STOP)) goto out; if (!tick_device_is_functional(dev)) goto out; cpu = smp_processor_id(); bc = tick_broadcast_device.evtdev; bc_stopped = cpumask_empty(tick_broadcast_mask); switch (mode) { case TICK_BROADCAST_FORCE: tick_broadcast_forced = 1; case TICK_BROADCAST_ON: cpumask_set_cpu(cpu, tick_broadcast_on); if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_mask)) { /* * Only shutdown the cpu local device, if: * * - the broadcast device exists * - the broadcast device is not a hrtimer based one * - the broadcast device is in periodic mode to * avoid a hickup during switch to oneshot mode */ if (bc && !(bc->features & CLOCK_EVT_FEAT_HRTIMER) && tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) clockevents_shutdown(dev); } break; case TICK_BROADCAST_OFF: if (tick_broadcast_forced) break; cpumask_clear_cpu(cpu, tick_broadcast_on); if (!tick_device_is_functional(dev)) break; if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_mask)) { if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(dev, 0); } break; } if (bc) { if (cpumask_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 tick_broadcast_setup_oneshot(bc); } } out: raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner24890.51%1184.62%
Mike Galbraith248.76%17.69%
Rusty Russell20.73%17.69%
Total274100.00%13100.00%

EXPORT_SYMBOL_GPL(tick_broadcast_control); /* * Set the periodic handler depending on broadcast on/off */
void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast) { if (!broadcast) dev->event_handler = tick_handle_periodic; else dev->event_handler = tick_handle_periodic_broadcast; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner31100.00%1100.00%
Total31100.00%1100.00%

#ifdef CONFIG_HOTPLUG_CPU /* * Remove a CPU from broadcasting */
void tick_shutdown_broadcast(unsigned int cpu) { struct clock_event_device *bc; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; cpumask_clear_cpu(cpu, tick_broadcast_mask); cpumask_clear_cpu(cpu, tick_broadcast_on); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) { if (bc && cpumask_empty(tick_broadcast_mask)) clockevents_shutdown(bc); } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner7697.44%685.71%
Rusty Russell22.56%114.29%
Total78100.00%7100.00%

#endif
void tick_suspend_broadcast(void) { struct clock_event_device *bc; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; if (bc) clockevents_shutdown(bc); raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner47100.00%3100.00%
Total47100.00%3100.00%

/* * This is called from tick_resume_local() on a resuming CPU. That's * called from the core resume function, tick_unfreeze() and the magic XEN * resume hackery. * * In none of these cases the broadcast device mode can change and the * bit of the resuming CPU in the broadcast mask is safe as well. */
bool tick_resume_check_broadcast(void) { if (tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT) return false; else return cpumask_test_cpu(smp_processor_id(), tick_broadcast_mask); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner28100.00%1100.00%
Total28100.00%1100.00%


void tick_resume_broadcast(void) { struct clock_event_device *bc; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; if (bc) { clockevents_tick_resume(bc); switch (tick_broadcast_device.mode) { case TICKDEV_MODE_PERIODIC: if (!cpumask_empty(tick_broadcast_mask)) tick_broadcast_start_periodic(bc); break; case TICKDEV_MODE_ONESHOT: if (!cpumask_empty(tick_broadcast_mask)) tick_resume_broadcast_oneshot(bc); break; } } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner8290.11%666.67%
Suresh B. Siddha77.69%111.11%
Rusty Russell11.10%111.11%
Viresh Kumar11.10%111.11%
Total91100.00%9100.00%

#ifdef CONFIG_TICK_ONESHOT static cpumask_var_t tick_broadcast_oneshot_mask __cpumask_var_read_mostly; static cpumask_var_t tick_broadcast_pending_mask __cpumask_var_read_mostly; static cpumask_var_t tick_broadcast_force_mask __cpumask_var_read_mostly; /* * Exposed for debugging: see timer_list.c */
struct cpumask *tick_get_broadcast_oneshot_mask(void) { return tick_broadcast_oneshot_mask; }

Contributors

PersonTokensPropCommitsCommitProp
Ingo Molnar1083.33%150.00%
Rusty Russell216.67%150.00%
Total12100.00%2100.00%

/* * Called before going idle with interrupts disabled. Checks whether a * broadcast event from the other core is about to happen. We detected * that in tick_broadcast_oneshot_control(). The callsite can use this * to avoid a deep idle transition as we are about to get the * broadcast IPI right away. */
int tick_check_broadcast_expired(void) { return cpumask_test_cpu(smp_processor_id(), tick_broadcast_force_mask); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner16100.00%1100.00%
Total16100.00%1100.00%

/* * Set broadcast interrupt affinity */
static void tick_broadcast_set_affinity(struct clock_event_device *bc, const struct cpumask *cpumask) { if (!(bc->features & CLOCK_EVT_FEAT_DYNIRQ)) return; if (cpumask_equal(bc->cpumask, cpumask)) return; bc->cpumask = cpumask; irq_set_affinity(bc->irq, bc->cpumask); }

Contributors

PersonTokensPropCommitsCommitProp
Daniel Lezcano58100.00%1100.00%
Total58100.00%1100.00%


static void tick_broadcast_set_event(struct clock_event_device *bc, int cpu, ktime_t expires) { if (!clockevent_state_oneshot(bc)) clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT); clockevents_program_event(bc, expires, 1); tick_broadcast_set_affinity(bc, cpumask_of(cpu)); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner2650.98%444.44%
Daniel Lezcano1937.25%222.22%
Viresh Kumar59.80%222.22%
Martin Schwidefsky11.96%111.11%
Total51100.00%9100.00%


static void tick_resume_broadcast_oneshot(struct clock_event_device *bc) { clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner1794.44%375.00%
Viresh Kumar15.56%125.00%
Total18100.00%4100.00%

/* * Called from irq_enter() when idle was interrupted to reenable the * per cpu device. */
void tick_check_oneshot_broadcast_this_cpu(void) { if (cpumask_test_cpu(smp_processor_id(), tick_broadcast_oneshot_mask)) { struct tick_device *td = this_cpu_ptr(&tick_cpu_device); /* * We might be in the middle of switching over from * periodic to oneshot. If the CPU has not yet * switched over, leave the device alone. */ if (td->mode == TICKDEV_MODE_ONESHOT) { clockevents_switch_state(td->evtdev, CLOCK_EVT_STATE_ONESHOT); } } }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner4284.00%342.86%
Frédéric Weisbecker48.00%114.29%
Christoph Lameter24.00%114.29%
Rusty Russell12.00%114.29%
Viresh Kumar12.00%114.29%
Total50100.00%7100.00%

/* * Handle oneshot mode broadcasting */
static void tick_handle_oneshot_broadcast(struct clock_event_device *dev) { struct tick_device *td; ktime_t now, next_event; int cpu, next_cpu = 0; bool bc_local; raw_spin_lock(&tick_broadcast_lock); dev->next_event = KTIME_MAX; next_event = KTIME_MAX; cpumask_clear(tmpmask); now = ktime_get(); /* Find all expired events */ for_each_cpu(cpu, tick_broadcast_oneshot_mask) { td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev->next_event <= now) { cpumask_set_cpu(cpu, tmpmask); /* * Mark the remote cpu in the pending mask, so * it can avoid reprogramming the cpu local * timer in tick_broadcast_oneshot_control(). */ cpumask_set_cpu(cpu, tick_broadcast_pending_mask); } else if (td->evtdev->next_event < next_event) { next_event = td->evtdev->next_event; next_cpu = cpu; } } /* * Remove the current cpu from the pending mask. The event is * delivered immediately in tick_do_broadcast() ! */ cpumask_clear_cpu(smp_processor_id(), tick_broadcast_pending_mask); /* Take care of enforced broadcast requests */ cpumask_or(tmpmask, tmpmask, tick_broadcast_force_mask); cpumask_clear(tick_broadcast_force_mask); /* * Sanity check. Catch the case where we try to broadcast to * offline cpus. */ if (WARN_ON_ONCE(!cpumask_subset(tmpmask, cpu_online_mask))) cpumask_and(tmpmask, tmpmask, cpu_online_mask); /* * Wakeup the cpus which have an expired event. */ bc_local = tick_do_broadcast(tmpmask); /* * Two reasons for reprogram: * * - The global event did not expire any CPU local * events. This happens in dyntick mode, as the maximum PIT * delta is quite small. * * - There are pending events on sleeping CPUs which were not * in the event mask */ if (next_event != KTIME_MAX) tick_broadcast_set_event(dev, next_cpu, next_event); raw_spin_unlock(&tick_broadcast_lock); if (bc_local) { td = this_cpu_ptr(&tick_cpu_device); td->evtdev->event_handler(td->evtdev); } }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner20388.26%1071.43%
Daniel Lezcano166.96%214.29%
Rusty Russell104.35%17.14%
Mike Travis10.43%17.14%
Total230100.00%14100.00%


static int broadcast_needs_cpu(struct clock_event_device *bc, int cpu) { if (!(bc->features & CLOCK_EVT_FEAT_HRTIMER)) return 0; if (bc->next_event == KTIME_MAX) return 0; return bc->bound_on == cpu ? -EBUSY : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Preeti U. Murthy51100.00%1100.00%
Total51100.00%1100.00%


static void broadcast_shutdown_local(struct clock_event_device *bc, struct clock_event_device *dev) { /* * For hrtimer based broadcasting we cannot shutdown the cpu * local device if our own event is the first one to expire or * if we own the broadcast timer. */ if (bc->features & CLOCK_EVT_FEAT_HRTIMER) { if (broadcast_needs_cpu(bc, smp_processor_id())) return; if (dev->next_event < bc->next_event) return; } clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN); }

Contributors

PersonTokensPropCommitsCommitProp
Preeti U. Murthy5496.43%133.33%
Thomas Gleixner11.79%133.33%
Viresh Kumar11.79%133.33%
Total56100.00%3100.00%


int __tick_broadcast_oneshot_control(enum tick_broadcast_state state) { struct clock_event_device *bc, *dev; int cpu, ret = 0; ktime_t now; /* * If there is no broadcast device, tell the caller not to go * into deep idle. */ if (!tick_broadcast_device.evtdev) return -EBUSY; dev = this_cpu_ptr(&tick_cpu_device)->evtdev; raw_spin_lock(&tick_broadcast_lock); bc = tick_broadcast_device.evtdev; cpu = smp_processor_id(); if (state == TICK_BROADCAST_ENTER) { /* * If the current CPU owns the hrtimer broadcast * mechanism, it cannot go deep idle and we do not add * the CPU to the broadcast mask. We don't have to go * through the EXIT path as the local timer is not * shutdown. */ ret = broadcast_needs_cpu(bc, cpu); if (ret) goto out; /* * If the broadcast device is in periodic mode, we * return. */ if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) { /* If it is a hrtimer based broadcast, return busy */ if (bc->features & CLOCK_EVT_FEAT_HRTIMER) ret = -EBUSY; goto out; } if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_oneshot_mask)) { WARN_ON_ONCE(cpumask_test_cpu(cpu, tick_broadcast_pending_mask)); /* Conditionally shut down the local timer. */ broadcast_shutdown_local(bc, dev); /* * We only reprogram the broadcast timer if we * did not mark ourself in the force mask and * if the cpu local event is earlier than the * broadcast event. If the current CPU is in * the force mask, then we are going to be * woken by the IPI right away; we return * busy, so the CPU does not try to go deep * idle. */ if (cpumask_test_cpu(cpu, tick_broadcast_force_mask)) { ret = -EBUSY; } else if (dev->next_event < bc->next_event) { tick_broadcast_set_event(bc, cpu, dev->next_event); /* * In case of hrtimer broadcasts the * programming might have moved the * timer to this cpu. If yes, remove * us from the broadcast mask and * return busy. */ ret = broadcast_needs_cpu(bc, cpu); if (ret) { cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask); } } } } else { if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) { clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); /* * The cpu which was handling the broadcast * timer marked this cpu in the broadcast * pending mask and fired the broadcast * IPI. So we are going to handle the expired * event anyway via the broadcast IPI * handler. No need to reprogram the timer * with an already expired event. */ if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_pending_mask)) goto out; /* * Bail out if there is no next event. */ if (dev->next_event == KTIME_MAX) goto out; /* * If the pending bit is not set, then we are * either the CPU handling the broadcast * interrupt or we got woken by something else. * * We are not longer in the broadcast mask, so * if the cpu local expiry time is already * reached, we would reprogram the cpu local * timer with an already expired event. * * This can lead to a ping-pong when we return * to idle and therefor rearm the broadcast * timer before the cpu local timer was able * to fire. This happens because the forced * reprogramming makes sure that the event * will happen in the future and depending on * the min_delta setting this might be far * enough out that the ping-pong starts. * * If the cpu local next_event has expired * then we know that the broadcast timer * next_event has expired as well and * broadcast is about to be handled. So we * avoid reprogramming and enforce that the * broadcast handler, which did not run yet, * will invoke the cpu local handler. * * We cannot call the handler directly from * here, because we might be in a NOHZ phase * and we did not go through the irq_enter() * nohz fixups. */ now = ktime_get(); if (dev->next_event <= now) { cpumask_set_cpu(cpu, tick_broadcast_force_mask); goto out; } /* * We got woken by something else. Reprogram * the cpu local timer device. */ tick_program_event(dev->next_event, 1); } } out: raw_spin_unlock(&tick_broadcast_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner25182.03%1365.00%
Preeti U. Murthy3110.13%210.00%
Daniel Lezcano165.23%315.00%
Andi Kleen72.29%15.00%
Viresh Kumar10.33%15.00%
Total306100.00%20100.00%

/* * Reset the one shot broadcast for a cpu * * Called with tick_broadcast_lock held */
static void tick_broadcast_clear_oneshot(int cpu) { cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask); cpumask_clear_cpu(cpu, tick_broadcast_pending_mask); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner2295.65%375.00%
Rusty Russell14.35%125.00%
Total23100.00%4100.00%


static void tick_broadcast_init_next_event(struct cpumask *mask, ktime_t expires) { struct tick_device *td; int cpu; for_each_cpu(cpu, mask) { td = &per_cpu(tick_cpu_device, cpu); if (td->evtdev) td->evtdev->next_event = expires; } }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner5092.59%133.33%
Rusty Russell47.41%266.67%
Total54100.00%3100.00%

/** * tick_broadcast_setup_oneshot - setup the broadcast device */
static void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { int cpu = smp_processor_id(); if (!bc) return; /* Set it up only once ! */ if (bc->event_handler != tick_handle_oneshot_broadcast) { int was_periodic = clockevent_state_periodic(bc); bc->event_handler = tick_handle_oneshot_broadcast; /* * 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. */ cpumask_copy(tmpmask, tick_broadcast_mask); cpumask_clear_cpu(cpu, tmpmask); cpumask_or(tick_broadcast_oneshot_mask, tick_broadcast_oneshot_mask, tmpmask); if (was_periodic && !cpumask_empty(tmpmask)) { clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT); tick_broadcast_init_next_event(tmpmask, tick_next_period); tick_broadcast_set_event(bc, cpu, tick_next_period); } else bc->next_event = KTIME_MAX; } else { /* * The first cpu which switches to oneshot mode sets * the bit for all other cpus which are in the general * (periodic) broadcast mask. So the bit is set and * would prevent the first broadcast enter after this * to program the bc device. */ tick_broadcast_clear_oneshot(cpu); } }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner9980.49%857.14%
Rusty Russell1512.20%17.14%
Viresh Kumar43.25%214.29%
Daniel Lezcano43.25%214.29%
Stephen Boyd10.81%17.14%
Total123100.00%14100.00%

/* * Select oneshot operating mode for the broadcast device */
void tick_broadcast_switch_to_oneshot(void) { struct clock_event_device *bc; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); tick_broadcast_device.mode = TICKDEV_MODE_ONESHOT; bc = tick_broadcast_device.evtdev; if (bc) tick_broadcast_setup_oneshot(bc); raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner4788.68%266.67%
Suresh B. Siddha611.32%133.33%
Total53100.00%3100.00%

#ifdef CONFIG_HOTPLUG_CPU
void hotplug_cpu__broadcast_tick_pull(int deadcpu) { struct clock_event_device *bc; unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); bc = tick_broadcast_device.evtdev; if (bc && broadcast_needs_cpu(bc, deadcpu)) { /* This moves the broadcast assignment to this CPU: */ clockevents_program_event(bc, bc->next_event, 1); } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner64100.00%1100.00%
Total64100.00%1100.00%

/* * Remove a dead CPU from broadcasting */
void tick_shutdown_broadcast_oneshot(unsigned int cpu) { unsigned long flags; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); /* * Clear the broadcast masks for the dead cpu, but do not stop * the broadcast device! */ cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask); cpumask_clear_cpu(cpu, tick_broadcast_pending_mask); cpumask_clear_cpu(cpu, tick_broadcast_force_mask); raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner5098.04%583.33%
Rusty Russell11.96%116.67%
Total51100.00%6100.00%

#endif /* * Check, whether the broadcast device is in one shot mode */
int tick_broadcast_oneshot_active(void) { return tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner14100.00%1100.00%
Total14100.00%1100.00%

/* * Check whether the broadcast device supports oneshot. */
bool tick_broadcast_oneshot_available(void) { struct clock_event_device *bc = tick_broadcast_device.evtdev; return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner27100.00%1100.00%
Total27100.00%1100.00%

#else
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state) { struct clock_event_device *bc = tick_broadcast_device.evtdev; if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER)) return -EBUSY; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner38100.00%1100.00%
Total38100.00%1100.00%

#endif
void __init tick_broadcast_init(void) { zalloc_cpumask_var(&tick_broadcast_mask, GFP_NOWAIT); zalloc_cpumask_var(&tick_broadcast_on, GFP_NOWAIT); zalloc_cpumask_var(&tmpmask, GFP_NOWAIT); #ifdef CONFIG_TICK_ONESHOT zalloc_cpumask_var(&tick_broadcast_oneshot_mask, GFP_NOWAIT); zalloc_cpumask_var(&tick_broadcast_pending_mask, GFP_NOWAIT); zalloc_cpumask_var(&tick_broadcast_force_mask, GFP_NOWAIT); #endif }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner61100.00%5100.00%
Total61100.00%5100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Thomas Gleixner231578.24%5662.92%
Mark Rutland1464.93%44.49%
Preeti U. Murthy1364.60%22.25%
Daniel Lezcano1143.85%33.37%
Rusty Russell632.13%22.25%
Stephen Boyd521.76%33.37%
Ingo Molnar341.15%11.12%
Mike Galbraith250.84%11.12%
Viresh Kumar170.57%33.37%
Suresh B. Siddha140.47%33.37%
Waiman Long130.44%11.12%
Sören Brinkmann80.27%11.12%
Andi Kleen70.24%11.12%
Venkatesh Pallipadi40.14%11.12%
Frédéric Weisbecker40.14%11.12%
Christoph Lameter20.07%11.12%
Dmitri Vorobiev10.03%11.12%
Mike Travis10.03%11.12%
Martin Schwidefsky10.03%11.12%
Russell King10.03%11.12%
Li Zefan10.03%11.12%
Total2959100.00%89100.00%
Directory: kernel/time
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.