5.3_IPIPE双内核交互之ipipe_flags

5.3 IPIPE: 双内核交互之ipipe_flags

5.3.1 IPIPE改造struct thread_info之ipipe_flags

struct task_struct 是Linux内核用来描述一个进程或线程的最核心的数据结构。每个任务(这里可以理解为进程或者线程)都有一个对应的 task_struct 实例。它包含了任务的所有信息,比如状态、进程ID (PID)、调度信息以及其他特定于任务的各种信息。

struct task_struct 中包含了struct thread_info结构体(CONFIG_THREAD_INFO_IN_TASK=y,否则struct thread_info在内核栈的栈底)。struct thread_info 包含了低级别的任务数据,这些数据是汇编代码(entry.S)需要立即访问的。当从用户空间切换到内核空间时(如执行系统调用或发生中断),内核需要快速获取当前线程的一些基本信息来正确地保存现场(即当前执行环境的状态)并进行上下文切换。因此,这些信息必须存储在一个可以被迅速定位和访问的位置。

在这里插入图片描述

struct thread_info中的flags是一个标志位字段,用于存储与线程相关的低级别的状态信息或配置选项。例如,它可能用来标记线程是否被抢占、是否需要重新调度等。不同的位可以代表不同的含义,例如TIF_SIGPENDING、TIF_NEED_RESCHED等宏定义。这些宏定义以TIF_为前缀,意思是Thread Info Flags!

在这里插入图片描述

如上图,IPIPE在struct thread_info中新增了ipipe_flags,用于Xenomai/Linux双内核之间进行交互。

如下图,ipipe_flags不同的位可以代表不同的含义,例如TIP_MAYDAY、TIP_NOTIFY、TIP_HEAD和TIP_USERINTRET等4中情况。这些宏定义以TIP_为前缀,猜测意思是Thread Info Primary,因为Primary代表Head域。

在这里插入图片描述

5.3.2 ipipe_flags之TIP_HEAD

TIP_HEAD最简单,最好理解,代表进程当前运行在Head域。

当从root域迁移到head域,利用ipipe_set_ti_thread_flag设置TIP_HEAD;

在这里插入图片描述

当从head域进入root域,利用ipipe_clear_thread_flag清除TIP_HEAD.

在这里插入图片描述

5.3.3 ipipe_flags之TIP_NOTIFY

TIP_NOTIFY的机制是最复杂的!

(1)Xenomai线程必有TIP_NOTIFY标记

只要是绑定到xenomai core的进程或线程,一定有TIP_NOTIFY标记!除非收到IPIPE_KEVT_CLEANUP,代表任务退出才清除。

引用ipipe.rst中_binding段落中的说明:

The real-time core should inform the kernel about its intent to receive notifications about that task, by calling :c:func::`ipipe_enable_notifier` when such task is current.

include/linux/ipipe.h:
#define ipipe_enable_notifier(p)                    \
    ipipe_set_ti_thread_flag(task_thread_info(p), TIP_NOTIFY)

内核态线程创建,会调用ipipe_enable_notifier设置TIP_NOTIFY

rtdm_task_init // kernel/xenomai/rtdm/drvlib.c
->xnthread_init // kernel/xenomai/thread.c
    ->__xnthread_init // kernel/xenomai/thread.c
        ->(如果是内核线程)spawn_kthread // kernel/xenomai/thread.c
            ->kthread_trampoline // kernel/xenomai/thread.c
                ->map_kthread // kernel/xenomai/thread.c
                    ->pipeline_enable_kevents //kernel/xenomai/pipeline/kevents.c
                        ->ipipe_enable_notifier // include/linux/ipipe.h

用户态线程创建过程中,也会调用ipipe_enable_notifier设置TIP_NOTIFY。

COBALT_SYSCALL(thread_create)
->__cobalt_thread_create // kernel/xenomai/posix/thread.c
    |->pthread_create //kernel/xenomai/posix/thread.c
    |    ->xnthread_init // kernel/xenomai/thread.c
    |        ->__xnthread_init // kernel/xenomai/thread.c
    |->cobalt_map_user // kernel/xenomai/posix/process.c
        ->pipeline_enable_kevents //kernel/xenomai/pipeline/kevents.c
            ->ipipe_enable_notifier // include/linux/ipipe.h

(2)TIP_NOTIFY分3类syscall/trap/kevent

TIP_NOTIFY定义的notifications,在代码中分为三类syscall、trap、kevent,在include/linux/ipipe_domain.h中有如下宏定义。

