xen的事件通道

在我的理解,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位对应的事件来选择不同的处理方式.

现在为止我们通过超级调用建立起了事件的通知机制

事件通道

---------------------------

超级调用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值