1. 程式人生 > >Linux核心時間管理子系統——時鐘源

Linux核心時間管理子系統——時鐘源

struct clocksource {
        /*
         * Hotpath data, fits in a single cache line when the
         * clocksource itself is cacheline aligned.
         */
        cycle_t (*read)(struct clocksource *cs);
        cycle_t cycle_last;
        cycle_t mask;
        u32 mult;
        u32 shift;
        u64 max_idle_ns;
        u32 maxadj;
#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
        struct arch_clocksource_data archdata;
#endif

        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
} ____cacheline_aligned;

read()

返回當前時鐘源的計數值,即cycle數。

rating

時鐘源的等級,越高越好。

mult和shift

用於將時鐘源的計數值換算為納秒,即(cycle * mult) >> shift 可以得到相應的納秒數。

mask

時鐘源的計數值用型別cycle_t表示,該型別其實就是無符號64位數。但是時鐘源實際的計數範圍可能小於64位,mask的作用就是表示真實的計數範圍。對於一個32位的時鐘源硬體,mask等於0xffffffff,即32個bit 1。這種方法的好處是即使時鐘源發生溢位,一樣可以通過簡單的計算得出實際經歷的cycle數。如下所示,假設上次讀時鐘源得到的計數值是cycle_last,這次讀取的計數值是cycle_now,則經歷的cycle數等於: (cycle_now - cycle_last) & mask 對於一個32位的時鐘源,假設cycle_last=0xffffffff,cycle_now=0,即發生了溢位,通過上面的等式依然可以得到經歷了一次cycle。

max_idle_ns

表示兩次讀取之間所允許的最大時間間隔。當兩次讀取間隔超過mask+1時就無法正確計算出經歷的cycle數,因此該數值需要小於mask+1個cycle所對應的納秒數。另一個限制因素來自於clocksource_cyc2ns()函式。該函式用於將cycle數換算成納秒。由於函式返回值是有符號64位數(s64),兩次讀取間隔的cycle數不能使最終的計算結果溢位。

時鐘源的註冊

核心提供了多個函式用於註冊新的時鐘源。其中帶_hz或_khz字尾的函式可以根據hz/khz引數自動計算mult和shift,簡化了呼叫者的負擔。
extern int clocksource_register(struct clocksource*);
static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
註冊函式完成下面的工作: 1. 將新的clock source掛在連結串列clocksource_list上。連結串列上的時鐘源按rating從大到小排序。 2. 根據clock source的flags決定是否需要將新的clock source掛在連結串列watchdog_list上。詳見clock source watchdog一節。 3. 選擇具有最高rating的時鐘源並將其設定為當前時鐘源(curr_clocksource指向當前使用的時鐘源)。使用者可以通過sysfs強制指定某個時鐘源,詳見使用者介面一節。

Clock Source Watchdog

核心選擇rating最高的時鐘源作為watchdog時鐘源監測其他時鐘源的誤差。如果新註冊的時鐘源的flags包含CLOCK_SOURCE_MUST_VERIFY,表示該時鐘源需要經過watchdog的監測。如果兩者之間在0.5s內的誤差大於0.0625秒,則新註冊的時鐘源被認為是unstable的。如果誤差小於該標準,且滿足下面的情況,則新註冊的時鐘源被標記為CLOCK_SOURCE_VALID_FOR_HRES,即高精度時鐘。 1. 新註冊的時鐘源的flags包含CLOCK_SOURCE_IS_CONTINUOUS。 2. watchdog時鐘源的flags包含CLOCK_SOURCE_VALID_FOR_HRES。

時鐘源的其他API

下面的API用於suspend和resume所有註冊過的時鐘源。
void clocksource_suspend(void)
void clocksource_resume(void)

下面的API用於將時鐘源標記為unstable

void clocksource_mark_unstable(struct clocksource *cs)

核心啟動過程與時鐘源

clocksource_done_booting執行前

核心完成如下工作:
1. 各種時鐘源的註冊。 2. 時鐘源watchdog對需要監測的時鐘源進行檢測。 3. 對於不符合標準的時鐘源,在clocksource_watchdog中該時鐘源被mark成unstable。由於此時finished_booting=0,因此不會啟動核心執行緒clocksource_watchdog_kthread。該執行緒的作用將在後面描述。

clocksource_done_booting執行中

在該函式中finished_booting被標記為1。
直接執行clocksource_watchdog_kthread,該函式將所有unstable的時鐘源的rating設定為0,並重新呼叫clocksource_select選擇當前使用的時鐘源。

clocksource_done_booting執行後

clocksource_done_booting函式被標記為fs_initcall,表示該函式將在subsys_initcall之後,device_initcall執行之前被執行。
此時finished_booting已經被標記為1,當任何時鐘源被標記為unstable時,將啟動核心執行緒clocksource_watchdog_kthread,如前所述,函式將重新設定時鐘源的rating並選擇當前的時鐘源。

sysfs介面

當前可用的時鐘源 /sys/devices/system/clocksource/clocksource0/available_clocksource
當前所用的時鐘源 /sys/devices/system/clocksource/clocksource0/current_clocksource


相關推薦

no