在这段宏定义中,后缀 P、E 和 R 分别代表不同的含义:

  • P 代表 “Position”,即位置。在 __IPIPE_SYSCALL_P、__IPIPE_TRAP_P 和 __IPIPE_KEVENT_P 中,P 表示这些事件在某个优先级或位掩码中的位置。
  • E 代表 “Enable”,即启用。在 __IPIPE_SYSCALL_E、__IPIPE_TRAP_E 和 __IPIPE_KEVENT_E 中,E 表示相应事件的启用标志。这些标志是通过将 1 左移相应的位置(由 P 定义)来设置的。
  • R 代表 “Response” 或 “Result”,即响应或结果。在 __IPIPE_SYSCALL_R、__IPIPE_TRAP_R 和 __IPIPE_KEVENT_R 中,R 表示相应事件的响应或结果。这些值是通过将 8 左移相应的位置(由 P 定义)来设置的。- 我觉的是response的可能性比较大,估计是表达正在处理中的意思。
include/linux/ipipe_domain.h:

#define __IPIPE_SYSCALL_P  0
#define __IPIPE_TRAP_P     1
#define __IPIPE_KEVENT_P   2
#define __IPIPE_SYSCALL_E (1 << __IPIPE_SYSCALL_P)
#define __IPIPE_TRAP_E    (1 << __IPIPE_TRAP_P)
#define __IPIPE_KEVENT_E  (1 << __IPIPE_KEVENT_P)
#define __IPIPE_ALL_E      0x7
#define __IPIPE_SYSCALL_R (8 << __IPIPE_SYSCALL_P)
#define __IPIPE_TRAP_R    (8 << __IPIPE_TRAP_P)
#define __IPIPE_KEVENT_R  (8 << __IPIPE_KEVENT_P)
#define __IPIPE_SHIFT_R    3
#define __IPIPE_ALL_R     (__IPIPE_ALL_E << __IPIPE_SHIFT_R)

/* ipipe_set_hooks(..., enables) */
#define IPIPE_SYSCALL   __IPIPE_SYSCALL_E
#define IPIPE_TRAP  __IPIPE_TRAP_E
#define IPIPE_KEVENT    __IPIPE_KEVENT_E

上述三类notifications,应用在不同的场景,来看一下ipipe_set_hooks。

xenomai_init // kernel/xenomai/init.c
    ->cobalt_init // kernel/xenomai/posix/process.c
        ->pipeline_trap_kevents // kernel/xenomai/pipeline/kevents.c

int pipeline_trap_kevents(void)
{
    init_hostrt();
    ipipe_set_hooks(ipipe_root_domain, IPIPE_SYSCALL|IPIPE_KEVENT);
    ipipe_set_hooks(&xnsched_primary_domain, IPIPE_SYSCALL|IPIPE_TRAP);
    return 0;
}

ipipe_set_hooks 的函数,它用于设置root domain和head domain所对应的 ipipe_percpu_domain_data 结构体中的 coflags 字段,以反映新的钩子启用或禁用状态。为啥不把coflags直接定义在root domain和head domain数据结构中的呢?暂时不清楚。

不过,root domain和head domain的coflags是不同的哦!

对于root domain,设置coflags为IPIPE_SYSCALL|IPIPE_KEVENT,代表当实时进程运行在root domain,则可以调用__ipipe_notify_syscall 、 __ipipe_notify_trap告知xenomai处理此两类信号。

对于head domain,设置coflags为IPIPE_SYSCALL|IPIPE_TRAP,代表当实时进程运行在head domain,则可以调用__ipipe_notify_syscall 、 __ipipe_notify_kevent告知xenomai处理此两类信号。

其中,系统调用syscall是共同的,trap是root domain独有的,kevent是head domain独有的。

(3)TIP_NOTIFY需要hook钩子函数来响应

从上面的分析可知,notification是由IPIPE在不同场景,调用__ipipe_notify_syscall、__ipipe_notify_trap、__ipipe_notify_kevent三个接口发送出去的,那么谁是接收者?

Xenomai是接收者,它需要实现一系列hook函数,来完成notifications的处理。

第一,__ipipe_notify_syscall对应的钩子函数

kernel/xenomai/pipeline/syscall.c:

int ipipe_syscall_hook(struct ipipe_domain *ipd, struct pt_regs *regs)

int ipipe_fastcall_hook(struct pt_regs *regs) 

系统调用syscall对应两个hook。

ipipe_fastcall_hook是系统调用快速处理通路,专门用于处理Xenomai实时线程运行在Head域时触发的Xenomai系统调用!

ipipe_syscall_hook是系统调用慢速处理通路,一般需要先把Xenomai实时线程从root domain切换到head domain之后,再处理系统调用。

ipipe_syscall_hook 这个函数的命名很有意思。

  • ipipe: 表明这是ipipe所需的函数

  • syscall:表明这是TIP_NOTIFY中的syscall notification

  • hook:这是一个预埋的钩子函数,需要co-kernel去实现!

