時間子係統12_clockevent設備注冊
// 向係統注冊時鍾事件設備
// 函數任務:
// 1.添加設備到clockevent_devices鏈表
// 2.通知tick device管理機製有clockevent設備注冊
1.1 void clockevents_register_device(struct clock_event_device *dev)
{
unsigned long flags;
raw_spin_lock_irqsave(&clockevents_lock, flags);
//將設備添加到clockevent_devices鏈表
list_add(&dev->list, &clockevent_devices);
//向係統通知有新設備注冊
clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
// clockevent設備注冊處理函數
// 調用路徑:tick_notify->CLOCK_EVT_NOTIFY_ADD
// 函數任務:
// 1.確認設備服務於本cpu
// 2.如果設備非本cpu的local device
// 2.1 設置cpu的irq親和性
// 2.2 如果本cpu已經綁定一個local device,並且二者服務的cpu相同,則忽略
// 3.如果本cpu已有local device,選擇兩者中更好的
// 3.1 優先選擇支持單觸發的
// 3.2 然後選擇高rating的
// 4.如果本cpu使用的clockevent設備為廣播設備,直接關閉
// 5.更新cpu的tick device為新tick device
// 6.啟動tick device
// 7.異步通知周期時鍾關於時鍾事件設備的改變
// 注:如果不能設置為本cpu tick device的事件源,考慮其是否可以作為廣播設備。
1.2 static int tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu, ret = NOTIFY_OK;
unsigned long flags;
raw_spin_lock_irqsave(&tick_device_lock, flags);
//檢查設備是否服務於本cpu
cpu = smp_processor_id();
if (!cpumask_test_cpu(cpu, newdev->cpumask))
goto out_bc;
//當前cpu的tick_device
td = &per_cpu(tick_cpu_device, cpu);
curdev = td->evtdev;
//檢查是否為本cpu的local device
// 如果設備隻服務於本cpu,則為local device
if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) {
//非local device,並且不能設置irq 親和性,忽略
if (!irq_can_set_affinity(newdev->irq))
goto out_bc;
//如果本cpu已經綁定有一個local device,則忽略
if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))
goto out_bc;
}
//本cpu已有一個tick device
if (curdev) {
//選擇兩者中最好的tick device
//優先選擇 支持單觸發模式
if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) &&
!(newdev->features & CLOCK_EVT_FEAT_ONESHOT))
goto out_bc;
//然後選擇 高rating
if (curdev->rating >= newdev->rating)
goto out_bc;
}
//如果當前使用的clockevent設備為廣播設備,直接關閉
if (tick_is_broadcast_device(curdev)) {
clockevents_shutdown(curdev);
curdev = NULL;
}
//設置cpu的tick device為新tick device
clockevents_exchange_device(curdev, newdev);
//啟動tick device
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
//異步通知周期時鍾關於時鍾事件設備的改變
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
return NOTIFY_STOP;
out_bc:
//嚐試設置clockevent設備為tick device
if (tick_check_broadcast_device(newdev))
ret = NOTIFY_STOP;
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
return ret;
}
// 綁定tick device到clockevnet設備
// 函數任務:
// 1.tick device未綁定過clockevent
// 1.1 選擇cpu負責更新全局時間
// 1.2 以周期模式啟動
// 2.tick device綁定過clockevnet
// 2.1 暫時設置其eventhandler=noop,保留下次到期時間
// 3.綁定tick device到新clockevent
// 4.clockevent並不僅服務於本cpu,設置irq親和本cpu
// 5.如果設備沒有正常工作,將設備加入廣播組,然後退出
// 6.否則更新clockevent的事件處理函數
// 6.1 首次綁定,以周期模式啟動
// 6.2 否則按照之前的模式啟動
// 調用路徑:tick_check_new_device->tick_setup_device
// 注:
// 1.由負責更新全局時間的cpu,初始化tick_next_period,tick_period
// tick_next_period, 周期時鍾下次到期的時間
// tick_period,周期時鍾的周期,1HZ經曆的納秒
1.2 static void tick_setup_device(struct tick_device *td,
struct clock_event_device *newdev, int cpu,
const struct cpumask *cpumask)
{
ktime_t next_event;
void (*handler)(struct clock_event_device *) = NULL;
//tick device第一次創建,沒有綁定clockevent
if (!td->evtdev) {
//如果沒有cpu負責do_timer,由本cpu負責
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
tick_do_timer_cpu = cpu;
tick_next_period = ktime_get();
tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
}
//以周期模式啟動
td->mode = TICKDEV_MODE_PERIODIC;
} else {
//tick device非第一次創建,此次為更新clockevent
//更新event處理函數為noop
handler = td->evtdev->event_handler;
next_event = td->evtdev->next_event;
td->evtdev->event_handler = clockevents_handle_noop;
}
//更新tick device的clockevent
td->evtdev = newdev;
//clockevent並不僅服務於本cpu,設置irq與本cpu親和
if (!cpumask_equal(newdev->cpumask, cpumask))
irq_set_affinity(newdev->irq, cpumask);
//如果設備沒有正常工作,將設備加入廣播組,然後退出
if (tick_device_uses_broadcast(newdev, cpu))
return;
//設置clockevent的eventhandler
if (td->mode == TICKDEV_MODE_PERIODIC)
{
//以周期模式啟動
tick_setup_periodic(newdev, 0);
}
else
{
//以單觸發模式啟動
tick_setup_oneshot(newdev, handler, next_event);
}
}
// 周期模式啟動設備
// 調用路徑:tick_check_new_device->tick_setup_device->tick_setup_periodic
// 函數任務:
// 1.設置設備周期處理函數
// 2.根據設備支持的模式,激活設備
// 2.1 如果設備支持周期模式,並且為激活單觸發模式
// 2.1.1 設置設備當前為周期模式,退出
// 2.1.2 由周期處理函數執行周期任務
// 2.2 否則,通過單觸發模式模擬周期運行
// 2.2.1 獲取下次tick到期的時間
// 2.2.2 編程設備下一次到期時間
// 2.2.3 由周期處理函數執行周期任務,更新下次到期時間
1.3 void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
//設置clockevent的周期處理函數
tick_set_periodic_handler(dev, broadcast);
//設備不能正常啟動,則有可能broadcast代其運行
if (!tick_device_is_functional(dev))
return;
//如果設備支持周期模式,並且沒有激活單觸發模式
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
!tick_broadcast_oneshot_active())
{
//設置設備為周期模式
clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
} else
{
unsigned long seq;
ktime_t next;
//獲取下次tick到期的時間
do {
seq = read_seqbegin(&xtime_lock);
next = tick_next_period;
} while (read_seqretry(&xtime_lock, seq));
//設置設備為單觸發模式
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
//設置設備下一次到期時間
for (;;) {
if (!clockevents_program_event(dev, next, ktime_get()))
return;
next = ktime_add(next, tick_period);
}
}
}
// 單觸發模式啟動設備
// 調用路徑:tick_check_new_device->tick_setup_device->tick_setup_oneshot
// 函數任務:
// 1.設置事件處理函數
// 2.設置設備當前為單觸發模式
// 3.更新設備事件的到期時間
1.4 void tick_setup_oneshot(struct clock_event_device *newdev,
void (*handler)(struct clock_event_device *),
ktime_t next_event)
{
//設置事件處理函數
newdev->event_handler = handler;
clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
//更新設備事件到期時間
tick_dev_program_event(newdev, next_event, 1);
}
// 設置設備為廣播設備
// 函數任務:
// 1.成為廣播設備的條件
// 1.1 設備不受電源狀態的影響
// 1.2 設備的rating高於當前使用的廣播設備
// 2.綁定此設備到全局廣播設備
// 3.如果有設備被代理
// 3.1 開啟廣播設備
2.1 int tick_check_broadcast_device(struct clock_event_device *dev)
{
//檢查是否可以作為廣播設備
if ((tick_broadcast_device.evtdev &&
tick_broadcast_device.evtdev->rating >= dev->rating) ||
(dev->features & CLOCK_EVT_FEAT_C3STOP))
return 0;
clockevents_exchange_device(NULL, dev);
tick_broadcast_device.evtdev = dev;
//有設備需要被廣播設備代理
if (!cpumask_empty(tick_get_broadcast_mask()))
{
//啟動廣播設備
tick_broadcast_start_periodic(dev);
}
return 1;
}
// 開啟廣播設備
// 函數任務:
// 1.設置廣播設備的事件處理函數
// 2.編程啟動廣播設備
// 調用路徑:tick_check_new_device->tick_broadcast_start_periodic
2.2 static void tick_broadcast_start_periodic(struct clock_event_device *bc)
{
if (bc)
tick_setup_periodic(bc, 1);
}
// 將cpu加入廣播組
// 函數任務:
// 1.如果cpu的tick device沒有正常工作
// 1.1 設置tick device的事件處理函數為周期事件處理函數
// 1.2 將設備加入廣播組
// 1.3 啟動廣播設備
// 2.否則,如果設備不受省電模式C3的影響
// 2.1 將cpu從廣播組中刪除
3.1 int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
{
unsigned long flags;
int ret = 0;
raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
if (!tick_device_is_functional(dev)) {
dev->event_handler = tick_handle_periodic;
cpumask_set_cpu(cpu, tick_get_broadcast_mask());
tick_broadcast_start_periodic(tick_broadcast_device.evtdev);
ret = 1;
} else {
//如果本設備不受省電模式C3的影響
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) {
int cpu = smp_processor_id();
//將cpu從廣播組中刪除
cpumask_clear_cpu(cpu, tick_get_broadcast_mask());
tick_broadcast_clear_oneshot(cpu);
}
}
raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
return ret;
}
最後更新:2017-04-03 14:54:38