本文主要是介绍High-Resolution Timers,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
通常软时钟是建立在周期性的时钟中断的基础之上的,为了获取精度较高的软时钟中断,不得不提高时钟中断频率,但是过高的中断频率会造成CPU运算周期的浪费。High-Resolution机制,通过可编程的硬件定时器,把它的到期时间设置为软定时器队列中最早到期的时间,当时钟到期时,再把剩余的软定时器队列的最早到期时间编程到硬件定时器中,这样既能提高软时钟的精度,也不至于影响系统的性能。
High-Resolution Timers管理结构:
/*** struct hrtimer - the basic hrtimer structure* @node: timerqueue node, which also manages node.expires,* the absolute expiry time in the hrtimers internal* representation. The time is related to the clock on* which the timer is based. Is setup by adding* slack to the _softexpires value. For non range timers* identical to _softexpires.* @_softexpires: the absolute earliest expiry time of the hrtimer.* The time which was given as expiry time when the timer* was armed.* @function: timer expiry callback function* @base: pointer to the timer base (per cpu and per clock)* @state: state information (See bit values above)* @start_site: timer statistics field to store the site where the timer* was started* @start_comm: timer statistics field to store the name of the process which* started the timer* @start_pid: timer statistics field to store the pid of the task which* started the timer** The hrtimer structure must be initialized by hrtimer_init()*/
struct hrtimer {struct timerqueue_node node; ktime_t _softexpires;enum hrtimer_restart (*function)(struct hrtimer *);struct hrtimer_clock_base *base;unsigned long state;
#ifdef CONFIG_TIMER_STATSint start_pid;void *start_site;char start_comm[16];
#endif
};struct timerqueue_node {struct rb_node node; //采用红黑树进行管理ktime_t expires; //定时器到期时间
};/*** struct hrtimer_clock_base - the timer base for a specific clock* @cpu_base: per cpu clock base* @index: clock type index for per_cpu support when moving a* timer to a base on another cpu.* @clockid: clock id for per_cpu support* @active: red black tree root node for the active timers* @resolution: the resolution of the clock, in nanoseconds* @get_time: function to retrieve the current time of the clock* @softirq_time: the time when running the hrtimer queue in the softirq* @offset: offset of this clock to the monotonic base*/
struct hrtimer_clock_base {struct hrtimer_cpu_base *cpu_base;int index;clockid_t clockid;struct timerqueue_head active;ktime_t resolution;ktime_t (*get_time)(void);ktime_t softirq_time;ktime_t offset;
};
High-Resolution Timers的初始化:
void __init hrtimers_init(void)
{hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,(void *)(long)smp_processor_id());register_cpu_notifier(&hrtimers_nb);
#ifdef CONFIG_HIGH_RES_TIMERSopen_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
#endif
}//最终调用/** Functions related to boot-time initialization:*/
static void __cpuinit init_hrtimers_cpu(int cpu)
{struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);int i;raw_spin_lock_init(&cpu_base->lock);for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {cpu_base->clock_base[i].cpu_base = cpu_base;timerqueue_init_head(&cpu_base->clock_base[i].active);}hrtimer_init_hres(cpu_base);
}
内核初始化之初,并没有启动High-Resolution Timers,每当执行时钟软中断时,会检查能否需要进入High-Resolution Timers模式,如果能,则进行进一步的初始化工作:
/*
* This function runs timers and the timer-tq in bottom half context.
*/
static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = __this_cpu_read(tvec_bases);
hrtimer_run_pending();
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
/*
* Called from timer softirq every jiffy, expire hrtimers:
*
* For HRT its the fall back code to run the softirq in the timer
* softirq context in case the hrtimer initialization failed or has
* not been done yet.
*/
void hrtimer_run_pending(void)
{
if (hrtimer_hres_active()) //如果已经启用了hrtimer机制,则返回。
return;
/*
* This _is_ ugly: We have to check in the softirq context,
* whether we can switch to highres and / or nohz mode. The
* clocksource switch happens in the timer interrupt with
* xtime_lock held. Notification from there only sets the
* check bit in the tick_oneshot code, otherwise we might
* deadlock vs. xtime_lock.
*/
if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
/*hrtimer_is_hres_enabled: query if the highres mode is enabled
* tick_check_oneshot_change: Check, if a change happened, which makes oneshot possible.
*/
hrtimer_switch_to_hres(); //Switch to high resolution mode
}
/*
* Switch to high resolution mode
*/
static int hrtimer_switch_to_hres(void)
{
int i, cpu = smp_processor_id();
struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
unsigned long flags;
if (base->hres_active) //如果已经启用了hres机制,则返回
return 1;
local_irq_save(flags);
if (tick_init_highres()) {
local_irq_restore(flags);
printk(KERN_WARNING "Could not switch to high resolution "
"mode on CPU %d\n", cpu);
return 0;
}
base->hres_active = 1;
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
base->clock_base[i].resolution = KTIME_HIGH_RES;
tick_setup_sched_timer(); // setup the tick emulation timer
/* "Retrigger" the interrupt to get things going */
retrigger_next_event(NULL);
local_irq_restore(flags);
return 1;
}
/**
* tick_init_highres - switch to high resolution mode
*
* Called with interrupts disabled.
*/
int tick_init_highres(void)
{
return tick_switch_to_oneshot(hrtimer_interrupt);
}
/**
* tick_switch_to_oneshot - switch to oneshot mode
*/
int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
{
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
struct clock_event_device *dev = td->evtdev;
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
!tick_device_is_functional(dev)) {
printk(KERN_INFO "Clockevents: "
"could not switch to one-shot mode:");
if (!dev) {
printk(" no tick device\n");
} else {
if (!tick_device_is_functional(dev))
printk(" %s is not functional.\n", dev->name);
else
printk(" %s does not support one-shot mode.\n",
dev->name);
}
return -EINVAL;
}
td->mode = TICKDEV_MODE_ONESHOT;
/*
把CPU本地clock event device的event handler设置为hrtimer_interrupt()
*/
dev->event_handler = handler;
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
tick_broadcast_switch_to_oneshot();
return 0;
}
/*
* 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);
/*
把全局broadcast device的event_handler设置为tick_broadcast_setup_oneshot.de
*/
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);
}
/*
*切换到High-Resolution Timers后,时钟中断设备主要不是为了周期性的tick而发出中断,所以每一个CPU有一个虚拟tick定时器。
*/
/**
* struct tick_sched - sched tick emulation and no idle tick control/stats
* @sched_timer: hrtimer to schedule the periodic tick in high
* resolution mode
* @idle_tick: Store the last idle tick expiry time when the tick
* timer is modified for idle sleeps. This is necessary
* to resume the tick timer operation in the timeline
* when the CPU returns from idle
* @tick_stopped: Indicator that the idle tick has been stopped
* @idle_jiffies: jiffies at the entry to idle for idle time accounting
* @idle_calls: Total number of idle calls
* @idle_sleeps: Number of idle calls, where the sched tick was stopped
* @idle_entrytime: Time when the idle call was entered
* @idle_waketime: Time when the idle was interrupted
* @idle_exittime: Time when the idle state was left
* @idle_sleeptime: Sum of the time slept in idle with sched tick stopped
* @iowait_sleeptime: Sum of the time slept in idle with sched tick stopped, with IO outstanding
* @sleep_length: Duration of the current idle sleep
* @do_timer_lst: CPU was the last one doing do_timer before going idle
*/
struct tick_sched {
struct hrtimer sched_timer;
unsigned long check_clocks;
enum tick_nohz_mode nohz_mode;
ktime_t idle_tick;
int inidle;
int tick_stopped;
unsigned long idle_jiffies;
unsigned long idle_calls;
unsigned long idle_sleeps;
int idle_active;
ktime_t idle_entrytime;
ktime_t idle_waketime;
ktime_t idle_exittime;
ktime_t idle_sleeptime;
ktime_t iowait_sleeptime;
ktime_t sleep_length;
unsigned long last_jiffies;
unsigned long next_jiffies;
ktime_t idle_expires;
int do_timer_last;
};
/**
* tick_setup_sched_timer - setup the tick emulation timer
*/
void tick_setup_sched_timer(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
ktime_t now = ktime_get();
/*
* Emulate tick processing via per-CPU hrtimers:
*/
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ts->sched_timer.function = tick_sched_timer;
/* Get the next period (per cpu) */
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
/*
*添加hrtimer
*/
for (;;) {
hrtimer_forward(&ts->sched_timer, now, tick_period);
hrtimer_start_expires(&ts->sched_timer,
HRTIMER_MODE_ABS_PINNED);
/* Check, if the timer was already in the past */
if (hrtimer_active(&ts->sched_timer))
break;
now = ktime_get();
}
#ifdef CONFIG_NO_HZ
if (tick_nohz_enabled) {
ts->nohz_mode = NOHZ_MODE_HIGHRES;
printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id());
}
#endif
}
/** Called from hardirq context every jiffy*/
void hrtimer_run_queues(void)
{struct timerqueue_node *node;struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);struct hrtimer_clock_base *base;int index, gettime = 1;
/*如果已经启用了hrtimer,则返回*/if (hrtimer_hres_active())return;for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { //处理CPU上所有的hr_clock_basebase = &cpu_base->clock_base[index];if (!timerqueue_getnext(&base->active))continue;if (gettime) {hrtimer_get_softirq_time(cpu_base);gettime = 0;}raw_spin_lock(&cpu_base->lock);while ((node = timerqueue_getnext(&base->active))) { //处理base上所有到期的定时器struct hrtimer *timer;timer = container_of(node, struct hrtimer, node);if (base->softirq_time.tv64 <= //如果发现一个定时器未到期,则其后的定时器肯定没有到期,则breakhrtimer_get_expires_tv64(timer))break;__run_hrtimer(timer, &base->softirq_time); //如果已经到期,则执行High-Resolution Timers的软中断函数}raw_spin_unlock(&cpu_base->lock);}
}static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
{struct hrtimer_clock_base *base = timer->base;struct hrtimer_cpu_base *cpu_base = base->cpu_base;enum hrtimer_restart (*fn)(struct hrtimer *);int restart;WARN_ON(!irqs_disabled());debug_deactivate(timer);__remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0); //将传递过来的事件从红黑树中删除timer_stats_account_hrtimer(timer);fn = timer->function; //获取到期时钟的处理函数/** Because we run timers from hardirq context, there is no chance* they get migrated to another cpu, therefore its safe to unlock* the timer base.*/raw_spin_unlock(&cpu_base->lock);trace_hrtimer_expire_entry(timer, now);restart = fn(timer); //处理timertrace_hrtimer_expire_exit(timer);raw_spin_lock(&cpu_base->lock);/** Note: We clear the CALLBACK bit after enqueue_hrtimer and* we do not reprogramm the event hardware. Happens either in* hrtimer_start_range_ns() or in hrtimer_interrupt()*/if (restart != HRTIMER_NORESTART) {BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);enqueue_hrtimer(timer, base);}WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));timer->state &= ~HRTIMER_STATE_CALLBACK;
}
这篇关于High-Resolution Timers的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!