目录
Interrupt Pipeline系列文章大纲-CSDN博客
2.3.6.1 概要
2.3.6.2 位于root域且虚拟中断打开
2.3.6.2.1 tsk_linux_root任务类型
2.3.6.2.2 task_xenomai_root任务类型
2.3.6.3 位于root域且虚拟中断屏蔽
2.3.6.3.1 tsk_linux_root任务类型
2.3.6.3.2 task_xenomai_root任务类型
2.3.6.4 位于head域
当el0_irq发生时,被中断的用户态任务有三种类型:
任务类型 |
说明 |
|
1 |
tsk_linux_root |
Linux进程,运行在root域。 |
2 |
tsk_xenomai_root |
Xenomai进程,已经迁移到root域即作为Linux进程进行调度 |
3 |
tsk_xenomai_head |
Xenomai进程,在head域运行。 |
综合上一章节中断发生的场景,可能出现的组合如下表。接下来按照场景进行分析。
场景/任务类型 |
irq_handler返回值 |
tsk_linux_root |
tsk_xenomai_root |
tsk_xenomai_head |
位于root域, |
1 |
可能 |
可能 |
不可能 |
位于root域, |
0 |
可能 |
可能 |
不可能 |
位于head域 |
0 |
不可能 |
不可能 |
可能 |
接下来按照场景进行分析。
因为位于root域,此时用户态任务只可能是tsk_linux_root和tsk_xenomai_root两种类型。
第1步,在el0_irq通过b ret_to_user_nocheck跳转到此处,关闭daif共4个中断屏蔽标识。在此之前da_f都是打开状态,而__i_是屏蔽状态。
第2步,检查TSK_TI_FLAGS中是否设置了_TIF_WORK_MASK标记。TSK_TI_FLAGS定义在arch/arm64/kernel/asm-offsets.c:
DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags));
_TIF_WORK_MASK定义在arch/arm64/include/asm/thread_info.h:
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
_TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
_TIF_UPROBE | _TIF_FSCHECK)
如果TSK_TI_FLAGS与_TIF_WORK_MASK按位与运算位0,则直接跳转到第5步。否则跳转到第3步。
第3步,执行do_notify_resume函数,该函数通过判断TSK_TI_FLAGS来执行具体逻辑。最值得一提的是,如果匹配了_TIF_NEED_RESCHED,则调用schedule()执行任务切换。
第4步,在第3步中,可能将第2步中的x1寄存器重新赋值,所以需要对x1寄存器按照第2步进行初始化,为进入第5步做准备。
第5步,调用enable_step_ts,检查TSK_TI_FLAGS中是否设置了TIF_SINGLESTEP标记。如果是,则置位MDSCR.SS。此处和kernel_entry中调用disable_step_tsk是相对的。因为在第1步中已经关闭了debug中断,所以此时置位MDSCR.SS并不会真正使能Single Step。
TSK_TI_FLAGS定义在arch/arm64/kernel/asm-offsets.c:
DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags));
enable_step_tsk定义在arch/arm64/include/asm/assembler.h:
.macro enable_step_tsk, flgs, tmp
tbz \flgs, #TIF_SINGLESTEP, 9990f
mrs \tmp, mdscr_el1
orr \tmp, \tmp, #DBG_MDSCR_SS
msr mdscr_el1, \tmp
9990:
.endm
第6步,对于tsk_linux_root任务类型,直接跳到第7步。
第7步,调用kernel_exit 0,返回用户空间。
首先沿着实线从第1步走到第5步,与task_linux_root的过程完全相同。
第6步,检查TSK_TI_IPIPE中是否设置了_TIP_USERINTRET标记。TSK_TI_IPIPE定义在arch/arm64/kernel/asm-offsets.c:
DEFINE(TSK_TI_IPIPE, offsetof(struct task_struct, thread_info.ipipe_flags));
_TIP_USERINTRET定义在arch/arm64/include/asm/thread_info.h。根据注释,它用于在异常返回用户空间之前,通知实时内核Xenomai。
#define TIP_USERINTRET 3 /* Notify on IRQ/trap return to root userspace */
#define _TIP_USERINTRET (1 << TIP_USERINTRET)
如果仅仅打上了I-pipe Patch,那么ipipe_kevent_hook不会对_TIP_USERINTRET做任何处理,调用关系如下:
__ipipe_notify_user_intreturn [kernel/ipipe/core.c]
->__ipipe_notify_kevent
-> ipipe_kevent_hook [kernel/ipipe/core.c]
->return 0
如果安装了Xenomai,则定义新的ipipe_kevent_hook函数,调用关系如下,且是同步调用:
__ipipe_notify_user_intreturn [kernel/ipipe/core.c]
->__ipipe_notify_kevent
-> ipipe_kevent_hook [kernel/xenomai/pipeline/kevents.c]
-> handle_user_return
->ipipe_disable_user_intret_notifier()
->cobalt_handle_user_return
-> xnthread_harden
在cobalt_handle_user_return的主体部分,依赖于对XNCONTHI的检查,来决定是否调用xnthread_harden。Xenomai中调用xnthread_set_info(curr, XNCONTHI)来设置XNCONTHI标记的地方,均有debuging(单步或ptrace)相关。
#define XNCONTHI 0x00000200 /**< Continue in primary mode after debugging */
因此,Xenomai使用_TIP_USERINTRET只用于处理处于debugging过程中的任务。如果任务处于debugging状态,Xenomai认为此种情况下不需在乎实时性,无需自己处理,会将任务切换到root域交给Linux处理。
最终从__ipipe_notify_user_intreturn返回时,通过return !ipipe_root_p来检查当前是root域还是head域,核心部分在xnthread_harden中。
xnthread_harden在tsk_linux_root切换到head域之前,调用signal_pending检查TIF_SIGPENDING。如果检查到了TIF_SIGPENDING,说明任务中有singal信号待处理,那么xnthread_harden直接返回,任务依然在root域。
xnthread_harden在tsk_linux_root切换到head域之后,再次调用signal_pending检查TIF_SIGPENDING。如果没有检查到TIF_SIGPENDING,直接返回,此时切换到了root域。如果检查到了TIF_SIGPENDING,说明任务中有singal信号待处理,那么xnthread_harden调用xnthread_relax切换任务回到root域。
signal_pending函数定义在include/linux/sched/signal.h:
static inline int signal_pending(struct task_struct *p)
{
return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING));
}
从xnthread_harden返回后,如果切换到了head域,则__ipipe_notify_user_intreturn的返回值return !ipipe_root_p为0,跳转到第7步,执行kernel_exit 0,返回用户层。
从xnthread_harden返回后,如果依然在root域,说明任务中有singal信号待处理,依次跳转到2->3->4->5->6->7(如图中虚线所示),最终执行kernel_exit 0,返回用户层。第2步中,因为TSK_TI_FLAGS 中必然有TIF_SIGPENDING,所以一定会跳转到第3步。第6步中,因为之前在第8步中已经关闭了TIP_USERINTRET,所以一定会跳转到第7步。
关闭TIP_USERINTRET的过程:__ipipe_notify_user_intreturn->__ipipe_notify_kevent-> ipipe_kevent_hook -> handle_user_return->ipipe_disable_user_intret_notifier()
与虚拟中断打开的情况对比,直接跳转到第4步,沿着4->5->6->7返回用户空间。本质上,虚拟中断屏蔽的情况下,代表Linux内核并不能响应中断。此时,虽然物理中断发生了,但因为虚拟中断是屏蔽的,所以在物理中断返回过程中,不能让Linux内核感知到有中断发生并运行原中断处理逻辑,因此跳过第1~3步。
与虚拟中断打开的情况对比,直接跳转到第4步。具体原因见上一小节分析。
第4步之后的逻辑,与虚拟中断打开的情况相同。
对于tsk_xenomai_head任务类型,第1~3步和第8步都是处理root域的逻辑,不需要调用。因此沿着4->5->6->7返回用空间。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!