点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
3.4.1.1 IPIPE基础数据结构
先上图,按图索骥,一图抵千言。
前面章节多次提到了Head域(头域)和Root域(根域),它们在IPIPE中抽象成数据结构struct ipipe_domain。一切从根开始,先说Root域(根域)。
Root域的实例是一个全局变量ipipe_root,它里面的成员ipipe_root.name后续会初始化为“Linux”。为方便访问ipipe_root,为它定义了一个全局指针ipipe_root_domain,后面分析时基本都使用ipipe_root_domain代指Root域。
kernel/ipipe/core.c:
struct ipipe_domain ipipe_root;
EXPORT_SYMBOL_GPL(ipipe_root);
include/linux/ipipe_domain.h:
#define ipipe_root_domain (&ipipe_root)
Head域的实例化分为两个阶段。第一阶段是IPIPE初始化阶段,定义一个全局指针*ipipe_head_domain指向Root域ipipe_root。第二阶段是Xenomai初始化阶段,调用ipipe_register_head向IPIPE注册名字为“Xenomai”的Head域:xnsched_primary_domain。后面分析时基本都使用ipipe_head_domain代指Head域。
//第一阶段,全局指针指向ipipe_root
kernel/ipipe/core.c:
struct ipipe_domain *ipipe_head_domain = &ipipe_root;
EXPORT_SYMBOL_GPL(ipipe_head_domain);
//第二阶段,Xenomai注册到Head域
kernel/xenomai/pipeline/init.c:
pipeline_init()->ipipe_register_head(&xnsched_primary_domain, "Xenomai");
IPIPE是支持SMP对称多核CPU的,所以还得为每一个CPU core定义per cpu变量ipipe_percpu,它的数据结构是struct ipipe_percpu_data。这个数据结构里面的成员很多,当前先关注其中的三个变量:root、head、curr!
include/linux/ipipe_domain.h
struct ipipe_percpu_domain_data {
unsigned long status; /* <= Must be first in struct. */
unsigned long irqpend_0map;
#if __IPIPE_IRQMAP_LEVELS >= 3
unsigned long irqpend_1map[IPIPE_IRQ_1MAPSZ];
#if __IPIPE_IRQMAP_LEVELS >= 4
unsigned long irqpend_2map[IPIPE_IRQ_2MAPSZ];
#endif
#endif
unsigned long irqpend_map[IPIPE_IRQ_MAPSZ];
unsigned long irqheld_map[IPIPE_IRQ_MAPSZ];
unsigned long irqall[IPIPE_NR_IRQS];
struct ipipe_domain *domain;
int coflags;
};
struct ipipe_percpu_data {
struct ipipe_percpu_domain_data root;
struct ipipe_percpu_domain_data head;
struct ipipe_percpu_domain_data *curr;
struct pt_regs tick_regs;
int hrtimer_irq;
struct task_struct *task_hijacked;
struct task_struct *rqlock_owner;
struct ipipe_vm_notifier *vm_notifier;
unsigned long nmi_state;
struct mm_struct *active_mm;
#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
int context_check;
int context_check_saved;
#endif
};
kernel/ipipe/core.c:
DEFINE_PER_CPU(struct ipipe_percpu_data, ipipe_percpu) = {
.root = {
.status = IPIPE_STALL_MASK,
.domain = &ipipe_root,
},
.curr = &bootup_context,
.hrtimer_irq = -1,
#ifdef CONFIG_IPIPE_DEBUG_CONTEXT
.context_check = 1,
#endif
};
EXPORT_PER_CPU_SYMBOL(ipipe_percpu);
如何理解root和head两个成员?为了记录Root域ipipe_root_domain和Head域ipipe_head_domain在每个CPU core上留下的身影(即上下文context),struct ipipe_percpu_data为它们各自量身打造了变量root和head,数据结构是struct ipipe_percpu_domain_data。域的运行上下文context有哪些?每个CPU core都要有各自的虚拟中断标志位,就是struct ipipe_percpu_domain_data的成员status。在代码中,ipipe_percpu.root.status被初始化为IPIPE_STALL_MASK。为了方便的找到Root域和Head域,ipipe_percpu.root.domain和ipipe_percpu .head.domain被初始化为ipipe_root_domain和ipipe_head_domain!
如果理解curr成员?每个CPU core当前处于root和head? struct ipipe_percpu_data又定义了一个curr(current的缩写)指针来指向root或head。
基础数据结构介绍完了,接下来介绍一下各个数据结构相互访问的API。
从struct ipipe_percpu_data到struct ipipe_percpu_domain_data,再到struct ipipe_domain,是逐层包含的关系,利用指针可以非常方便地正向地找到每个想要的数据结构或变量,具体如下:
include/linux/ipipe_domain.h:
//获取 ipipe_percpu.root
static inline struct ipipe_percpu_domain_data * ipipe_this_cpu_root_context(void)
//获取 ipipe_percpu.head
static inline struct ipipe_percpu_domain_data * ipipe_this_cpu_head_context(void)
//获取 ipipe_percpu.curr
static inline struct ipipe_percpu_domain_data * __ipipe_get_current_context(void)
#define __ipipe_current_context __ipipe_get_current_context()
static inline struct ipipe_percpu_domain_data *ipipe_current_context(void)
//获取 ipipe_percpu.curr.domain
static inline struct ipipe_domain *__ipipe_get_current_domain(void)
#define __ipipe_current_domain __ipipe_get_current_domain()
static inline struct ipipe_domain *ipipe_get_current_domain(void)
反向思考,假设当前硬塞(传递)过来一个大冤种ipipe_domain指针,它自己都不知道它指向root域还是head域,如何反向找到准确的找到ipipe_percpu.root或是ipipe_percpu.head?
最简单粗暴的办法,就是判断这个大冤种ipipe_domain的身份,然后正向查询。
如果 ipipe_domain 和 ipipe_root_domain相等
ipipe_this_cpu_root_context()
否则
ipipe_this_cpu_head_context()
IPIPE Patch里面给了一个巧妙的办法。在root域和head域初始化过程中,为其成员context_offset进行赋值:
//在root域初始化过程中赋值
kernel/ipipe/core.c: __ipipe_init_early ()
ipipe_root_domain->context_offset = offsetof(struct ipipe_percpu_data, root)
//在head域初始化过程中赋值
kernel/ipipe/core.c: init_head_stage()
ipipe_head_domain->context_offset = offsetof(struct ipipe_percpu_data, head);
//通过context_offset直接拿到struct ipipe_percpu_domain_data结构指针
static inline struct ipipe_percpu_domain_data *
__context_of(struct ipipe_percpu_data *p, struct ipipe_domain *ipd)
{
return (void *)p + ipd->context_offset;
}
IPIPE的基本数据结构介绍告一段落,接下来重点介绍为中断新增的数据结构。
点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!