双内核主要依靠中断来分发任务给linux kernel和实时os。
资料来源:https://www.bilibili.com/video/BV1Zt421G7wS/
数据结构和相关变量
irq_stage指示irq的运行状态,分为linux(stage0、inband等)和实时os(stage1、oob(out of band)等)。irq_pipeline是一个per cpu变量,用于处理不同os的irq请求,irq_stage是里面的成员变量。irq_pipeline还有log变量,用来记录当前stage发生了哪些中断,inband中的irq必须等oob执行完才能执行。
extern struct irq_stage inband stage;
extern struct irq_stage oob_stage;
struct irq stage{
int index;
const char *name;
}
DEFINE _PER_CPU(struct irq_pipeline_data, irq pipeline) = {
.stages ={
[0]={
.log={
.map = &inband irq_map,
},
.stage = &inband_stage,
},
[1]={
.log ={
.map = &oob_irq_map,
}
}
}
}
struct irq stage data {
struct irq_1og log;
struct irg stage *stage;
#ifdef CONFIG DEBUG IRO PIPELINE
int cpu;
#endif
};
*Per-CPU pipeline descriptor.*
struct irg pipeline data {
struct irq_stage_data stages[2];
struct pt regs tick regs;
#ifdef CONFIG DOVETAIL
struct task struct *task inflight,
struct task struct *rqlock owner;
#ifdef CONFIG KVM
struct kvm oob notifier *vcpu notify;
#endif
#endif
};
irq_domain是linux中的一个概念,作用是把各种设备的中断统一管理(irq chip可能有多个)。
task_struct也是linux中的概念,提供了线程任务管理的信息,与linux不同的是多了一个stall_bits字段,stall_bits表示当前状态是inband(linux)还是oob(实时os)。
双内核操作系统分为2个层次的分类,一个是CPU层级是在inband还是oob,即irq_stage;一个是task层级是在inband还是oob,即stall_bits,两者并不相同。
内核初始化处理
Dovetail 5.15是个分水岭,之前都是以patch形式发布,之后是以完整的仓库代码发布。在5.15版本中,start_kernel函数中修改了5处:
在start_kernel阶段,中断部分已经开启,所以在初始化时需要关中断,以防止中断打断初始化过程。Dovetail在这5处修改中最重要的是irq_pipeline_init_early(),irq_pipeline_init_early会调用fixup_percpu_data函数来初始化上文提及的变量:
在RROS中,额外调用以下3个函数,初始化更多变量:
enable_oob_stage函数会初始化oob stage,oob是可以被关闭的:
enable_tick()会初始化类似中断向量表的结构,中断该如何分发给双内核,由__request_percpu_irq函数初始化oob中断。初始化oob哪些关键中断需要处理,并指定中断处理程序:
dovetail_start设置dovetail_enable变量使得后面检查到Dovetail开启:
中断处理过程
在linux中,关中断是关闭所有中断。但在双内核os中,linux关中断不再是关闭所有中断,只是关闭inband中断。因此需要对对中断处理加一个虚拟层,修改相关的中断函数,使其不能直接修改中断寄存器。而在oob中的中断是真实关中断,linux里的关中断只是不把进入log的中断分发给linux kernel。
中断事件到来时,根据中断是否为之前注册的oob中断进行分发,oob中断直接分发给oob irq handler,linux中断则记录在log中并不会立即执行。
中断切换
inband切换到oob要经过3次切换,因为inband和oob之间有一些依赖关系,第一次切到oob进行task准备,把task控制权交给oob,第二次切回inband等待task准备完毕,把相关signal关闭,第三次task准备完再切到oob,再打开signal:
oob切换到inband:
Dovetail需要修改的代码
通用部分不需要修改,主要修改跟架构相关的部分,和少量设备相关的部分。
irqflags需要根据架构不同重新移植,并且把真实的关中断给oob使用,inband记录中断。
atomic ops需要保证inband里一些原子性操作在被oob抢断后,能够按照原顺序执行。因此,inband执行原子操作时,会调用oob的真实关中断。
ipi handing是CPU之间相互发出的中断,inband和oob处理不同。
entry和fault handing加oob中断向量。