131
社区成员




Tick代理设备是一种合成的时钟事件设备,用于将硬件Tick设备的控制权移交给高精度out-of-band时序逻辑,该逻辑不能被in-band内核代码所延迟。有了这个代理,任何out-of-band代码都可以获得对定时器硬件的控制,以执行其自己的定时任务。在同一步骤中,需要接受从in-band定时器层(即hrtimers)收到的定时请求,因为后者在Tick代理处于活动状态时无法将定时器事件直接编程到硬件中。
换言之, Tick代理设备在in-band和out-of-band上下文之间共享实际设备的功能,而只有后者实际对硬件进行控制。
Dovetail中tick代理设备从in-band内核借用了一个真正的时钟芯片设备,并控制它,同时取代了当前的tick设备。可以由tick代理设备控制的时钟芯片需要它们的驱动器专门适配于这种用途,如下所示:
__IRQF_TIMER:
必须为计时器设备中断的操作处理程序设置IRQF_TIMER。注意:
只有一个具有one-shot功能的时钟事件设备可以通过dovetail的tick代理设备共享。
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -156,11 +156,11 @@ static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
* the Global Timer flag _after_ having incremented
* the Comparator register value to a higher value.
*/
- if (clockevent_state_oneshot(evt))
+ if (clockevent_is_oob(evt) || clockevent_state_oneshot(evt))
gt_compare_set(ULONG_MAX, 0);
writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
- evt->event_handler(evt);
+ clockevents_handle_event(evt);
return IRQ_HANDLED;
}
@@ -171,7 +171,7 @@ static int gt_starting_cpu(unsigned int cpu)
clk->name = "arm_global_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
- CLOCK_EVT_FEAT_PERCPU;
+ CLOCK_EVT_FEAT_PERCPU | CLOCK_EVT_FEAT_PIPELINE;
clk->set_state_shutdown = gt_clockevent_shutdown;
clk->set_state_periodic = gt_clockevent_set_periodic;
clk->set_state_oneshot = gt_clockevent_shutdown;
@@ -195,11 +195,6 @@ static int gt_dying_cpu(unsigned int cpu)
return 0;
}
@@ -302,8 +307,8 @@ static int __init global_timer_of_register(struct device_node *np)
goto out_clk;
}
- err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
- "gt", gt_evt);
+ err = __request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
+ IRQF_TIMER, "gt", gt_evt);
if (err) {
pr_warn("global-timer: can't register interrupt %d (%d)\n",
gt_ppi, err);
这是调整现有时钟芯片驱动器以服务out-of-band定时请求的一个例子,其中Dovetail在中断处理程序中测试时钟设备的当前状态的方式发生了细微的变化:
首先调用tick_install_proxy()在它接收的cpumask中提到的每个CPU上注册代理tick设备的一个实例。该例程还被传递一个例程的地址,该例程应该为当前CPU设置给定的结构clock_proxy_device描述符。对于cpumask中标记的每个CPU,tick_install_proxy()间接调用此例程。
初始化代理描述符:
struct clock_proxy_device {
struct clock_event_device proxy_device;
struct clock_event_device *real_device;
void (*handle_oob_event)(struct clock_event_device *dev);
/* Internal data - don't depend on this. */
void (*__setup_handler)(struct clock_proxy_device *dev);
void (*__original_handler)(struct clock_event_device *dev);
};
用户设置调用必须至少设置.handle_ob_event()处理程序:这是例程的地址,每次从代理控制的底层计时器硬件接收到out-of-band滴答时钟时都应该调用该例程。这是设置处理程序所需的唯一信息,用户可以不需要任何其他特定设置,其余信息可以从预设数据中继承。
如果代理tick设备的用户代码更喜欢处理纳秒而不是直接处理时钟tick,则应将CLOCK_EVT_FEAT_KTIME添加到proxy_device.features,以及有效的proxy_devices.set_next_ktime()处理程序和适当的最小/最大delta值。
其次,准备代理设备的setup_proxy()例程
static DEFINE_PER_CPU(struct clock_proxy_device, tick_device);
static void oob_event_handler(struct clock_event_device *dev)
{
/*
* We are running on the out-of-band stage, in NMI-like mode.
* Schedule a tick on the proxy device to satisfy the
* corresponding timing request asap.
*/
tick_notify_proxy();
}
static void setup_proxy(struct clock_proxy_device *dev)
{
* Create a proxy which acts as a transparent device, simply
* relaying the timing requests to the in-band code, without
* any additional out-of-band processing.
*/
dev->handle_oob_event = oob_event_handler;
}
提示:
proxy_device.set_next_event()或proxy_devict.set_next_ktime()成员可以设置为从in-band内核接收定时器请求的处理程序的地址。该处理函数通常由实时内核实现,实时内核通过代理设备控制定时器硬件。每当该内核确定从这样的处理程序接收到的未完成处理的tick到期请求时,它都应该调用tick_notify_proxy()将事件发送到实时内核。
一旦用户提供的setup_proxy()例程返回,以下事件将按顺序发生:
步骤3包括通过由一些out-of-band定时器管理控制的软件逻辑来模拟由in-band内核调度的ticks,该软件逻辑由如步骤4中所述的接收到的实际ticks来控制。当这个逻辑决定下一个in-band tick到期时,它应该调用tick_notify_proxy()来触发in-band内核的相应事件,这将满足挂起的(hr)计时器请求。
翻译自https://evlproject.org/dovetail/porting/timer/,请大家指正。