Chinaunix首页 | 论坛 | 博客
  • 博客访问: 326984
  • 博文数量: 78
  • 博客积分: 1322
  • 博客等级: 中尉
  • 技术积分: 680
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-14 13:24
文章分类
文章存档

2012年(20)

2011年(55)

2010年(3)

分类:

2011-11-01 13:00:00

高精度定时器hrtimer属于某个cpu的hrtimer_cpu_base,意思即他是与cpu绑定的。如果某个进程在另外的cpu上使能这个hrtimer,就需要改变这个hrtimer的cpu base了。这个很类似进程在cfs_rq之间的迁移,不过一个定时器占用的资源太少了,迁移起来相对容易。

这种设计思路,我认为是有些问题的。时间轴是唯一的,所有的定时器,只要是realtime/monotonic time,都可以挂在这个标尺上去维护。多核上定时器在cpu之间迁移,应该是为了其他mode的定时器考虑。最新的hrtimer增加了些clock base和timer mode,目前还不清楚这些背后的机理。

简单了解下使能一个高精度定时器的流程。
hrtimer_start()在当前cpu上使能一个hrtimer,它调用__hrtimer_start_range_ns()完成这个任务。

int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
        unsigned long delta_ns, const enum hrtimer_mode mode,
        int wakeup)
{
    struct hrtimer_clock_base *base, *new_base;
    unsigned long flags;
    int ret, leftmost;

    base = lock_hrtimer_base(timer, &flags);

    /* Remove an active timer from the queue: */
    ret = remove_hrtimer(timer, base);//先从当前红黑树摘下

    /* Switch the timer base, if necessary: */
    new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);//迁移定时器的cpu base

    if (mode & HRTIMER_MODE_REL) {
        tim = ktime_add_safe(tim, new_base->get_time());
        /*
         * CONFIG_TIME_LOW_RES is a temporary way for architectures
         * to signal that they simply return xtime in
         * do_gettimeoffset(). In this case we want to round up by
         * resolution when starting a relative timer, to avoid short
         * timeouts. This will go away with the GTOD framework.
         */
#ifdef CONFIG_TIME_LOW_RES
        tim = ktime_add_safe(tim, base->resolution);
#endif
    }

    hrtimer_set_expires_range_ns(timer, tim, delta_ns);

    timer_stats_hrtimer_set_start_info(timer);

    leftmost = enqueue_hrtimer(timer, new_base); //把定时器重新放入当前cpu的队列

    /*
     * Only allow reprogramming if the new base is on this CPU.
     * (it might still be on another CPU if the timer was pending)
     *
     * XXX send_remote_softirq() ?
     */
    if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases))
        hrtimer_enqueue_reprogram(timer, new_base, wakeup);  //如果是最左侧的定时器,就要重新对底层clock event device设置中断了。

    unlock_hrtimer_base(timer, &flags);

    return ret;
}

决定hrtimer是否迁移的函数是:
/*
 * Switch the timer base to the current CPU when possible.
 */
static inline struct hrtimer_clock_base *
switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base,
            int pinned)
{
    struct hrtimer_clock_base *new_base;
    struct hrtimer_cpu_base *new_cpu_base;
    int this_cpu = smp_processor_id();
    int cpu = hrtimer_get_target(this_cpu, pinned);

again:
    new_cpu_base = &per_cpu(hrtimer_bases, cpu);
    new_base = &new_cpu_base->clock_base[base->index];

    if (base != new_base) { //当前进程使能hrtimer的cpu base与hrtimer默认挂载的cpu base不同,则要pull hrtimer from old cpu to current cpu.
        /*
         * We are trying to move timer to new_base.
         * However we can't change timer's base while it is running,
         * so we keep it on the same CPU. No hassle vs. reprogramming
         * the event source in the high resolution case. The softirq
         * code will take care of this when the timer function has
         * completed. There is no conflict as we hold the lock until
         * the timer is enqueued.
         */
        if (unlikely(hrtimer_callback_running(timer)))
            return base;

        /* See the comment in lock_timer_base() */
        timer->base = NULL;
        raw_spin_unlock(&base->cpu_base->lock);
        raw_spin_lock(&new_base->cpu_base->lock);

        if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
            cpu = this_cpu;
            raw_spin_unlock(&new_base->cpu_base->lock);
            raw_spin_lock(&base->cpu_base->lock);
            timer->base = base; //完成修改base
            goto again;
        }
        timer->base = new_base;
    }
    return new_base;
}

不同cpu的clock source配置可能不一致,这会引起hrtiemr实际运行中的精度问题。
阅读(3872) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~