sched_clock为什么要retry

来源:互联网 发布:wpsexcel数据分析工具 编辑:程序博客网 时间:2024/06/07 01:12
前面讲了sched_clock 会返回当前从arch_time 读取的时间,但是计算的时候有个epoch_cyc 和 epoch_ns也需要实时更新.
unsigned long long notrace sched_clock(void)
{
    u64 cyc, res;
    unsigned long seq;
    struct clock_read_data *rd;

    do {
        seq = raw_read_seqcount(&cd.seq);
        rd = cd.read_data + (seq & 1);

        cyc = (rd->read_sched_clock() - rd->epoch_cyc) &
              rd->sched_clock_mask;
        res = rd->epoch_ns + cyc_to_ns(cyc, rd->mult, rd->shift);
    } while (read_seqcount_retry(&cd.seq, seq));

    return res;
}
那这两个值是在哪里更新的呢?
答案是sched_clock_postinit
void __init sched_clock_postinit(void)
{
    /*
     * If no sched_clock() function has been provided at that point,
     * make it the final one one.
     */
    if (cd.actual_read_sched_clock == jiffy_sched_clock_read)
        sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);

    update_sched_clock();

    /*
     * Start the timer to keep sched_clock() properly updated and
     * sets the initial epoch.
     */
    hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    sched_clock_timer.function = sched_clock_poll;
    hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL);
}
这个函数会初始化一个high time,这个high time的expiry time,也就是第二个参数是在sched_clock_register的时候更新的
    /* Calculate how many nanosecs until we risk wrapping */
    wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask, NULL);
    cd.wrap_kt = ns_to_ktime(wrap);
这个high time的callback函数是sched_clock_poll。
static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt)
{
    update_sched_clock();
    hrtimer_forward_now(hrt, cd.wrap_kt);

    return HRTIMER_RESTART;
}
这个函数会调用hrtimer_forward_now 来让持续推动high time向前,会在update_sched_clock->update_clock_read_data 中更新epoch_cyc 和 epoch_ns
static void update_clock_read_data(struct clock_read_data *rd)
{
    /* update the backup (odd) copy with the new data */
    cd.read_data[1] = *rd;

    /* steer readers towards the odd copy */
    raw_write_seqcount_latch(&cd.seq);

    /* now its safe for us to update the normal (even) copy */
    cd.read_data[0] = *rd;

    /* switch readers back to the even copy */
    raw_write_seqcount_latch(&cd.seq);
}
注意这个函数更新的时候分两次更新。
static inline void raw_write_seqcount_latch(seqcount_t *s)
{
       smp_wmb();      /* prior stores before incrementing "sequence" */
       s->sequence++;
       smp_wmb();      /* increment "sequence" before following stores */
}
在raw_write_seqcount_latch 函数中会增加s->sequence++
这也是为什么在sched_clock 会retry的原因。
unsigned long long notrace sched_clock(void)
{
    u64 cyc, res;
    unsigned long seq;
    struct clock_read_data *rd;

    do {
        seq = raw_read_seqcount(&cd.seq);
        rd = cd.read_data + (seq & 1);

        cyc = (rd->read_sched_clock() - rd->epoch_cyc) &
              rd->sched_clock_mask;
        res = rd->epoch_ns + cyc_to_ns(cyc, rd->mult, rd->shift);
    } while (read_seqcount_retry(&cd.seq, seq));

    return res;
}
在read_seqcount_retry->__read_seqcount_retry 中retry的条件如下。如果前两次读的s->sequence 不一样,就继续读。
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{
    return unlikely(s->sequence != start);
}

不一样的原因就是sched_clock在读的时候update_clock_read_data也在更新s->sequence。因此会持续读,直到update_clock_read_data 更新完毕
0 0