第二,__ipipe_notify_trap对应的钩子函数

kernel/xenomai/pipeline/kevents.c:

int ipipe_trap_hook(struct ipipe_trap_data *data)

什么是TRAP?看起来是IPIPE自己创造的软件层面的概念。

arch/arm64/include/asm/ipipe_base.h:

/* ARM64 traps */
#define IPIPE_TRAP_MAYDAY        0	/* Internal recovery trap */
#define IPIPE_TRAP_ACCESS	 1	/* Data or instruction access exception */
#define IPIPE_TRAP_SECTION	 2	/* Section fault */
#define IPIPE_TRAP_DABT		 3	/* Generic data abort */
#define IPIPE_TRAP_UNKNOWN	 4	/* Unknown exception */
#define IPIPE_TRAP_BREAK	 5	/* Instruction breakpoint */
#define IPIPE_TRAP_FPU_ACC	 6	/* Floating point access */
#define IPIPE_TRAP_FPU_EXC	 7	/* Floating point exception */
#define IPIPE_TRAP_UNDEFINSTR	 8	/* Undefined instruction */
#define IPIPE_TRAP_ALIGNMENT	 9	/* Unaligned access exception */
#define IPIPE_NR_FAULTS         10

宏定义的值(如IPIPE_TRAP_ACCESS、IPIPE_TRAP_SECTION、IPIPE_TRAP_DABT等)是用于标识不同类型的异常或陷阱(trap),这些值与ARM64架构的异常类型有一定的对应关系,但并不完全一致。这些宏定义的值是为了在软件层面上区分和处理不同类型的异常,而不是直接对应于硬件错误码。

例如,IPIPE_TRAP_ACCESS可能对应于ARM64架构中的数据访问异常,而IPIPE_TRAP_DABT可能对应于数据中止异常。这些宏定义的值是为了在软件层面上提供一个统一的接口,以便处理不同类型的异常,而不是直接与硬件错误码对应。

在实际的异常处理程序中,这些宏定义的值会被用来索引到相应的处理代码,以便对不同类型的异常进行处理。因此,虽然这些宏定义的值与ARM64 CPU的硬件错误码不完全一致,但它们在软件层面上是用于处理和区分不同类型的异常的关键。

举一个具体的例子,在entry.S中,同步异常el0_fpsimd_acc跳转到do_fpsimd_acc函数,IPIPE在其中插入了__ipipe_report_trap-> __ipipe_notify_trap调用。

do_fpsimd_acc会检查__ipipe_report_trap的返回值,如果返回1则直接return,代表Xenomai已经处理完毕了,不在需要Linux处理。如果返回0,代表继续向下执行原来Linux的处理逻辑。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

紧接着,__ipipe_notify_trap如果发现此时在root domain,直接返回,并不需要xenomai处理trap。如果此时在head domain,则调用ipipe_trap_hook继续处理!

在这里插入图片描述

紧接着,调用ipipe_trap_hook,这是由xenomai来实现的,专门处理trap。

在这里插入图片描述

ipipe_trap_hook具体的实现就不展开了,发现它的返回值非常有意思:

#define KEVENT_PROPAGATE   0
#define KEVENT_STOP        1

再回到do_fpsimd_acc的逻辑,如果返回1则直接return,代表Xenomai已经处理完毕了,不在需要Linux处理。如果返回0,代表继续向下执行原来Linux的处理逻辑。

第三,__ipipe_notify_kevent对应的钩子函数

kernel/xenomai/pipeline/kevents.c:

int ipipe_kevent_hook(int kevent, void *data)

kevent是指一些纯的内核事件(似乎与硬件无关),可能会影响Xenomai的任务管理,所以需要通知Xenomai进行处理。

以下事件在(include/linux/ipipe_domain.h)中定义:

#define IPIPE_KEVT_SCHEDULE 0
#define IPIPE_KEVT_SIGWAKE  1
#define IPIPE_KEVT_SETSCHED 2 //已被废弃,不应该使用
#define IPIPE_KEVT_SETAFFINITY  3
#define IPIPE_KEVT_EXIT     4
#define IPIPE_KEVT_CLEANUP  5
#define IPIPE_KEVT_HOSTRT   6
#define IPIPE_KEVT_CLOCKFREQ    7
#define IPIPE_KEVT_USERINTRET   8
#define IPIPE_KEVT_PTRESUME 9

以IPIPE_KEVT_EXIT来举一个实际的例子。

SYSCALL_DEFINE1(exit, int, error_code) // __arm64_sys_exit, kernel/exit.c
-> do_exit // kernel/exit.c
-> __ipipe_report_exit(tsk); // include/linux/ipipe.h
->__ipipe_notify_kevent(IPIPE_KEVT_EXIT, p); //kernel/ipipe/core.c

