Clock source is used to provide a time baseline for the Linux kernel. If you use the Linux date command to obtain the current time, the kernel will read the current clock source, convert it, and return a suitable time unit to the user space. On the hardware layer, it is usually implemented as a counter driven by a fixed clock frequency. The counter can only be monotonically increased until it overflows. The clock source is the basis for Kernel timing. When the system starts, the kernel obtains the current time through the hardware RTC. After that, in most cases, the Kernel updates the real-time information (Wall time) through the selected clock source, instead of reading the RTC time. The kernel code tree in this section is based on v3.4.10.
/*************************************** **************************************** **********************/
Statement: the content of this blog is created at http://blog.csdn.net/droidphone. please refer to it for help. Thank you!
/*************************************** **************************************** **********************/
1. struct clocksource Structure
The kernel uses a clocksource structure to abstract the real clock source. Now we start with the clock source data structure. Its definition is as follows:
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_DATAstruct arch_clocksource_data archdata;#endifconst 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;
We only focus on several important fields in clocksource.
1.1 rating: the precision of the clock source can be multiple clock sources under the same device. The precision of each clock source is determined by the clock frequency that drives it, for example, if a 10 MHz clock is used to drive a clock source, its precision is 100ns. There is a rating field in the clocksource structure, which represents the precision range of the clock source. Its value range is as follows:
- 1--99: it is not suitable for use as the actual clock source and is only used for the startup process or for testing;
- 100--199: It is basically available and can be used as a real clock source, but not recommended;
- 200--299: high precision, can be used as a real clock source;
- 300--399: Very good, accurate clock source;
- 400--499: Ideal clock source. If possible, you must use it as the clock source;
1.2 The read callback function does not interrupt the clock source itself. To obtain the current count of the clock source, you can only obtain the current Count value by actively calling its read callback function, note that only the Count value can be obtained here, that is, the so-called cycle. To obtain the corresponding time, you must use the clocksource mult and shift fields for conversion calculation. 1.3 because the value read from clocksource is a cycle count value, to convert it to time, we must know the clock frequency f of the clocksource driver, A simple computation can be completed:
T = Cycle/F;
However, clocksource does not save the frequency f of the clock. Because the above formula is used for computation, floating-point computation is required, which is not allowed in the kernel. Therefore, the kernel uses another work und. Based on the clock frequency and expected precision, two auxiliary constants mult and shift are calculated in advance, and then cycle and T are converted using the following formula:
T = (cycle * mult)> shift;
As long as we guarantee:
F = (1 <shift)/mult;
The kernel uses 64-bit for this conversion calculation:
static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift){ return ((u64) cycles * mult) >> shift;}
Considering the conversion accuracy, the larger the mult value is, the better. However, in order to avoid overflow in the calculation process, the mult value cannot be too large. For this reason, the kernel assumes that the maximum time after the cycle count value is converted is 10 minutes (600 seconds). The main consideration is that after the CPU enters the idle status, the time information will not be updated, as long as the idle is exited within 10 minutes, the cycle count value of clocksource can be correctly converted to the corresponding time, and then the system's time information can be correctly updated. Of course, the final result is not necessarily 10 minutes. It is calculated by clocksource_max_deferment and saved in the max_idle_ns field. The tickless code should consider this value to avoid the no_hz configuration environment, the system maintains the idle status for a long time. In this way, we can calculate the appropriate mult and shift values from the time value of the assumption of 10 minutes. 2. clocksource registration and initialization usually, clocksource must notify the kernel of its working clock frequency through the clocksource_register_hz function in the initialization phase. The call process is as follows:
As you can see, most of the final work will be completed by _ clocksource_register_scale. This function is used to calculate the mult and shift values first, and then according to the mult and shift values, the maximum idle time that clocksource can accept is obtained through clocksource_max_deferment and recorded in the max_idle_ns field of clocksource. The clocksource_enqueue function is responsible for attaching the clocksource to the global linked list clocksource_list Based on the rating size of clocksource. The larger the rating value, the higher the position on the linked list. Every time a new clocksource is registered, the clocksource_select function is called. It selects the best clocksource according to the rating value, records it in the global variable curr_clocksource, and then notifies timekeeping through the timekeeping_notify function, the current clocksource has changed. For timekeeping, I will explain it in the subsequent blog. 3. clocksource Watchdog
The system may register multiple clocksources at the same time. The precision and stability of each clocksource are different. to filter these registered clocksources, the kernel enables a timer to monitor the performance of these clocksources, set the timer cycle to 0.5 seconds:
#define WATCHDOG_INTERVAL (HZ >> 1)#define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 4)
When a new clocksource is registered, in addition to the global linked list clocksource_list, it is also mounted to a watchdog linked list: watchdog_list. The timer periodically checks clocksource on watchdog_list (0.5 seconds). The value of watchdog_threshold is defined as 0.0625 seconds. If the value is within 0.5 seconds, the clocksource deviation is greater than this value, indicating that the clocksource is unstable. The timer callback function marks the clocksource through the clocksource_watchdog_kthread and changes its rate to 0, indicating that the precision is very poor.
4. Brief clocksource creation process
In the system startup phase, the kernel registers a clocksource Based on jiffies. The code is located in kernel/time/jiffies. C:
struct clocksource clocksource_jiffies = {.name= "jiffies",.rating= 1, /* lowest valid rating*/.read= jiffies_read,.mask= 0xffffffff, /*32bits*/.mult= NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */.shift= JIFFIES_SHIFT,};......static int __init init_jiffies_clocksource(void){return clocksource_register(&clocksource_jiffies);}core_initcall(init_jiffies_clocksource);
Its precision is only 1/Hz seconds, and the rating value is 1. If the platform code does not provide the customized clocksource_default_clock function, it will return the clocksource:
struct clocksource * __init __weak clocksource_default_clock(void){return &clocksource_jiffies;}
Then, after initialization, The clocksource Code sets the global variable curr_clocksource as the clocksource:
static int __init clocksource_done_booting(void){ ......curr_clocksource = clocksource_default_clock(); ......finished_booting = 1; ......clocksource_select(); ......return 0;}fs_initcall(clocksource_done_booting);
Of course, if the platform-level code registers the real hardware clocksource during initialization, after the clocksource_select () function, curr_clocksource will be set as the most appropriate clocksource. If the clocksource_select function deems it necessary to switch a better clock source, it will notify the timekeeping system through timekeeping_notify and use the new clocksource for time counting and updating.