High-Resolution Timers

来源:互联网 发布:java异常机制 编辑:程序博客网 时间:2024/05/22 04:53

通常软时钟是建立在周期性的时钟中断的基础之上的,为了获取精度较高的软时钟中断,不得不提高时钟中断频率,但是过高的中断频率会造成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_nodenode;  ktime_t_softexpires;enum hrtimer_restart(*function)(struct hrtimer *);struct hrtimer_clock_base*base;unsigned longstate;#ifdef CONFIG_TIMER_STATSintstart_pid;void*start_site;charstart_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);                                      //处理timer    trace_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;}