__ipipe_notify_kevent会调用ipipe_root_only()确保当前运行在root域。

在这里插入图片描述

Xenomai实现的ipipe_kevent_hook,通过switch case语句根据不同的kevent,调用不同的函数处理。

在这里插入图片描述

5.3.4 ipipe_flags之TIP_MAYDAY

第一,什么是MAYDAY紧急情况

“MAYDAY” 作为紧急情况的信号,源于航空和海事通信领域。它是一个国际公认的无线电求救信号,用来表示船只、飞机或其他交通工具遇到了严重的、直接威胁到生命或安全的紧急状况。选择 “MAYDAY” 这个词是因为它在法语中的发音类似于 “m’aider” 或 “m’aidez”,意为“帮助我”。这个词清晰易读,即使在嘈杂或干扰严重的环境下也能被正确理解。

在Xenomai中,什么被定义成紧急情况?

(1)watchdog_handler 函数

watchdog_handler 函数,用于处理看门狗定时器的触发事件。这个函数的主要功能是检测软件死锁,并采取相应的措施来恢复系统的正常运行。

if (xnthread_test_state(curr, XNUSER)) {
	printk(XENO_WARNING "watchdog triggered on CPU #%d -- runaway thread "
	       "'%s' signaled\n", xnsched_cpu(sched), curr->name);
	xnthread_call_mayday(curr, SIGDEBUG_WATCHDOG);
} else {
	printk(XENO_WARNING "watchdog triggered on CPU #%d -- runaway thread "
	       "'%s' canceled\n", xnsched_cpu(sched), curr->name);
	xnthread_set_info(curr, XNKICKED|XNCANCELD);
}

如果是用户空间线程,则打印一条警告信息,表明看门狗定时器已经触发,并且当前线程可能是一个失控的线程。然后调用 xnthread_call_mayday -> pipeline_raise_mayday -> ipipe_raise_mayday 函数,向当前线程发送一个 SIGDEBUG_WATCHDOG 信号,强制线程退出primary模式,进入secondary模式,即从Xenomai迁移到Linux。

(2)处理SIGKICK信号

SIGKICK信号,用于强制线程退出主模式,即从Xenomai迁移到Linux:

__cobalt_kill ->xnthread_kick->__xnthread_kick->pipeline_raise_mayday

#define SIGSUSP (SIGRTMAX + 1) //挂起线程
#define SIGRESM (SIGRTMAX + 2) //恢复线程
#define SIGRELS (SIGRTMAX + 3) //解除阻塞线程
#define SIGKICK (SIGRTMAX + 4) //强制线程退出主模式
#define SIGDEMT (SIGRTMAX + 5) //将线程降级到 SCHED_OTHER 调度类

这些信号仅在使用 pthread_kill() 函数时可用,用于同步地挂起、恢复、解除阻塞线程,强制它们退出主模式,甚至将它们降级到 SCHED_OTHER 调度类。这些信号不能被阻塞、排队,甚至不能设置在信号集中。它们是非常不规范的,是为了满足特定的实时系统需求而提供的

第二,MAYDAY运行机制

TIP_MAYDAY仅仅是struct thread_info中的ipipe_flags的标志位。怎么告诉Xenomai去处理呢?需要利用TIP_NOTIFY中的trap机制,并且专门定义了一个TRAP类型IPIPE_TRAP_MAYDAY。

当利用ipipe_raise_mayday标记TIP_MAYDAY紧急情况时,需要确保其依赖的TIP_NOTIFY是否置位了。

在这里插入图片描述

IPIPE在处理系统调用(ipipe_handle_syscall)和中断(__ipipe_exit_irq)的过程中,会通过如下过程处理MAYDAY:

__ipipe_call_mayday
->__ipipe_notify_trap
->ipipe_trap_hook
->handle_mayday_event
->xnthread_relax

具体的展开过程如下:

当利用__ipipe_call_mayday处理TIP_MAYDAY时,实际上是调用__ipipe_notify_trap发送IPIPE_TRAP_MAYDAY给ipipe_trap_hook函数。

在这里插入图片描述

Xenomai实现的ipipe_trap_hook总是先处理IPIPE_TRAP_MAYDAY!

在这里插入图片描述

最终,handle_mayday_event调用xnthread_relax将实时线程切换到Linux中运行。

小结:TIP_MAYDAY定义了一种紧急情况,依赖TIP_NOTIFY中的trap机制,在不同的场景下,最终将实时线程切换到Linux中运行。

5.3.5 ipipe_flags之TIP_USERINTRET

已在前面的章节《2.4 EL0_IRQ 中断改造之返回》介绍过,不再重复。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值