|
在我的理解,xen的事件通道是每个guest都会拥有的一个事件的数组.
evtchn的定义是
struct evtchn
{
#define ECS_FREE 0 /* Channel is available for use. */
#define ECS_RESERVED 1 /* Channel is reserved. */
#define ECS_UNBOUND 2 /* Channel is waiting to bind to a remote domain. */
#define ECS_INTERDOMAIN 3 /* Channel is bound to another domain. */
#define ECS_PIRQ 4 /* Channel is bound to a physical IRQ line. */
#define ECS_VIRQ 5 /* Channel is bound to a virtual IRQ line. */
#define ECS_IPI 6 /* Channel is bound to a virtual IPI line. */
u8 state; /* ECS_* */
u8 xen_consumer; /* Consumer in Xen, if any? (0 = send to guest) */
u16 notify_vcpu_id; /* VCPU for local delivery notification */
u32 port;
union {
struct {
domid_t remote_domid;
} unbound; /* state == ECS_UNBOUND */
struct {
u16 remote_port;
struct domain *remote_dom;
} interdomain; /* state == ECS_INTERDOMAIN */
struct {
u16 irq;
u16 next_port;
u16 prev_port;
} pirq; /* state == ECS_PIRQ */
u16 virq; /* state == ECS_VIRQ */
} u;
u8 priority;
u8 pending:1;
u16 last_vcpu_id;
u8 last_priority;
#ifdef FLASK_ENABLE
void *ssid;
#endif
事件通道的这个结构中,主要的参数是state(0-6),然后是与state对应的u.事件通道的作用就像它名字一样,用于通知guest有一个事件到达,u就是事件到达的一些参数(比如谁的哪个端口引起了这个事件).事件通道仅仅是达到了一种异步通知,域间通知的效果.其他具体的事情还需要其他机制来完成.
每个domain都会有一个事件通道数组结构
/* Event channel information. */
struct evtchn *evtchn; /* first bucket only */
struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */
unsigned int max_evtchns;
unsigned int max_evtchn_port;
spinlock_t event_lock;
const struct evtchn_port_ops *evtchn_port_ops;
struct evtchn_fifo_domain *evtchn_fifo;
#define BUCKETS_PER_GROUP (PAGE_SIZE/sizeof(struct evtchn *))
/* Round size of struct evtchn up to power of 2 size */
#define __RDU2(x) ( (x) | ( (x) >> 1))
#define __RDU4(x) ( __RDU2(x) | ( __RDU2(x) >> 2))
#define __RDU8(x) ( __RDU4(x) | ( __RDU4(x) >> 4))
#define __RDU16(x) ( __RDU8(x) | ( __RDU8(x) >> 8))
#define __RDU32(x) (__RDU16(x) | (__RDU16(x) >>16))
#define next_power_of_2(x) (__RDU32((x)-1) + 1)
/* Maximum number of event channels for any ABI. */
#define MAX_NR_EVTCHNS MAX(EVTCHN_2L_NR_CHANNELS, EVTCHN_FIFO_NR_CHANNELS)
#define EVTCHNS_PER_BUCKET (PAGE_SIZE / next_power_of_2(sizeof(struct evtchn)))
#define EVTCHNS_PER_GROUP (BUCKETS_PER_GROUP * EVTCHNS_PER_BUCKET)
#define NR_EVTCHN_GROUPS DIV_ROUND_UP(MAX_NR_EVTCHNS, EVTCHNS_PER_GROUP)
MAX_NR_EVTCHNS=sizeof(ulong)^2*64=2^10
next_power_of_2(24)=32
所以EVTCHNS_PER_BUCKET=4096/32=2^7
BUCKETS_PER_GROUP=2^12/2^2=2^10
所以现在的情况应该是总共只有一个group的事件通道.上面这些计算都是我自己弄的好玩,不一定对,可以自己动手算一算.
只有这个结构还打不到通知的效果,还需要有一个标志位,这个标志位就在shared_info中,前面介绍共享信息页的时候介绍过了
evtchn_pending 和evtchn_mask 表示有事件到来和这个dom屏蔽了什么时间
一个事件通道的大小sizeof(xen_ulong_t)*8个xen_ulong_t 刚好是2^10个bit,每个bit对应于一个事件通道.这就很合情合理了,表示其中一个事件到来这个标志位就设置为1.mask同样,如果要屏蔽相应的事件通道,设置为1.mask的设置只是影响是否通知domain,不影响evtchn_pending的设置.
每个vcpu也有事件通道相关的结构
evtchn_upcall_pending 被设置表示有事件通道需要处理 u8
evtchn_upcall_mask 表示屏蔽所有时间通 u8
evtchn_pending_sel 选择一组事件通道进行处理,这个值作为evtchn_pending的下标 来指示cpu去处理那一组事件 ulong
事件通道整个结构的初始化过程在domain_create的时候进行
int evtchn_init(struct domain *d)
{
evtchn_2l_init(d);<span style="white-space:pre"></span>//设置了evtchn的端口的操作函数集合,和最多的evtchn的数量
d->max_evtchn_port = INT_MAX;
d->evtchn = alloc_evtchn_bucket(d, 0);<span style="white-space:pre"></span>//分配一个组的evtchn
if ( !d->evtchn )
return -ENOMEM;
spin_lock_init(&d->event_lock);<span style="white-space:pre"></span>//把第一个事件通道保留,并且设置poll_mask的cpu为所有vcpu
if ( get_free_port(d) != 0 )
{
free_evtchn_bucket(d, d->evtchn);
return -EINVAL;
}
evtchn_from_port(d, 0)->state = ECS_RESERVED;
#if MAX_VIRT_CPUS > BITS_PER_LONG
d->poll_mask = xmalloc_array(unsigned long, BITS_TO_LONGS(MAX_VIRT_CPUS));
if ( !d->poll_mask )
{
free_evtchn_bucket(d, d->evtchn);
return -ENOMEM;
}
bitmap_zero(d->poll_mask, MAX_VIRT_CPUS);
#endif
return 0;
}
这个结构就建立 起来了,然后怎么利用事件通道来通知.
首先我们必须按事件通道的用途来给事件通道的结构填充内容(初始化只是分配了内存,这个结构还是空的)
填充这个结构是采用超级调用HYPERVISOR_event_channel_op的方式,这是为什么说超级调用是xen的基础,不详细介绍这个超级调用,它的作用就是填写evtchn结构,即将evtchn的结构设置为0-6中的一种,并设置对应的联合体u中的值,u的值一般都是用来指示事件通道的绑定的dom的id和port,或者虚拟或物理中断号等.
这个结构填写完了,我们就可以说说怎么来实现异步通知的了.
任何一个dom想知道的事情,它都可以来分配一个事件通道,并且绑定这个事情.如果这个事情发生了,比如中断,会把shared_info中到达为设置为1,如果没有被屏蔽evtchn_mask没有设置为1,xen会调用一个upcall函数来设置,vcpu_info的upcall,sel位置,来指示vcpu去处理这个事情,vcpu只知道evtchn_pending的某一位需要处理,它就需要查询这个位对应的evtchn结构,它发现是某个中断发生了,中断号记录在里面,然后它就可以调用中断处理程序来处理这个中断了.xen中所有的事情(粒度至少大于cpu)的异步通知都是通过事件通道来完成的.
这个机制并不复杂,我们只需要分配一个事件通道(需要将事件的具体描述填入这个evtchn中)
然后事件发生就会设置事件通道的pending位表示事件发生了,dom读取pending位对应的事件来选择不同的处理方式.
现在为止我们通过超级调用建立起了事件的通知机制
事件通道
---------------------------
超级调用
版权声明:本文为博主原创文章,未经博主允许不得转载。 |
|