感谢支持
我们一直在努力

Linux hrtimer的实现

1. Linux hrtimer的实现方案


Linux hrtimer的实现是依赖硬件(通过可编程定时器来实现)的支持的,而且此定时器有自己的专用寄存器, 硬中断和频率。比如我的板子上的对应参数如下:


Timer at Vir:0xE0100200 = Phy:0xE0100200, using Irq:27, at Freq:250000000,由此可见,其频率为250MHz,所以其精度为:1/250000000=4ns,比系统时钟jiffy(HZ=100,精度为10ms)的精度高得太多了。可是支持此高精度timer是需要付出硬件成本的。即它是一个硬件时钟。这里所说的硬件时钟特指的是硬件计时器时钟


2. 硬件时钟 数据结构
  和硬件计时器(本文又称作硬件时钟,区别于软件时钟)相关的数据结构主要有两个:
  struct clocksource :对硬件设备的抽象,描述时钟源信息


  1. struct clocksource {  

  2.     /* 

  3.      * First part of structure is read mostly 

  4.      */  

  5.     char *name;  

  6.     struct list_head list;  

  7.     int rating;  

  8.     cycle_t (*read)(struct clocksource *cs);  

  9.     int (*enable)(struct clocksource *cs);  

  10.     void (*disable)(struct clocksource *cs);  

  11.     cycle_t mask;  

  12.     u32 mult;  

  13.     u32 shift;  

  14.     u64 max_idle_ns;  

  15.     unsigned long flags;  

  16.     cycle_t (*vread)(void);  

  17.     void (*suspend)(struct clocksource *cs);  

  18.     void (*resume)(struct clocksource *cs);  

  19. #ifdef CONFIG_IA64   

  20.     void *fsys_mmio;        /* used by fsyscall asm code */  

  21. #define CLKSRC_FSYS_MMIO_SET(mmio, addr)      ((mmio) = (addr))   

  22. #else   

  23. #define CLKSRC_FSYS_MMIO_SET(mmio, addr)      do { } while (0)   

  24. #endif   

  25.   

  26.     /* 

  27.      * Second part is written at each timer interrupt 

  28.      * Keep it in a different cache line to dirty no 

  29.      * more than one cache line. 

  30.      */  

  31.     cycle_t cycle_last ____cacheline_aligned_in_smp;  

  32.   

  33. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG   

  34.     /* Watchdog related data, used by the framework */  

  35.     struct list_head wd_list;  

  36.     cycle_t wd_last;  

  37. #endif   

  38. };  

struct clock_event_device :时钟的事件信息,包括当硬件时钟中断发生时要执行那些操作(实际上保存了相应函数的指针)。本文将该结构称作为“时钟事件设备”。


  1. /** 

  2.  * struct clock_event_device – clock event device descriptor 

  3.  * @name:       ptr to clock event name 

  4.  * @features:       features 

  5.  * @max_delta_ns:   maximum delta value in ns 

  6.  * @min_delta_ns:   minimum delta value in ns 

  7.  * @mult:       nanosecond to cycles multiplier 

  8.  * @shift:      nanoseconds to cycles divisor (power of two) 

  9.  * @rating:     variable to rate clock event devices 

  10.  * @irq:        IRQ number (only for non CPU local devices) 

  11.  * @cpumask:        cpumask to indicate for which CPUs this device works 

  12.  * @set_next_event: set next event function 

  13.  * @set_mode:       set mode function 

  14.  * @event_handler:  Assigned by the framework to be called by the low 

  15.  *          level handler of the event source 

  16.  * @broadcast:      function to broadcast events 

  17.  * @list:       list head for the management code 

  18.  * @mode:       operating mode assigned by the management code 

  19.  * @next_event:     local storage for the next event in oneshot mode 

  20.  * @retries:        number of forced programming retries 

  21.  */  

  22. struct clock_event_device {  

  23.     const char      *name;  

  24.     unsigned int        features;  

  25.     u64         max_delta_ns;  

  26.     u64         min_delta_ns;  

  27.     u32         mult;  

  28.     u32         shift;  

  29.     int         rating;  

  30.     int         irq;  

  31.     const struct cpumask    *cpumask;  

  32.     int         (*set_next_event)(unsigned long evt,  

  33.                           struct clock_event_device *);  

  34.     void            (*set_mode)(enum clock_event_mode mode,  

  35.                         struct clock_event_device *);  

  36.     void            (*event_handler)(struct clock_event_device *);  

  37.     void            (*broadcast)(const struct cpumask *mask);  

  38.     struct list_head    list;  

  39.     enum clock_event_mode   mode;  

  40.     ktime_t         next_event;  

  41.     unsigned long       retries;  

  42. };  

  43.    

上述两个结构内核源代码中有较详细的注解,分别位于文件 clocksource.h 和 clockchips.h 中。需要特别注意的是结构 clock_event_device 的成员 event_handler ,它指定了当硬件时钟中断发生时,内核应该执行那些操作,也就是真正的时钟中断处理函数。


Linux 内核维护了两个链表,分别存储了系统中所有时钟源的信息和时钟事件设备的信息。这两个链表的表头在内核中分别是 clocksource_list 和 clockevent_devices 。 

3. hrtimer是如何实现的呢?


    下文就为之一一描述。


3.1 初始化hrtimer硬件定时器


3.1.1 设置硬件中断


     前面已经看到,它有一个硬件中断,为了使此硬件中断能正常工作,肯定需要设置一个硬件中断,其参考代码如下: 


  1. static unsigned long my_timer_irqnbr = 25;  //硬件中断号   

  2. static struct irqaction my_timer_irqaction = {  

  3.     .name       = “My HrTimer”,  

  4.     .flags      = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,  

  5.     .handler    = my_timer_interrupt_handler, //中断处理函数   

  6. };  

  7.   

  8. setup_irq(my_timer_irqnbr, &my_timer_irqaction);  

