2.3.6 返回用户空间

Interrupt Pipeline系列文章大纲-CSDN博客

        2.3 el0_irq

                2.3.1 el0_irq代码框架
                2.3.2 kernel_entry 0 与kernel_exit 0
                2.3.3 el0_irq_naked与enable_da_f
                2.3.4 trace_hardirqs_off与trace_hardirqs_on
                2.3.5 irq_handler
                2.3.6 返回用户空间

目录

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域


2.3.6.1 概要

       当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

不可能

不可能

可能

        接下来按照场景进行分析。 

2.3.6.2 位于root域且虚拟中断打开

       因为位于root域,此时用户态任务只可能是tsk_linux_root和tsk_xenomai_root两种类型。

2.3.6.2.1 tsk_linux_root任务类型 

2.3.6 返回用户空间_第1张图片

第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,返回用户空间。

2.3.6.2.2 task_xenomai_root任务类型

首先沿着实线从第1步走到第5步,与task_linux_root的过程完全相同。

2.3.6 返回用户空间_第2张图片

第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()

2.3.6.3 位于root域且虚拟中断屏蔽
2.3.6.3.1 tsk_linux_root任务类型

        与虚拟中断打开的情况对比,直接跳转到第4步,沿着4->5->6->7返回用户空间。本质上,虚拟中断屏蔽的情况下,代表Linux内核并不能响应中断。此时,虽然物理中断发生了,但因为虚拟中断是屏蔽的,所以在物理中断返回过程中,不能让Linux内核感知到有中断发生并运行原中断处理逻辑,因此跳过第1~3步。

2.3.6 返回用户空间_第3张图片

2.3.6.3.2 task_xenomai_root任务类型

        与虚拟中断打开的情况对比,直接跳转到第4步。具体原因见上一小节分析。

        第4步之后的逻辑,与虚拟中断打开的情况相同。

2.3.6 返回用户空间_第4张图片

2.3.6.4 位于head域

       对于tsk_xenomai_head任务类型,第1~3步和第8步都是处理root域的逻辑,不需要调用。因此沿着4->5->6->7返回用空间。

2.3.6 返回用户空间_第5张图片

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

你可能感兴趣的:(Interrupt,Pipeline,linux,运维,服务器)