Interrupt Pipeline系列文章大纲-CSDN博客
2.5 el1_irq
2.5.1 el1_irq代码框架
2.5.2 kernel_entry 1与kernel_exit 1
2.5.3 irq_handler返回值
2.5.4 对CONFIG_PREEMPT的处理
2.5.4.1 抢占式内核
没有CONFIG_PREEMPT,就是非抢占式内核,即内核层的代码是不可能抢占的。如果代码运行内核层即运行在EL1异常级别,el1_irq的irq_handler执行完毕后,不会触发调度。
- 进程通过系统调用陷入到内核态的时候,进入EL1执行态。如果有更高优先级的进程,只有在系统调用返回用户空间的时候,才可被调度程序调度,由高优先级的进程占用CPU。
- 内核进程在运行时,也在EL1执行态,无法被抢占。
CONFIG_PREEMPT代表在内核层发生中断时,可以进行抢占。对于抢占式内核而言,即便是从中断上下文返回内核空间的进程上下文,只要内核代码不在临界区内,就可以发生抢占,让最高优先级的任务调度执行。
禁止内核抢占的情况列出如下:
(1)内核执行中断处理例程时不允许内核抢占,中断返回时再执行内核抢占。
(2)当内核执行软中断或tasklet时,禁止内核抢占,软中断返回时再执行内核抢占。
(3)在临界区禁止内核抢占,临界区保护函数通过抢占计数宏控制抢占,计数大于0,表示禁止内核抢占。
为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_ count,称为内核抢占锁。这一变量被设置在进程的PCB结构task_struct中。每当内核要进入以上几种状态时,变量preempt_ count就加1,指示内核不允许抢占。每当内核从以上几种状态退出时,变量preempt_ count就减1,同时进行可抢占的判断与调度。
2.5.4.2 __ipipe_preempt_schedule_irq
引入了I-pipe后,__ipipe_preempt_schedule_irq取代了preempt_schedule_irq来完成抢占。
#ifdef CONFIG_PREEMPT el1_preempt: mov x24, lr #ifdef CONFIG_IPIPE 1: bl __ipipe_preempt_schedule_irq #else 1: bl preempt_schedule_irq // irq en/disable is done inside #endif ldr x0, [tsk, #TSK_TI_FLAGS] // get new tasks TI_FLAGS tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling? ret x24 #endif |
此时,已经从中断栈切换到了内核栈。此处发生抢占,也是不同进程内核栈直接的切换。但是此处可能会无限抢占。所以需要在__ipipe_preempt_schedule_irq中,1)打开物理中断,让xenomai得以被执行。2)不断处理积压的虚拟中断。
1 | void __sched __ipipe_preempt_schedule_irq(void) | ||
2 | { | ||
3 | struct ipipe_percpu_domain_data *p; | ||
4 | unsigned long flags; | ||
5 | |||
6 | if (WARN_ON_ONCE(!hard_irqs_disabled())) | ||
7 | hard_local_irq_disable(); | ||
8 | |||
9 | local_irq_save(flags); | ||
10 | hard_local_irq_enable(); | ||
11 | preempt_schedule_irq(); /* Ok, may reschedule now. */ | ||
12 | hard_local_irq_disable(); | ||
13 | |||
14 | /* | ||
15 | * Flush any pending interrupt that may have been logged after | ||
16 | * preempt_schedule_irq() stalled the root stage before | ||
17 | * returning to us, and now. | ||
18 | */ | ||
19 | p = ipipe_this_cpu_root_context(); | ||
20 | if (unlikely(__ipipe_ipending_p(p))) { | ||
21 | trace_hardirqs_on(); | ||
22 | __clear_bit(IPIPE_STALL_FLAG, &p->status); | ||
23 | __ipipe_sync_stage(); | ||
24 | } | ||
25 | |||
26 | __ipipe_restore_root_nosync(flags); | ||
27 | } |
第6~7行,如果硬件中断没有关闭,进行警告,并把硬件中断关闭。注意一旦硬件关闭,那么实时内核也接收不到中断。
第9行,关闭虚拟中断,并将当前虚拟中断状态保存到flags变量。
第10~12行,在打开硬件中断的情况下,调用preempt_schedule_irq,之后再关闭硬件中断。保证了在执行preempt_schedule_irq期间,实时内核可以接收硬件中断。
第14~24行,第9行关闭了虚拟中断,而后在第11行执行preempt_schedule_irq期间,虚拟中断又经历了打开、关闭的过程。只要存在虚拟中断被关闭的情况,那么就有可能出现虚拟中断积压并记录到虚拟中断日志中,需要在此处调用__ipipe_sync_stage()对虚拟中断日志的每一条日志进行处理,调用相应虚拟中断处理函数。在__ipipe_sync_stage()执行过程中,硬件中断会被打开,保证实时内核可以接收硬件中断。
第26行,恢复在第9行中保存的虚拟中断状态。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!