Linux hrtimer的實現

來源:互聯網
上載者:User

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 :對硬體裝置的抽象,描述時鐘源資訊

struct clocksource {/* * First part of structure is read mostly */char *name;struct list_head list;int rating;cycle_t (*read)(struct clocksource *cs);int (*enable)(struct clocksource *cs);void (*disable)(struct clocksource *cs);cycle_t mask;u32 mult;u32 shift;u64 max_idle_ns;unsigned long flags;cycle_t (*vread)(void);void (*suspend)(struct clocksource *cs);void (*resume)(struct clocksource *cs);#ifdef CONFIG_IA64void *fsys_mmio;        /* used by fsyscall asm code */#define CLKSRC_FSYS_MMIO_SET(mmio, addr)      ((mmio) = (addr))#else#define CLKSRC_FSYS_MMIO_SET(mmio, addr)      do { } while (0)#endif/* * Second part is written at each timer interrupt * Keep it in a different cache line to dirty no * more than one cache line. */cycle_t cycle_last ____cacheline_aligned_in_smp;#ifdef CONFIG_CLOCKSOURCE_WATCHDOG/* Watchdog related data, used by the framework */struct list_head wd_list;cycle_t wd_last;#endif};

  struct clock_event_device :時鐘的事件資訊,包括當硬體時鐘中斷髮生時要執行那些操作(實際上儲存了相應函數的指標)。本文將該結構稱作為“時鐘事件裝置”。

/** * struct clock_event_device - clock event device descriptor * @name:ptr to clock event name * @features:features * @max_delta_ns:maximum delta value in ns * @min_delta_ns:minimum delta value in ns * @mult:nanosecond to cycles multiplier * @shift:nanoseconds to cycles divisor (power of two) * @rating:variable to rate clock event devices * @irq:IRQ number (only for non CPU local devices) * @cpumask:cpumask to indicate for which CPUs this device works * @set_next_event:set next event function * @set_mode:set mode function * @event_handler:Assigned by the framework to be called by the low *level handler of the event source * @broadcast:function to broadcast events * @list:list head for the management code * @mode:operating mode assigned by the management code * @next_event:local storage for the next event in oneshot mode * @retries:number of forced programming retries */struct clock_event_device {const char*name;unsigned intfeatures;u64max_delta_ns;u64min_delta_ns;u32mult;u32shift;intrating;intirq;const struct cpumask*cpumask;int(*set_next_event)(unsigned long evt,  struct clock_event_device *);void(*set_mode)(enum clock_event_mode mode,    struct clock_event_device *);void(*event_handler)(struct clock_event_device *);void(*broadcast)(const struct cpumask *mask);struct list_headlist;enum clock_event_modemode;ktime_tnext_event;unsigned longretries;}; 

  上述兩個結構核心原始碼中有較詳細的註解,分別位於檔案 clocksource.h 和 clockchips.h 中。需要特別注意的是結構 clock_event_device 的成員 event_handler ,它指定了當硬體時鐘中斷髮生時,核心應該執行那些操作,也就是真正的時鐘中斷處理函數。
  Linux 核心維護了兩個鏈表,分別儲存了系統中所有時鐘源的資訊和時鐘事件裝置的資訊。這兩個鏈表的表頭在核心中分別是 clocksource_list 和 clockevent_devices 。

 

3. hrtimer是如何?的呢?

    下文就為之一一描述。

3.1 初始化hrtimer硬體定時器

3.1.1 設定硬體中斷

     前面已經看到,它有一個硬體中斷,為了使此硬體中斷能正常工作,肯定需要設定一個硬體中斷,其參考代碼如下: 

static unsigned long my_timer_irqnbr = 25;  //硬體中斷號static struct irqaction my_timer_irqaction = {.name= "My HrTimer",.flags= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,.handler= my_timer_interrupt_handler, //中斷處理函數};setup_irq(my_timer_irqnbr, &my_timer_irqaction);

     設定中斷之後,中斷處理函數也有了。

 

3.1.2 初始化硬體時鐘相關寄存器並註冊此硬體時鐘到系統中

 

static struct clocksource myclocksource = {.name= "my_hrtimer_src",.rating = 300,.read= my_get_cycles, //讀取COUNT寄存器以擷取cycle value.mask= CLOCKSOURCE_MASK(64),.flags= CLOCK_SOURCE_IS_CONTINUOUS,};static void __init my_clocksource_init(void){unsigned long ctrl = 0;unsigned long count = (my_timer_freq / HZ);        ...writel(count, my_timer_vaddr + MY_TIMER_COMPARATOR_LOW);writel(count, my_timer_vaddr + MY_TIMER_AUTO_INCREMENT);ctrl = (MY_TIMER_CTRL_IRQ_ENA   | MY_TIMER_CTRL_COMP_ENA |        MY_TIMER_CTRL_TIMER_ENA | MY_TIMER_CTRL_AUTO_INC);writel(ctrl, my_timer_vaddr + MY_TIMER_CONTROL);        ...clocksource_calc_mult_shift(&myclocksource, my_timer_freq, 4);                //向系統註冊我的硬體時鐘,即把它加入clocksource_listclocksource_register(&myclocksource);}

3.1.3 初始化時鐘事件裝置並註冊到系統中

static struct clock_event_device myclockevent = {.name= "my_timer_evt",.features= CLOCK_EVT_FEAT_PERIODIC,.set_mode= my_set_mode,  //通過寫寄存器設定clock_event_mode.set_next_event= my_set_next_event, // 通過寫寄存器寫下一個事件.rating= 300,.cpumask= cpu_all_mask,};static void __init my_clockevents_init(unsigned int timer_irq){    myclockevent.irq = timer_irq;    clockevents_calc_mult_shift(&myclockevent, my_timer_freq, 4);    myclockevent.max_delta_ns = clockevent_delta2ns(0xffffffff, &myclockevent);    myclockevent.min_delta_ns = clockevent_delta2ns(0xf, &myclockevent);    //註冊我的時鐘事件裝置,即把它加入clockevent_devices鏈表    clockevents_register_device(&myclockevent);}

3.2 硬體中處理函數my_timer_interrupt_handler

static irqreturn_t my_timer_interrupt_handler(int irq, void *dev_id){struct clock_event_device *evt = &myclockevent;/* clear the interrupt */writel(value, register_addr);evt->event_handler(evt);return IRQ_HANDLED;}

硬體中斷處理函數很簡單,它直接調用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>

 

 

 

 

 

 

 

          
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.