Dovetail的Tick代理设备机制

Legonext 2024-02-23 10:28:16

     Tick代理设备是一种合成的时钟事件设备,用于将硬件Tick设备的控制权移交给高精度out-of-band时序逻辑,该逻辑不能被in-band内核代码所延迟。有了这个代理,任何out-of-band代码都可以获得对定时器硬件的控制,以执行其自己的定时任务。在同一步骤中,需要接受从in-band定时器层(即hrtimers)收到的定时请求,因为后者在Tick代理处于活动状态时无法将定时器事件直接编程到硬件中。

    换言之, Tick代理设备在in-band和out-of-band上下文之间共享实际设备的功能,而只有后者实际对硬件进行控制。

使时钟芯片设备适配Tick代理

    Dovetail中tick代理设备从in-band内核借用了一个真正的时钟芯片设备,并控制它,同时取代了当前的tick设备。可以由tick代理设备控制的时钟芯片需要它们的驱动器专门适配于这种用途,如下所示:

  • clockevents_handle_event(): 用它来替换时钟中断处理中对事件句柄的调用。
  • struct clock_event_device:irq必须正确设置为从该设备发出事件信号的实际irq编号。
  • struct clock_event_device: 功能必须包括CLOCK_EVT_FEAT_PIPELINE。
  • __IRQF_TIMER: 必须为计时器设备中断的操作处理程序设置IRQF_TIMER。

注意:

     只有一个具有one-shot功能的时钟事件设备可以通过dovetail的tick代理设备共享。

示例:

  1. 使ARM全局定时器驱动程序适应out-of-band定时
--- 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代理驱动程序将真实/原始设备(如本例中的ARM全局定时器)移交给实时内核时,它将切换到保留模式,这与实际的分离模式类似。因此,在代理tick时,从非实时内核的角度来看,ARM全局定时器状态始终是保留的,而不是one-shot模式的。因此,在这种情况下,clockevent_state_oneshot()总是会导致false。
  • 然而,由于由代理控制的用于接收out-of-band事件的实际设备必须以one-shot模式驱动,因此除了测试clockevent_state_oneshot()之外,还测试clockevent_state_oob()可以确保我们确实采用了该分支,并在代理时将比较器寄存器设置为ULONG_MAX。

运行原理

    首先调用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()例程返回,以下事件将按顺序发生:

  1. 代理设备被注册在时钟事件框架上。
  2. 实际设备从时钟事件框架分离,切换到CLOCK_EVT_STATE_RESERVED状态,这使得它不再从常规时钟框架进行任何常规操作。然而,它的硬件仍处于功能状态。在同一步骤中,代理设备被框架选为新的tick设备。从那时起,当应该执行对硬件的操作时,对代理设备的请求可以经由代理间接地引导到真实设备进行操作。
  3. 代理设备现在控制引的真实设备来执行来自in-band内核的定时请求。当in-band内核的hrtimer层想要对当前tick设备的下一个one-shot进行编程时,它会调用代理设备的set_next_event()处理程序,该处理程序由用户定义(默认为真实设备的set_next_event)处理程序)。如果实时内核实现了自己的定时器管理,则该处理程序应该基于这样的方案在请求的时间调度in-band ticks。
  4. 由实际设备触发的定时器中断被切换到out-of-band处理。因此,handle_oob_event()直接从中断管道的out-of-band阶段接收由实际设备硬件发送的tick事件。这确保了高精度的定时,而in-band阶段不能通过中断屏蔽来延迟。从那时起,out-of-band代码除了可以满足in-band内核的定时请求外,还可以执行自己的定时职责。

    步骤3包括通过由一些out-of-band定时器管理控制的软件逻辑来模拟由in-band内核调度的ticks,该软件逻辑由如步骤4中所述的接收到的实际ticks来控制。当这个逻辑决定下一个in-band tick到期时,它应该调用tick_notify_proxy()来触发in-band内核的相应事件,这将满足挂起的(hr)计时器请求。

翻译自https://evlproject.org/dovetail/porting/timer/,请大家指正。

 

 

...全文
174 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

131

社区成员

发帖
与我相关
我的任务
社区描述
Xenomai中文社区。 Upstream - xenomai.org Mirror - gitee.com/Xenomai CSDN - bbs.csdn.net/forums/Xenomai
社区管理员
  • Xenomai
  • legonext
  • Cajb
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