点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
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中有如下宏定义。
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
在这段宏定义中,后缀 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的可能性比较大,估计是表达正在处理中的意思。
上述三类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来举一个实际的例子。
-> 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,调用不同的函数处理。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-优快云博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!