设置中断之后,中断处理函数也有了。 


3.1.2 初始化硬件时钟相关寄存器并注册此硬件时钟到系统中


  1. static struct clocksource myclocksource = {  

  2.     .name   = “my_hrtimer_src”,  

  3.     .rating = 300,  

  4.     .read   = my_get_cycles, //读取COUNT寄存器以获取cycle value   

  5.     .mask   = CLOCKSOURCE_MASK(64),  

  6.     .flags  = CLOCK_SOURCE_IS_CONTINUOUS,  

  7. };  

  8.   

  9. static void __init my_clocksource_init(void)  

  10. {  

  11.     unsigned long ctrl = 0;  

  12.     unsigned long count = (my_timer_freq / HZ);  

  13.         …  

  14.     writel(count, my_timer_vaddr + MY_TIMER_COMPARATOR_LOW);  

  15.     writel(count, my_timer_vaddr + MY_TIMER_AUTO_INCREMENT);  

  16.     ctrl = (MY_TIMER_CTRL_IRQ_ENA   | MY_TIMER_CTRL_COMP_ENA |  

  17.             MY_TIMER_CTRL_TIMER_ENA | MY_TIMER_CTRL_AUTO_INC);  

  18.     writel(ctrl, my_timer_vaddr + MY_TIMER_CONTROL);  

  19.         …  

  20.     clocksource_calc_mult_shift(&myclocksource, my_timer_freq, 4);  

  21.           

  22.         //向系统注册我的硬件时钟,即把它加入clocksource_list   

  23.     clocksource_register(&myclocksource);  

  24. }  

3.1.3 初始化时钟事件设备并注册到系统中


  1. static struct clock_event_device myclockevent = {  

  2.     .name       = “my_timer_evt”,  

  3.     .features       = CLOCK_EVT_FEAT_PERIODIC,  

  4.     .set_mode       = my_set_mode,  //通过写寄存器设置clock_event_mode   

  5.     .set_next_event = my_set_next_event, // 通过写寄存器写下一个事件   

  6.     .rating     = 300,  

  7.     .cpumask        = cpu_all_mask,  

  8. };  

  9.   

  10. static void __init my_clockevents_init(unsigned int timer_irq)  

  11. {  

  12.     myclockevent.irq = timer_irq;  

  13.   

  14.     clockevents_calc_mult_shift(&myclockevent, my_timer_freq, 4);  

  15.     myclockevent.max_delta_ns = clockevent_delta2ns(0xffffffff, &myclockevent);  

  16.     myclockevent.min_delta_ns = clockevent_delta2ns(0xf, &myclockevent);  

  17.     //注册我的时钟事件设备,即把它加入clockevent_devices链表   

  18.     clockevents_register_device(&myclockevent);  

  19. }  

3.2 硬件中处理函数my_timer_interrupt_handler


  1. static irqreturn_t my_timer_interrupt_handler(int irq, void *dev_id)  

  2. {  

  3.     struct clock_event_device *evt = &myclockevent;  

  4.     /* clear the interrupt */  

  5.     writel(value, register_addr);  

  6.     evt->event_handler(evt);  

  7.     return IRQ_HANDLED;  

  8. }  

硬件中断处理函数很简单,它直接调用clockevent的event_handler函数。前面的初始化中并没有初始化此event_handler,很显然是在使用过程中进行动态初始化的。下面看看hrtimer中是如何初始化此event_handler的。


4. hrtimer如何初始化clock_event_device的event_handler?


hrtimer的中断处理函数,很自然地想到了hrtimer_interrupt,哪这个东东与clock_event_device有关系吗?


此软中断TIMER_SOFTIRQ在run_local_timers函数中通过调用raise_softirq(TIMER_SOFTIRQ);来触发。(注:raise_softirq->raise_softirq_irqoff->__raise_softirq_irqoff)


init_timers(中调用open_softirq(TIMER_SOFTIRQ, run_timer_softirq);)
run_timer_softirq->
hrtimer_run_pending(Called from timer softirq every jiffy, expire hrtimers,check如果hrtimer_hres_enabled is on<=1>,则执行下面的代码切换到高精度模式)->
hrtimer_switch_to_hres->
tick_init_highres->
tick_switch_to_oneshot(hrtimer_interrupt)
<把hrtimer_interrupt赋值给dev->event_handler,即dev->event_handler = handler;>


看到没有?在每一次时钟软中断处理函数中,都会尝试把hrtimer切换到高精度模式,如果满足条件,就切换,切换之后高精度模式就被激活了,在hrtimer_run_pending检查是否被激活,如果被激活了,下面的代码就不用执行了。


5. hrtimer高精度模式下真正的中断处理函数


    hrtimer_interrupt 


6. hrtimer高精度式的触发过程


以下以nanosleep为例:


SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, struct timespec __user *, rmtp)->
hrtimer_nanosleep->
do_nanosleep->
hrtimer_start_expires->
hrtimer_start_range_ns->
__hrtimer_start_range_ns->
enqueue_hrtimer(insert into rb_tree) then  hrtimer_enqueue_reprogram-> hrtimer_reprogram->
tick_program_event->
tick_dev_program_event->
clockevents_program_event->
dev->set_next_event((unsigned long) clc, dev)<调用my clock_event_device的set_next_event方法设置register>

赞(0) 打赏
转载请注明出处:服务器评测 » Linux hrtimer的实现
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