Linux内核深入理解定时器和时间管理(5):clockevents 框架
Linux内核深入理解定时器和时间管理 clockevents 框架
rtoax 2021年3月
在原文基础上,增加5.10.13内核源码相关内容。
1. Introduction to the clockevents
framework
This is fifth part of the chapter which describes timers and time management related stuff in the Linux kernel. As you might noted from the title of this part, the clockevents
framework will be discussed. We already saw one framework in the second part of this chapter. It was clocksource
framework. Both of these frameworks represent timekeeping abstractions in the Linux kernel.
At first let’s refresh your memory and try to remember what is it clocksource
framework and and what its purpose. The main goal of the clocksource
framework is to provide timeline
. As described in the documentation:
For example issuing the command ‘date’ on a Linux system will eventually read the clock source to determine exactly what time it is.
The Linux kernel supports many different clock sources. You can find some of them in the drivers/clocksource. For example old good Intel 8253 - programmable interval timer with 1193182
Hz frequency, yet another one - ACPI PM timer with 3579545
Hz frequency. Besides the drivers/clocksource directory, each architecture may provide own architecture-specific clock sources. For example x86 architecture provides High Precision Event Timer, or for example powerpc provides access to the processor timer through timebase
register.
Each clock source provides monotonic atomic counter(递增的原子计数). As I already wrote, the Linux kernel supports a huge set of different clock source and each clock source has own parameters like frequency. The main goal of the clocksource
framework is to provide API to select best available clock source in the system i.e. a clock source with the highest frequency. Additional goal of the clocksource
framework is to represent an atomic counter provided by a clock source in human units. In this time, nanoseconds are the favorite choice for the time value units of the given clock source in the Linux kernel.
The clocksource
framework represented by the clocksource
structure which is defined in the include/linux/clocksource.h header code file which contains name
of a clock source, rating of certain clock source in the system (a clock source with the higher frequency has the biggest rating in the system), list
of all registered clock source in the system, enable
and disable
fields to enable and disable a clock source, pointer to the read
function which must return an atomic counter of a clock source and etc.
Additionally the clocksource
structure provides two fields: mult
and shift
which are needed for translation of an atomic counter which is provided by a certain clock source to the human units, i.e. nanoseconds. Translation occurs via following formula:
ns ~= (clocksource * mult) >> shift
As we already know, besides the clocksource
structure, the clocksource
framework provides an API for registration of clock source with different frequency scale factor:
static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
A clock source unregistration:
int clocksource_unregister(struct clocksource *cs)
and etc.
Additionally to the clocksource
framework, the Linux kernel provides clockevents
framework. As described in the documentation:
Clock events are the conceptual reverse of clock sources
Main goal of the clockevents
framework is to manage clock event devices or in other words - to manage devices that allow to register an event or in other words interrupt that is going to happen at a defined point of time in the future.
Now we know a little about the clockevents
framework in the Linux kernel, and now time is to see on it API.
2. API of clockevents
framework
The main structure which described a clock event device is clock_event_device
structure. This structure is defined in the include/linux/clockchips.h header file and contains a huge set of fields. as well as the clocksource
structure it has name
fields which contains human readable name of a clock event device, for example local APIC timer:
/** The local apic timer can be used for any function which is CPU local.*/
static struct clock_event_device lapic_clockevent = {.name = "lapic",.features = CLOCK_EVT_FEAT_PERIODIC |CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP| CLOCK_EVT_FEAT_DUMMY,.shift = 32,.set_state_shutdown = lapic_timer_shutdown,.set_state_periodic = lapic_timer_set_periodic,.set_state_oneshot = lapic_timer_set_oneshot,.set_state_oneshot_stopped = lapic_timer_shutdown,.set_next_event = lapic_next_event,.broadcast = lapic_timer_broadcast,.rating = 100,.irq = -1,
};
Addresses of the event_handler
, set_next_event
, next_event
functions for a certain clock event device which are an interrupt handler, setter of next event and local storage for next event respectively. Yet another field of the clock_event_device
structure is - features
field. Its value maybe on of the following generic features:
#define CLOCK_EVT_FEAT_PERIODIC 0x000001
#define CLOCK_EVT_FEAT_ONESHOT 0x000002
Where the CLOCK_EVT_FEAT_PERIODIC
represents device which may be programmed to generate events periodically. The CLOCK_EVT_FEAT_ONESHOT
represents device which may generate an event only once. Besides these two features, there are also architecture-specific features. For example x86_64 supports two additional features:
#define CLOCK_EVT_FEAT_C3STOP 0x000008
The first CLOCK_EVT_FEAT_C3STOP
means that a clock event device will be stopped in the C3 state. Additionally the clock_event_device
structure has mult
and shift
fields as well as clocksource
structure. The clocksource
structure also contains other fields, but we will consider it later.
After we considered part of the clock_event_device
structure, time is to look at the API
of the clockevents
framework. To work with a clock event device, first of all we need to initialize clock_event_device
structure and register a clock events device. The clockevents
framework provides following API
for registration of clock event devices:
void clockevents_register_device(struct clock_event_device *dev)
{.........
}
This function defined in the kernel/time/clockevents.c source code file and as we may see, the clockevents_register_device
function takes only one parameter:
- address of a
clock_event_device
structure which represents a clock event device.
So, to register a clock event device, at first we need to initialize clock_event_device
structure with parameters of a certain clock event device. Let’s take a look at one random clock event device in the Linux kernel source code.
We can find one in the drivers/clocksource directory or try to take a look at an architecture-specific clock event device. Let’s take for example - Periodic Interval Timer (PIT) for at91sam926x. You can find its implementation in the drivers/clocksource.
First of all let’s look at initialization of the clock_event_device
structure. This occurs in the at91sam926x_pit_common_init
function:
struct pit_data {......struct clock_event_device clkevt;......
};static void __init at91sam926x_pit_common_init(struct pit_data *data)
{.........data->clkevt.name = "pit";data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;data->clkevt.shift = 32;data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift);data->clkevt.rating = 100;data->clkevt.cpumask = cpumask_of(0);data->clkevt.set_state_shutdown = pit_clkevt_shutdown;data->clkevt.set_state_periodic = pit_clkevt_set_periodic;data->clkevt.resume = at91sam926x_pit_resume;data->clkevt.suspend = at91sam926x_pit_suspend;...
}
Here we can see that at91sam926x_pit_common_init
takes one parameter - pointer to the pit_data
structure which contains clock_event_device
structure which will contain clock event related information of the at91sam926x
periodic Interval Timer. At the start we fill name
of the timer device and its features
. In our case we deal with periodic timer which as we already know may be programmed to generate events periodically.
The next two fields shift
and mult
are familiar to us. They will be used to translate counter of our timer to nanoseconds. After this we set rating of the timer to 100
. This means if there will not be timers with higher rating in the system, this timer will be used for timekeeping. The next field - cpumask
indicates for which processors in the system the device will work. In our case, the device will work for the first processor. The cpumask_of
macro defined in the include/linux/cpumask.h header file and just expands to the call of the:
#define cpumask_of(cpu) (get_cpu_mask(cpu))
Where the get_cpu_mask
returns the cpumask containing just a given cpu
number. More about cpumasks
concept you may read in the CPU masks in the Linux kernel part. In the last four lines of code we set callbacks for the clock event device suspend/resume, device shutdown and update of the clock event device state.
After we finished with the initialization of the at91sam926x
periodic timer, we can register it by the call of the following functions:
clockevents_register_device(&data->clkevt);
Now we can consider implementation of the clockevent_register_device
function. As I already wrote above, this function is defined in the kernel/time/clockevents.c source code file and starts from the initialization of the initial event device state:
clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
Actually, an event device may be in one of this states:
enum clock_event_state {CLOCK_EVT_STATE_DETACHED,CLOCK_EVT_STATE_SHUTDOWN,CLOCK_EVT_STATE_PERIODIC,CLOCK_EVT_STATE_ONESHOT,CLOCK_EVT_STATE_ONESHOT_STOPPED,
};
Where:
CLOCK_EVT_STATE_DETACHED
- a clock event device is not not used byclockevents
framework. Actually it is initial state of all clock event devices;CLOCK_EVT_STATE_SHUTDOWN
- a clock event device is powered-off;CLOCK_EVT_STATE_PERIODIC
- a clock event device may be programmed to generate event periodically;CLOCK_EVT_STATE_ONESHOT
- a clock event device may be programmed to generate event only once;CLOCK_EVT_STATE_ONESHOT_STOPPED
- a clock event device was programmed to generate event only once and now it is temporary stopped.
The implementation of the clock_event_set_state
function is pretty easy:
static inline void clockevent_set_state(struct clock_event_device *dev,enum clock_event_state state)
{dev->state_use_accessors = state;
}
As we can see, it just fills the state_use_accessors
field of the given clock_event_device
structure with the given value which is in our case is CLOCK_EVT_STATE_DETACHED
. Actually all clock event devices has this initial state during registration. The state_use_accessors
field of the clock_event_device
structure provides current
state of the clock event device.
After we have set initial state of the given clock_event_device
structure we check that the cpumask
of the given clock event device is not zero:
if (!dev->cpumask) {WARN_ON(num_possible_cpus() > 1);dev->cpumask = cpumask_of(smp_processor_id());
}
Remember that we have set the cpumask
of the at91sam926x
periodic timer to first processor. If the cpumask
field is zero, we check the number of possible processors in the system and print warning message if it is less than on. Additionally we set the cpumask
of the given clock event device to the current processor. If you are interested in how the smp_processor_id
macro is implemented, you can read more about it in the fourth part of the Linux kernel initialization process chapter.
After this check we lock the actual code of the clock event device registration by the call following macros:
raw_spin_lock_irqsave(&clockevents_lock, flags);
...
...
...
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
Additionally the raw_spin_lock_irqsave
and the raw_spin_unlock_irqrestore
macros disable local interrupts, however interrupts on other processors still may occur. We need to do it to prevent potential deadlock if we adding new clock event device to the list of clock event devices and an interrupt occurs from other clock event device.
We can see following code of clock event device registration between the raw_spin_lock_irqsave
and raw_spin_unlock_irqrestore
macros:
list_add(&dev->list, &clockevent_devices);
tick_check_new_device(dev);
clockevents_notify_released();
First of all we add the given clock event device to the list of clock event devices which is represented by the clockevent_devices
:
static LIST_HEAD(clockevent_devices);
At the next step we call the tick_check_new_device
function which is defined in the kernel/time/tick-common.c source code file and checks do the new registered clock event device should be used or not.
CLOCK_EVT_STATE_ONESHOT
更受欢迎。
The tick_check_new_device
function checks the given clock_event_device
gets the current registered tick device which is represented by the tick_device
structure and compares their ratings and features. Actually CLOCK_EVT_STATE_ONESHOT
is preferred:
static bool tick_check_preferred(struct clock_event_device *curdev,struct clock_event_device *newdev)
{if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) {if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT))return false;if (tick_oneshot_mode_active())return false;}return !curdev ||newdev->rating > curdev->rating ||!cpumask_equal(curdev->cpumask, newdev->cpumask);
}
If the new registered clock event device is more preferred than old tick device, we exchange old and new registered devices and install new device:
clockevents_exchange_device(curdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
The clockevents_exchange_device
function releases or in other words deleted the old clock event device from the clockevent_devices
list.
The next function - tick_setup_device
as we may understand from its name, setups new tick device. This function check the mode of the new registered clock event device and call the tick_setup_periodic
function or the tick_setup_oneshot
depends on the tick device mode:
if (td->mode == TICKDEV_MODE_PERIODIC)tick_setup_periodic(newdev, 0);
elsetick_setup_oneshot(newdev, handler, next_event);
Both of this functions calls the clockevents_switch_state
to change state of the clock event device and the clockevents_program_event
function to set next event of clock event device based on delta between the maximum and minimum difference current time and time for the next event. The tick_setup_periodic
:
clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
clockevents_program_event(dev, next, false))
and the tick_setup_oneshot_periodic
:
clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT);
clockevents_program_event(newdev, next_event, true);
The clockevents_switch_state
function checks that the clock event device is not in the given state and calls the __clockevents_switch_state
function from the same source code file:
if (clockevent_get_state(dev) != state) {if (__clockevents_switch_state(dev, state))return;
The __clockevents_switch_state
function just makes a call of the certain callback depends on the given state:
static int __clockevents_switch_state(struct clock_event_device *dev,enum clock_event_state state)
{if (dev->features & CLOCK_EVT_FEAT_DUMMY)return 0;switch (state) {case CLOCK_EVT_STATE_DETACHED:case CLOCK_EVT_STATE_SHUTDOWN:if (dev->set_state_shutdown)return dev->set_state_shutdown(dev);return 0;case CLOCK_EVT_STATE_PERIODIC:if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))return -ENOSYS;if (dev->set_state_periodic)return dev->set_state_periodic(dev);return 0;.........
In our case for at91sam926x
periodic timer, the state is the CLOCK_EVT_FEAT_PERIODIC
:
data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
data->clkevt.set_state_periodic = pit_clkevt_set_periodic;
So, for the pit_clkevt_set_periodic
callback will be called. If we will read the documentation of the Periodic Interval Timer (PIT) for at91sam926x, we will see that there is Periodic Interval Timer Mode Register
which allows us to control of periodic interval timer.
It looks like:
31 25 24
+---------------------------------------------------------------+
| | PITIEN | PITEN |
+---------------------------------------------------------------+
23 19 16
+---------------------------------------------------------------+
| | PIV |
+---------------------------------------------------------------+
15 8
+---------------------------------------------------------------+
| PIV |
+---------------------------------------------------------------+
7 0
+---------------------------------------------------------------+
| PIV |
+---------------------------------------------------------------+
Where PIV
or Periodic Interval Value
- defines the value compared with the primary 20-bit
counter of the Periodic Interval Timer. The PITEN
or Period Interval Timer Enabled
if the bit is 1
and the PITIEN
or Periodic Interval Timer Interrupt Enable
if the bit is 1
. So, to set periodic mode, we need to set 24
, 25
bits in the Periodic Interval Timer Mode Register
. And we are doing it in the pit_clkevt_set_periodic
function:
static int pit_clkevt_set_periodic(struct clock_event_device *dev)
{struct pit_data *data = clkevt_to_pit_data(dev);.........pit_write(data->base, AT91_PIT_MR,(data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN);return 0;
}
Where the AT91_PT_MR
, AT91_PT_PITEN
and the AT91_PIT_PITIEN
are declared as:
#define AT91_PIT_MR 0x00
#define AT91_PIT_PITIEN BIT(25)
#define AT91_PIT_PITEN BIT(24)
After the setup of the new clock event device is finished, we can return to the clockevents_register_device
function. The last function in the clockevents_register_device
function is:
clockevents_notify_released();
This function checks the clockevents_released
list which contains released clock event devices (remember that they may occur after the call of the clockevents_exchange_device
function). If this list is not empty, we go through clock event devices from the clock_events_released
list and delete it from the clockevent_devices
:
static void clockevents_notify_released(void)
{struct clock_event_device *dev;while (!list_empty(&clockevents_released)) {dev = list_entry(clockevents_released.next,struct clock_event_device, list);list_del(&dev->list);list_add(&dev->list, &clockevent_devices);tick_check_new_device(dev);}
}
That’s all. From this moment we have registered new clock event device. So the usage of the clockevents
framework is simple and clear. Architectures registered their clock event devices, in the clock events core. Users of the clockevents core can get clock event devices for their use. The clockevents
framework provides notification mechanisms for various clock related management events like a clock event device registered or unregistered, a processor is offlined in system which supports CPU hotplug and etc.
We saw implementation only of the clockevents_register_device
function. But generally, the clock event layer API is small. Besides the API
for clock event device registration, the clockevents
framework provides functions to schedule the next event interrupt, clock event device notification service and support for suspend and resume for clock event devices.
If you want to know more about clockevents
API you can start to research following source code and header files: kernel/time/tick-common.c, kernel/time/clockevents.c and include/linux/clockchips.h.
That’s all.
3. Conclusion
This is the end of the fifth part of the chapter that describes timers and timer management related stuff in the Linux kernel. In the previous part got acquainted with the timers
concept. In this part we continued to learn time management related stuff in the Linux kernel and saw a little about yet another framework - clockevents
.
If you have questions or suggestions, feel free to ping me in twitter 0xAX, drop me email or just create issue.
Please note that English is not my first language and I am really sorry for any inconvenience. If you found any mistakes please send me PR to linux-insides.
4. Links
- timekeeping documentation
- Intel 8253
- programmable interval timer
- ACPI pdf
- x86
- High Precision Event Timer
- powerpc
- frequency
- API
- nanoseconds
- interrupt
- interrupt handler
- local APIC
- C3 state
- Periodic Interval Timer (PIT) for at91sam926x
- CPU masks in the Linux kernel
- deadlock
- CPU hotplug
- previous part
Linux内核深入理解定时器和时间管理(5):clockevents 框架相关推荐
- Linux内核深入理解定时器和时间管理(3):tick 广播 框架 和 dyntick
Linux内核深入理解定时器和时间管理 tick 广播 框架 和 dyntick rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 结构体 --------------- ...
- Linux内核深入理解定时器和时间管理(7):相关的系统调用
Linux内核深入理解定时器和时间管理 相关的系统调用 rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 结构体 ---------------------------- ...
- Linux内核深入理解定时器和时间管理(4):定时器 timer
Linux内核深入理解定时器和时间管理 定时器 timer rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 1. Timers This is fourth part ...
- Linux内核深入理解定时器和时间管理(2):clocksource 框架
Linux内核深入理解定时器和时间管理 clocksource 框架 rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 全局部变量 ------------------- ...
- Linux内核深入理解定时器和时间管理(1):硬件时钟和jiffies
Linux内核深入理解定时器和时间管理 硬件时钟和jiffies rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容. 全局部变量 --------------------- ...
- Linux内核深入理解定时器和时间管理(6):x86_64 相关的时钟源(kvm-clock,tsc,acpi_pm,hpet)
Linux内核深入理解定时器和时间管理 x86_64 相关的时钟源(kvm-clock,tsc,acpi_pm,hpet) rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容 ...
- 《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理
系统中有很多与时间相关的程序(比如定期执行的任务,某一时间执行的任务,推迟一段时间执行的任务),因此,时间的管理对于linux来说非常重要. 主要内容: 系统时间 定时器 定时器相关概念 定时器执行流 ...
- Linux内核——定时器和时间管理
定时器和时间管理 系统定时器是一种可编程硬件芯片.它能以固定频率产生中断.该中断就是所谓的定时器中断.它所相应的中断处理程序负责更新系统时间,还负责执行须要周期性执行的任务. 系统定时器和时钟中断处理 ...
- linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程
浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...
最新文章
- JQuery判断radio是否选中,获取选中值
- 贫穷中透着零基础的单人制作游戏手册之一:怎么开局
- 深入理解ThreadLocal
- 开发一款自动指向特定页面元素的jQuery插件:jQuery PointPoint
- 计算机考试前的心情作文,考试时的心情作文(七篇)
- java rx_史上最浅显易懂的RxJava入门教程
- Zookeeeper开源客户端curator watcherAPI的使用
- 数据库基础操作:建立学生表
- 定时炸弹?揭露AmazonBasics电池背后的秘密
- Composition
- 截至20161210创业板股票代码和名称
- Person Re-identification using Heterogeneous Local Graph Attention Networks(基于异构局部图注意力网络的人员再识别)
- 华为光纤猫HG8240破解,开启路由功能表
- ArcGIS山体坡度、坡向分析
- 《黄聪:手机移动站SEO优化教程》2、PC端和手机移动端SEO优化区别
- Java JUC 简介
- SIM卡状态字 SW1 SW2
- c语言字母从小到大,C语言编程:输入三个字符后,按各字符的ASCII码从小到大的顺序输出这三个字符。...
- MNS、IOU、bounding box regression详解
- 08.区块链的应用有什么?区块链将带来什么变革?
热门文章
- JDBC连接数据库的8个步骤
- 我们不生产代码,我们只是代码的搬运工
- GlobalAuthenticationConfigurerAdapter.class 整合activiti 报错
- java基础面试题之:普通类和抽象类有哪些区别?
- 页面跨域与iframe通信(Blocked a frame with origin)
- C++ 线程同步 (学习笔记)
- php修改mongo,php操作MongoDB基础教程(连接、新增、修改、删除、查询)
- 搜狗拼音输入法linux 18.04,解决Ubuntu 18.04中文输入法的问题,Ubuntu 18.04安装搜狗拼音...
- 数据中台 画像标签_数据中台实战:如何通过标签平台圈出产品高价值用户?...
- python语言的类型是_Python的语言类型