1、基本概念
事件通道(Event Channel)是Xen用于Dom和Xen之间、Dom和Dom之间的异步事件通知机制,事件通道的应用非常广泛,Xen体系结构上的物理中断(pIRQ)、虚拟中断(vIRQ)、虚拟处理器间中断(Virtual Inter-Processor Interrupt,vIPI)以及Dom域间通信(Inter-Domain Communication,IDC)均需通过事件通道实现。
事件通道机制和超级调用机制一起完成Xen和Dom之间的控制和交互,即:使用超级调用产生从Dom到Xen的同步调用;使用异步事件机制完成从Xen到Dom的通知(Notification)递交。由事件通道提供的从Xen到Dom的通信机制能够取代常用的利用设备中断的递交机制,使得采用轻量级的通知形式成为可能。域间通信(IDC)机制,配合以I/O环(I/O Ring)为基础的共享内存机制使得Dom之间传递信息和数据更加有效。
Xen系统中,每个Dom都能够拥有自己的事件通道。系统规定,每个Dom最多能够分配NR_EVENT_CHANNELS个事件通道。在X86平台上,宏NR_EVENT_CHANNELS的值为1024,即最多能够拥有1024个事件通道。
//xen/include/public/xen.h 47-51
/*每个Dom的事件通道个数:*/
#define NR_EVENT_CHANNELS (sizeof(unsigned long) * sizeof(unsigned long) * 64)
每个事件通道都有自己唯一的编号,即端口(port),其范围为0 -- NR_EVENT_CHANNELS-1。在X86平台上,事件通道的端口范围为0—1023,这些事件通道被分为8个组(Bucket),每组128个。
//xen/include/xen/sched.h 40-41
#define EVTCHNS_PER_BUCKET 128
#define NR_EVTCHN_BUCKETS (NR_EVENT_CHANNELS / EVTCHNS_PER_BUCKET)
系统为每个Dom分配NR_EVTCHN_BUCKETS个事件通道,每个事件通道与一个结构体evtchn对应。这些结构体组成一个长度为NR_EVTCHN_BUCKETS的结构体数组,供Dom调用。结构体数组的定义在代表Dom的结构体domain中。
//xen/include/xen/sched.h 143-227
struct domain
{
domid_t domain_id;
shared_info_t *shared_info; /* 共享数据区 */
……
/*事件通道信息*/
//数组指针,定位一个事件通道需要二维信息(哪一组中的哪一个)
struct evtchn *evtchn[NR_EVTCHN_BUCKETS];
spinlock_t evtchn_lock;
struct grant_table *grant_table;
/*对事件通道映射的中断*/
u16 pirq_to_evtchn[NR_IRQS];
DECLARE_BITMAP(pirq_mask, NR_IRQS);
……
struct vcpu *vcpu[MAX_VIRT_CPUS];
/*CPU的掩码将控制每个域的状态*/
cpumask_t domain_dirty_cpumask;
……
};
在结构体domain中,长度为NR_EVTCHN_BUCKETS的结构体数组以二维数组的形式存在,即满足8组,每组128个事件通道的划分。事件通道端口号和数组下标之间一一对应,它们之间的转换由系统定义的宏完成。这些宏主要包括bucket_from_port(d,p)、port_is_valid(d,p)和evtchn_from_port(d,p)。其中,port_is_valid(d,p)用来判断端口号是否有效以及端口号对应的事件通道是否存在;evtchn_from_port(d,p)和bucket_from_port(d,p)则是根据端口号获取该事件通道对应的结构体evtch n以及其所在的组别。例如,端口号为129的事件通道在结构体数组中所处的位置为evtchn[1][1]。这三个宏的定义在文件xen/common/event_channel.c中。
//xen/common/event_channel.c 34-40
//得到该端口事件通道在domain中的组别
#define bucket_from_port(d,p) \
((d)->evtchn[(p)/EVTCHNS_PER_BUCKET])
//判断端口号是否有效以及端口号对应的事件通道是否存在
#define port_is_valid(d,p) \
(((p) >= 0) && ((p) < MAX_EVTCHNS(d)) && \
(bucket_from_port(d,p) != NULL))
//根据端口号获取该事件通道对应的结构体evtch n
#define evtchn_from_port(d,p) \
//首先获得组号,再与低7位做与操作获得具体组内标识,从而得到具体的事件通道结构体位置
(&(bucket_from_port(d,p))[(p)&(EVTCHNS_PER_BUCKET-1)])
1.1 结构体evtchn
在事件通道对应的结构体evtchn中,主要定义了事件通道的各种状态以及不同状态所必须的基本参数。这些通过宏定义的不同状态既包含了事件通道的类型,又包含了事件通道在使用过程中可能的状态。
//xen/include/xen/sched.h 43-66
struct evtchn
{
#define ECS_FREE 0 /* 该通道可用 */
#define ECS_RESERVED 1 /* 该通道被保留 */
#define ECS_UNBOUND 2 /* 该通道可以跟远端的域绑定 */
#define ECS_INTERDOMAIN 3 /* 该通道已经绑定另一个域 */
#define ECS_PIRQ 4 /* 该通道绑定物理中断 */
#define ECS_VIRQ 5 /* 该通道绑定虚拟中断. */
#define ECS_IPI 6 /* 该通道绑定虚拟IPI */
u8 state; /* 该通道的状态 */
u8 consumer_is_xen; /* 是用Xen还是由客户使用 */
u16 notify_vcpu_id; /*用来传递通知的VCPU号 */
union {
struct {
domid_t remote_domid;
} unbound; /* state 值为 ECS_UNBOUND */
struct {
u16 remote_port;
struct domain *remote_dom;
} interdomain; /* state 值为 ECS_INTERDOMAIN */
u16 pirq; /* state 值为 ECS_PIRQ */
u16 virq; /* state 值为 ECS_VIRQ */
} u;
};
目前,事件通道有7种可能的状态(如表1-1所示):ECS_FREE表示该事件通道处于等待使用的状态,即事件通道已经完成初始化,等待分配;ECS_RESERVED表示该事件通道被系统保留,不参与分配; ECS_UNBOUND表示该事件通道处于未绑定状态,ECS_INTERDOMAIN表示该事件通道已经处于和其它Dom绑定的状态;ECS_PIRQ、ECS_VIR