其他分享
首页 > 其他分享> > riscv_clocksource

riscv_clocksource

作者:互联网

 

 

static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs)
{
        return get_cycles64();
}

clocksource

clocksource 提供了对不同软硬件时钟的抽象。可以理解为时间源,为 kernel 提供当前时间。

struct clocksource {
    cycle_t (*read)(struct clocksource *cs);            // 指向读取时钟的函数
    cycle_t mask;                                       // 能够表示的 cycle 上限,通常是 32/64 位的全 f,做与操作可以避免对 overflow 进行专门处理
    u32 mult;                                           // 将时间源的计数单位 (cycle_t) 转换为 ns
    u32 shift;                                          // 换算公式为 (cycles * mult) >> shift
    u64 max_idle_ns;                                    // 允许的最大空闲时间,单位 ns。当设置 CONFIG_NO_HZ 时,使用动态 tick,不限制 kernel 的睡眠时间,需要进行限制
    u32 maxadj;                                         // 允许的最大调整值,避免转换时 overflow
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
    struct arch_clocksource_data archdata;              // 架构专有(目前只有 x86 和 ia64)。
#endif
    u64 max_cycles;                                     // 设置了 cycle 上限,避免换算时溢出
    const char *name;                                   // 时间源名称
    struct list_head list;                              // 注册了该时间源?
    int rating;                                         // 优先级
    int (*enable)(struct clocksource *cs);              // 启用时间源函数
    void (*disable)(struct clocksource *cs);            // 停用时间源函数
    unsigned long flags;
    void (*suspend)(struct clocksource *cs);            // 暂停时间源函数
    void (*resume)(struct clocksource *cs);             // 恢复时间源函数

    /* private: */
#ifdef CONFIG_CLOCKSOURCE_WATCHDOG                      // 用于监控时间源,校验时间是否准确
    /* Watchdog related data, used by the framework */
    struct list_head wd_list;
    cycle_t cs_last;
    cycle_t wd_last;
#endif
    struct module *owner;                               // 指向拥有该时间源的内核模块
};

 

其中 rating 表示了时间源的准确度,它将作为 Linux 选择时钟源时的优先级:

只有 rating 最高的时间源会被选用

 

 

riscv_clocksource

 

static struct clocksource riscv_clocksource = {
        .name           = "riscv_clocksource",
        .rating         = 300,
        .mask           = CLOCKSOURCE_MASK(64),
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
        .read           = riscv_clocksource_rdtime,
};

 

riscv_timer_init_dt注册 riscv clocksource

 

riscv_timer_init_dt
     clocksource_register_hz(&riscv_clocksource, riscv_timebase);
      sched_clock_register(riscv_sched_clock, 64, riscv_timebase);

 

 

 

jiffies clocksource

在 Linux 中作为软件维护的时钟。表示一小段短暂而不确定的时间。

属于低精度时间源,因为没有 CLOCK_SOURCE_VALID_FOR_HRES 的 flag,所以不会出现在 available_clocksource 中。

  static struct clocksource clocksource_jiffies = {
  .name = "jiffies",
  .rating = 1, /* lowest valid rating*/ // 优先级最低
  .read = jiffies_read, // 读时返回 jiffies
  .mask = CLOCKSOURCE_MASK(32),
  .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
  .shift = JIFFIES_SHIFT, // NSEC_PER_JIFFY 和 JIFFIES_SHIFT 由 CONFIG_HZ 决定
  .max_cycles = 10,
  };

Linux 用全局变量 jiffies / jiffies_64 来存放系统启动后经过的 jiffy 数目:

  extern u64 __jiffy_data jiffies_64; // x86_64 下使用,非原子,读时需要加锁,如 get_jiffies_64 用了 seqlock
  extern unsigned long volatile __jiffy_data jiffies; // x86_32 下使用

根据 arch/x86/kernel/vmlinux.lds.S,在 32 位下,jiffies 指向 jiffies_64 的低 32 位。

每当收到 clock_event_device (后详)发出的中断时,会调用其 handler ,即 tick_handle_periodic ,于是有

tick_handle_periodic => tick_periodic => do_timer => jiffies_64 += ticks

由于 do_timer 的参数为 1,因此 jiffies_64 += 1。而根据 tick_setup_device 中 tick_period = ktime_set(0, NSEC_PER_SEC / HZ) ,表示 tick_device 每个 tick 间隔为 1 / HZ 秒。于是每个 jiffies 代表的时间为 1/ HZ 秒,系统启动至今所经过的秒数 = jiffies_64 / HZ。HZ 由 CONFIG_HZ 决定,而 CONFIG_HZ 由 CONFIG_HZ_* 决定,有 100/250/300/1000 可选,一般为 1000。所以每隔 1 毫秒,jiffies "时钟" 就会加一,1 jiffy 等于 1 毫秒。

从 jiffies 我们可以发现,时钟源的不仅能够通过读取硬件时钟源来实现,还能通过 tick_device,更本质上来说是定时器来实现

标签:HZ,struct,riscv,jiffies,clocksource,64
来源: https://www.cnblogs.com/dream397/p/15724445.html