Interrupt Pipeline
文章平均质量分 89
aspirestro三水哥
No STreams, neither Rivers nor Oceans.
不积小流,何以成江海?三万里河,奔流到海!
展开
-
Xenomai/Interrupt Pipeline系列文章大纲
1 I-pipe介绍2 I-pipe对ARM64异常向量表的改造2.1 ARM64中断机制http://t.csdnimg.cn/FwMas2.2 ARM64异常处理2.3 el0_irq2.4 el0_sync2.5 el1_irq2.6 el0_sync原创 2024-07-30 10:43:20 · 516 阅读 · 0 评论 -
4.2.1 通过DTS传递物理中断号给Linux
以dts中的timer设备为例,说明一下硬件中断号是如何传入Linux中的。原创 2024-09-30 21:06:12 · 646 阅读 · 0 评论 -
4.1 Xenomai如何初始化
Xenoami的初始化入口函数是xenomai_init,通过device_initcall(xenomai_init)向do_initcalls()注册回调函数。在Linux启动过程中,完整的调用堆栈如下图左侧所示。Xenomai是一个非常复杂的实时内核,这里不展开对它的分析,而是把重点放在Xenomai和IPIPE相交的部分,特别是关于中断的部分。原创 2024-09-24 23:46:03 · 2293 阅读 · 0 评论 -
3.5.2 __ipipe_init()之完成中断处理程序设置
__ipipe_init()最核心的就是__ipipe_enable_pipeline(),接下来对其展开分析!__ipipe_enable_pipeline()又可以分成两大部分来分析。本小节分析第二部分:完成中断处理程序的设置。原创 2024-09-22 17:45:58 · 744 阅读 · 0 评论 -
3.5.1 __ipipe_init()之IPIPE_CRITICAL_IPI
发送并处理IPIPE_CRITICAL_IPI原创 2024-09-21 23:52:50 · 1418 阅读 · 0 评论 -
3.4.4 __ipipe_init_early之再论虚拟中断
点击查看系列文章 =》3.4.4 __ipipe_init_early之再论虚拟中断根据《3.4.1.2 IPIPE对Linux中断号的改造》的分析,IPIPE引入的虚拟中断virtual interrupt的概念,其中前10个虚拟中断本质上是利用SGI实现的IPI中断。IPIPE在原来Linux原生的7个NR_IPI的基础上,多定义了3个IPIPE_OOB_IPI_NR。虚拟中断virtual interrupt总共是64个呢,其余的怎么用呢?原创 2024-09-18 16:19:16 · 1077 阅读 · 0 评论 -
3.4.3 __ipipe_init_early之初始化root domain
点击查看系列文章 =》3.4.3之初始化root domain如下图所示,红框里面的函数当前都是空的,本章还是分析蓝框中的代码片段。第295行,变量ipd指向了ipipe_root即ipd代表root domain。第305行,root domain的名字被命名为“Linux”。第306行,初始化ipd->context_offset变量,详细分析见《3.4.1.1 IPIPE基础数据结构》。第307行,调用init_stage(ipd)对root domain进行初始化。原创 2024-09-16 22:48:55 · 1819 阅读 · 0 评论 -
3.4.2 __ipipe_init_early之fixup_percpu_data()
点击查看系列文章 =》3.4.2 __ipipe_init_early之fixup_percpu_data()这个函数只有在CPU是SMP对称多core的情况下,才会真正运作,否则就是个空函数。在Linux处于启动过程中,而实时内核还没有注册到IPIPE中时,应该指向ipipe_percpu.root。但是,,在静态定义时是无法让ipipe_percpu.curr指向ipipe_percpu.root的。为什么?原创 2024-09-15 22:57:52 · 603 阅读 · 0 评论 -
3.4.1.3 IPIPE interrupt log数据结构
点击查看系列文章 =》3.4.1.3 IPIPE interrupt log数据结构IPIPE interrupt log的概念,来自于IPIPE中断分发的核心函数__ipipe_dispatch_irq(kernel/ipipe/core.c)里面的注释。这个函数的具体流程会在后面的章节讨论,这里只讨论interrupt log的数据结构是怎么设计的。第一步,先把中断记录到interrupt log。看一下这个截取自__ipipe_dispatch_irq 函数的图片。原创 2024-09-14 23:34:28 · 902 阅读 · 1 评论 -
3.4.1.2 IPIPE对Linux中断号的改造
截止目前,我们分析出了3组和中断相关数字:IPIPE_NR_IRQS = IPIPE_NR_XIRQS + IPIPE_NR_VIRQSGIC V3 INTID = SGI + hwirq(PPI + SPI + LPI)Linux virq的数量 = IRQ_BITMAP_BITS它们的关系到底是什么呢?接下来用一副图来揭秘!原创 2024-09-13 21:51:50 · 1328 阅读 · 0 评论 -
3.4.1.1 IPIPE基础数据结构
点击查看系列文章 =》3.4.1.1 IPIPE基础数据结构先上图,按图索骥,一图抵千言。前面章节多次提到了Head域(头域)和Root域(根域),它们在IPIPE中抽象成数据结构struct ipipe_domain。一切从根开始,先说Root域(根域)。Root域的实例是一个全局变量ipipe_root,它里面的成员ipipe_root.name后续会初始化为“Linux”。为方便访问ipipe_root,为它定义了一个全局指针ipipe_root_domain,后面分析时基本都使用。原创 2024-09-13 14:26:39 · 894 阅读 · 0 评论 -
3.3.2 IPIPE对Linux中断使能与屏蔽的改造
在,IPIPE patch为它多增加了一个头文件,正是这个新的头文件抢先定义了local_irq_disable最终调用的arch_local_irq_disable,把原先对DAIF寄存器的操作变成了对虚拟中断标志的操作ipipe_stall_root。是的,IPIPE重新定义一套函数来操作物理中断标志位DAIF,前缀是hard_local_irq_xxx,同样是两对函数。具体是怎么做到的呢?原创 2024-09-01 21:46:30 · 792 阅读 · 0 评论 -
3.3.1 Linux中断的使能与屏蔽
在分配sturct irq_desc的过程,desc->status_use_accessors是初始化为0的,所以_IRQ_DISABLE_UNLAZY是没有置位的,入参mask的值为0。IRQD_IRQ_DISABLED标记。如果IRQD_IRQ_DISABLED标记置位了,则调用mask_irq(desc)清空中断控制器的使能bit: desc->irq_data.chip->irq_mask(&desc->irq_data)->gic_mask_irq-> gic_poke_irq(d,原创 2024-08-29 14:49:31 · 1457 阅读 · 0 评论 -
3.2.6 盘古开天地start_kernel
2号进程是0号进程调用kernel_thread->_do_fork创建的,是一个内核进程,入口函数是kthreadd,它利用循环不断地判断kthread_create_list链表节点是否不为空,只要不为空,就取出链表节点,调用create_kthread-> kernel_thread->_do_fork创建相应的内核线程,然后删除链表节点。1号进程的入口函数是kernel_init,它在当前根目录下寻找init程序,通过execve执行init程序转到用户态,变身为用户进程。第657行,打开中断。原创 2024-08-24 20:13:35 · 904 阅读 · 0 评论 -
3.2.5 宙之CPU的时分复用
点击查看系列文章。原创 2024-08-23 13:18:57 · 842 阅读 · 0 评论 -
3.2.3 从头初始化到身体
最简单粗暴的办法就是给每个任务分一块地盘,但是任务和任务是有区别的,有的任务贪心,不断malloc,很快就会发现自己的地盘不够用;为了让进程听话,内核身先士卒,自身也不能搞特殊化,也得活在虚拟内存空间中,只不过内核算是既得利益者,它独占了一大片的物理内存页面。进程访问虚拟内存的某个地址时,这个地址总是属于一个虚拟页面,映射表能完成从虚拟页面到物理页面的查找,这样虚拟转成了现实。它欺骗进程,告诉它你有一块超大的内存空间可以用,并且已经帮你切分成一个一个大小一致的小块(即页面,一般是4KB),方便取用。原创 2024-08-22 22:51:22 · 611 阅读 · 0 评论 -
3.2 内核初始化(盘古开天地)
宙者,古往今来,时间为宙。盘古作为0号进程,在head.S汇编代码中,沿着_head(头)->stext(身)->__primary_switch(MMU)->__primary_switched(进程)完成了上述4个步骤,之后就可以跳转到C语言函数start_kernel开始开天辟地。Linux内核的初始化是从0开始的,Documentation/arm64/booting.txt里面写明了,此时中断是完全关闭的,一切似乎都是静止的。所以,只要在源码中定义了.head.text段,就会链接到_text。原创 2024-08-22 22:49:09 · 1320 阅读 · 0 评论 -
3.1 I-pipe四行启动代码
Linux内核的初始化流程是什么样的?是怎么走到start_kernel的?为什么I-pipe选择在start_kernel中进行初始化?四行代码的位置有什么学问?I-pipe的启动代码位于init/main.c: start_kernel函数中。越是简单的代码,背后的故事越多!下一章节,展开这个故事。原创 2024-08-22 22:41:57 · 218 阅读 · 0 评论 -
2.5.4 对CONFIG_PREEMPT的处理
为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_ count,称为内核抢占锁。第14~24行,第9行关闭了虚拟中断,而后在第11行执行preempt_schedule_irq期间,虚拟中断又经历了打开、关闭的过程。对于抢占式内核而言,即便是从中断上下文返回内核空间的进程上下文,只要内核代码不在临界区内,就可以发生抢占,让最高优先级的任务调度执行。第10~12行,在打开硬件中断的情况下,调用preempt_schedule_irq,之后再关闭硬件中断。原创 2024-08-16 21:44:02 · 661 阅读 · 0 评论 -
2.5 el1_irq
因此,kernel_entry 1保存struct pg_regs的位置,并不是进程内核栈的栈底位置,而是根据实际情况,在进程内核栈中间的某个位置。第18行,在kernel_ventry中,SP_EL1减去了#S_FRAME_SIZE。第2~10行,是对kernel_entry 1的第20~25行的反向操作,还原tast_struct.thread_info.addr_limit的值。第32行,内核栈指针sp_el1弹出,向上走#S_FRAME_SIZE,与kernel_entry压栈的大小一致。原创 2024-08-16 21:31:23 · 960 阅读 · 0 评论 -
2.4 el0_sync
首先读取ESR_EL1寄存器,向右移位ESR_ELx_EC_SHIFT(26)得到异常发生的原因(Bit 31~26),然后跳转到相应的分支继续处理。根据注释,它代表进程运行在HEAD域。第6行,如果TSK_TI_IPIPE中没有设置_TIP_HEAD标记,第14行tst指令(相当于ands)结果为0,则Z标志位为1,b.eq判断条件成立。第7行,如果TSK_TI_IPIPE中设置_了TIP_HEAD标记,第14行tst指令(相当于ands)结果为1,则Z标志位为0,b.eq判断条件不成立。原创 2024-08-14 21:20:06 · 548 阅读 · 0 评论 -
2.3.6 返回用户空间
2.3.6.1 概要当el0_irq发生时,被中断的用户态任务有三种类型:任务类型说明1Linux进程,运行在root域。2Xenomai进程,已经迁移到root域即作为Linux进程进行调度3Xenomai进程,在head域运行。综合上一章节中断发生的场景,可能出现的组合如下表。接下来按照场景进行分析。场景/任务类型返回值位于root域,虚拟中断打开1可能可能不可能位于root域,虚拟中断屏蔽0可能可能不可能位于head域0不可能不可能可能。原创 2024-08-13 22:32:16 · 1024 阅读 · 0 评论 -
2.3.5 irq_handler
新增的handle_arch_irq_pipelined除了调用原先的handle_arch_irq之外,就是改变了irq_handler的返回值。根据返回值的不同,el0_irq后续的代码走向是不同的,后面章节会相信分析,这里重点分析handle_arch_irq。如果打开了CONFIG_IPIPE配置项,那么函数调用顺序irq_handler->handle_arch_irq变成了irq_handler->handle_arch_irq_pipelined->handle_arch_irq。原创 2024-08-12 21:46:20 · 1123 阅读 · 0 评论 -
1.3 拉取I-pipe代码
所以,这个思路是把upstream的修改,不断合并到ipipe自己的分支ipipe/4.19.y-cip。根据上面的分析,ipipe-core-4.19.209-cip59-arm64-12.patch是哪两个分支或commit diff出来的呢?经过对比,是第(5)步和第(2)步的git diff的结果。也可以直接从I-pipe的git代码仓库,直接拉取代码,切换到对应的tag。可以下载I-pipe的patch,通过git apply到kernel.org的Linux内核。,能看到最原始的修改。原创 2024-08-06 22:04:14 · 923 阅读 · 0 评论 -
1.2 I-pipe基本概念
Head Stage是一个独立的高优先级执行阶段,一旦收到中断,立刻调用out-of-band中断处理程序,丝毫不会被Root Stage中的In-band代码影响。当根域的虚拟中断标志处于打开状态,从头域传递过来的中断可以立即被in-band中断处理程序处理。为了保证头域中的out-of-band代码的实时性,根域中的in-band代码一定不能关闭CPU的物理中断。既要保证头域中的out-of-band中断处理程序随时响应物理中断,又要保证根域中的in-band中断处理程序仅仅在根域能接收中断时执行。原创 2024-08-06 19:44:18 · 961 阅读 · 0 评论 -
1.1 I-pipe与Xenomai
但是RTAI更新速度比较慢,最新的版本是2021/5/19发布的RTAI 5.3,适配Linux 4.19,主页上宣称支持的硬件是x86和x86_64,对ARM的支持处于rework中。Xenomai2目前已经EOL,Xenomai4比较新,而Xenomai3比较稳定,处于长期维护中,应该是当前使用比较广泛的版本。这里重点学习Xenomai3.2.1版本。第二种,采用双内核的办法,在硬件之上运行一个实时内核,把Linux内核当成的实时内核的一个普通的进程,其代表有RTLinux、RTAI和Xenomai。原创 2024-08-01 12:11:28 · 823 阅读 · 0 评论 -
2.3.4 trace_hardirqs_off与trace_hardirqs_on
如果打开内核配置项CONFIG_TRACE_IRQFLAGS,那么在硬件中断关闭和打开的过程中,通过调用trace_hardirqs_off/trace_hardirqs_on函数可以记录硬件中断关闭和打开的事件。两个函数调用的tracer_hardirqs_off/tracer_hardirqs_off,服务于ftrace的众多tracer之一irqsoff(CONFIG_IRQSOFF_TRACER)。ipipe_root_p) //如果是非root域,直接return。原创 2024-07-30 21:54:04 · 831 阅读 · 0 评论 -
2.3.3 el0_irq_naked与enable_da_f
enable_da_f由commit b282e1ce29 arm64: entry.S: convert elX_irq引入,在comments里面写明,在处理IRQ中断时,应该使能调试异常Debug和SError,同时顺便打开了FIQ。el0_irq_naked只有在el0_irq_compat中才调用。此时因为已经调用了kernel_entry 0,32进行了用户态现场保存,因此必须避免重复调用el0_irq中已经调用的kernel_entry 0,直接跳转到标签el0_irq_naked。原创 2024-07-30 21:03:07 · 230 阅读 · 0 评论 -
2.3.2 kernel_entry 0 与kernel_exit 0
因为从用户栈切到内核栈,因此对于内核栈来说,不存在上一个caller的栈帧,所以pt_regs中定义的u64 stackframe[2]代表的FP/LR的值都设置为0.栈空间是在kernel_ventry中按照struct pt_regs的大小预留好的,所以相当于压入struct pt_regs的reg[0]~reg[29]。第24行,将elr_el1存入X22。第 31行,将X22和X23压栈,就是将elr_el1和spsr_el1的值压入struct pt_regs的pc变量和pstate变量。原创 2024-07-30 14:37:55 · 903 阅读 · 0 评论 -
2.3.1 el0_irq代码框架
首先沿着vectors->kernel_ventry 0,irq ->el0_irq分析。I-pipe的主要修改点如图中红色字体所示。接下来会对el0_irq中的每一行展开分析。当el0_irq发生时,被中断的用户态进程有两种:一种是Linux进程,一种是迁移到Linux的Xenomai进程。原创 2024-07-30 11:28:25 · 215 阅读 · 0 评论 -
2.1 ARM64中断机制
CPU收到一个中断号n后,会去中断描述表IDT(Interrupt Descriptor Table)中,找到第n个中断描述符,从中断描述符中找到中断处理程序。中断信号通常被分为同步中断和异步中断两大类。异步异常发生时,处理器正在处理的指令和异常是完全没有关系的,它们之间没有依赖关系,常见的异步异常包括物理中断和虚拟中断。ARM64的中断机制的顶层概念是异常,同步中断和异步中断被分别称为同步异常和异步异常两种。在不同的CPU架构中,对于中断机制的设计与实现不同,对中断信号的概念划分也有所不同。原创 2024-07-29 23:59:39 · 819 阅读 · 0 评论 -
2.2 ARM64异常处理
异常级别EL0不处理任何异常,必须在更高的异常级别中处理。不同的异常级别有各自独立的VBAR,例如VBAR_EL1,VBAR_EL2,VBAR_EL3等。在异常级别变化的时候,根据是否使用SP0,异常向量表中的表项不同。Linux的运行只涉及EL0和EL1,即Linux内核运行在异常级别EL1,应用程序运行在异常级别EL0. 在EL0或EL1中发生的异常,都会跳转到EL1的异常向量表,根据异常的类别,找到对应的表项。异常级别分为4级,其中EL0不处理异常。当EL0发生异常时,会进入更高的异常级别进行处理。原创 2024-07-30 10:49:09 · 1049 阅读 · 0 评论