ARM架构IRQ中断流程源码分析(三)
本文分析了Linux内核中断处理流程:从arch_irq_handler_default调用asm_do_IRQ开始,逐步经过handle_IRQ、__handle_domain_irq等函数处理。重点描述了中断上下文的进入过程(irq_enter)和RCU机制的处理,以及中断域(IRQ_DOMAIN)的支持。最后通过generic_handle_irq_desc调用用户注册的中断服务函数,并在i
接着(二)进行分析, 上节讲到arch_irq_handler_default调用asm_do_IRQ,进入c程序。
整体来说,之前的步骤已经完成现场处理和参数传入,接下来要做的是主要是中断嵌套的记录,并根据参数映射得到中断号,从而找到注册好的中断处理函数。
asm_do_IRQ接着调用handle_IRQ,也是在irq.c文件中。
再进一步调用__handle_domain_irq,定义在irqdesc.c文件中。
首先把传入的参数regs保存起来,放在old_regs,记录传入的中断号hwirq。
之后进入irq_enter函数,定义在sofrirq.c。
通过rcu_irq_enter函数通知RCU(Read Copy Update)系统进入中断上下文,保证中断期间RCU读正确。
中间判断是否在空闲进程且不在中断上下文中(此时系统只有一个空闲进程空转),此时不需要唤醒软中断,防止浪费一次调度。所以先关闭软中断、执行 tick_irq_enter()
处理tick中断,之后重启开启。
然后进入__irq_enter宏,定义在include/linux/hardirq.h中。记录进中断时间,硬中断嵌套计数+1,调用trace_hardirq_enter()插桩函数,调试用的。
返回到__handle_domain_irq
根据定义的条件宏CONFIG_IRQ_DOMAIN:表示是否启用中断域,用于支持可编程中断控制器(如 GIC、GPIO 控制器等)做硬件中断号到逻辑中断号的映射。
之后若无异常则调用通用中断分发函数generic_handle_irq,定义在irqdesc.c中。
首先将中断号转换为irq_desc中断描述符。最终会通过中断号从内核中注册好的irq_desc_tree链表中找到对应的irq_desc结构体。
调用generic_handle_irq_desc函数(irqdesc.h),内部最终会调用到irq_desc中的action成员中的中断服务函数。这个就是用户用request_irq函数注册的。
返回到__handle_domain_irq
调用irq_exit处理中断嵌套次数、退出中断时间。将之前保存的old_regs重新设置,恢复现场。
此时可以退出中断。
最后补充一下另一分支,在vector_irq处如果父模式是内核态则跳转进入irq_svc(此种情况一般发生在系统调用时被中断打断)
整个过程大差不差,但被打断之前系统处在svc模式,此时进中断的状态是svc-- 打断 -->irqc-- 中断处理 -->svc。整个过程相对简单一些。__irq_svc定义在entry-armv.S中。
首先进入svc_entry。
与用户态irq分支类似,同样腾出pt_reg的空间,保存r1-r12寄存器,手动保存实际的r0到栈底、保存返回上下文(sp_svc、lr_svc、lr_irq、spsr_irq、实际r0)到 pt_regs。
接下来这一段与用户态区别开来,由于线程在svc态被打断,此时的线程处于特权模式,默认能访问所有地址空间(通过addr_limit
字段设置),而在进中断处理时需要维持用户态权限(有限的地址访问空间),所以必须暂时修改权限。
通过get_thread_info tsk获取当前任务的 thread_info
结构体地址,包含线程的低级信息。
读取当前任务的地址访问限制addr_limit到r0。
修改当前线程的访问范围为只允许访问用户空间地址。
保存旧的地址访问限制addr_limit到r0。
最后四行从硬件层面彻底禁用对用户空间的访问权限(这里特指直接解引用地址,若通过特定函数如copy_from_usr是可以访问的)。
返回到__irq_svc
进入irq_handler,与用户态分支一样
从中断返回时,通过svc_exit宏返回(定义在entry-header.S中)
恢复地址访问限制
恢复处理器状态、寄存器等。
更多推荐
所有评论(0)