Xen事件通道详细介绍(四)

来源:互联网 发布:mac更改文件夹图标 编辑:程序博客网 时间:2024/05/08 14:20

4、事件通道的使用

GOS内部,除了位于特权级1GOS内核需要使用事件通道外,位于特权级3的应用程序也可能需要使用事件通道。为此,Xen采用了类似于特权级3使用超级调用的方式(privcmd内核驱动)来完成在用户空间的应用程序对事件通道的使用,即evtchn驱动。不同的是,privcmd内核驱动是proc文件系统下的驱动程序,而evtchn驱动则是dev文件系统下的驱动程序。

//linux-2.6-xen-sparse/drivers/xen/evtchn/evtchn.c 433-437

static struct miscdevice evtchn_miscdev = {

.minor        = MISC_DYNAMIC_MINOR,

.name         = "evtchn",

.fops         = &evtchn_fops,

};

4.1 设备驱动evtchn

应用程序需要通过预先定义的一系列文件操作来访问设备文件evtchn。这些文件操作的入口点由file_operation数据结构中的函数指针提供,其中包括设备的打开、读写、关闭、选择和命令控制等。

//linux-2.6-xen-sparse/drivers/xen/evtchn/evtchn.c 422-437

static const struct file_operations evtchn_fops = {

.owner   = THIS_MODULE,

.read    = evtchn_read,

.write   = evtchn_write,

.ioctl   = evtchn_ioctl,

.poll    = evtchn_poll,

.fasync  = evtchn_fasync,

.open    = evtchn_open,

.release = evtchn_release,

};

 

设备evtchn的控制和管理由底层驱动提供的ioctl函数实现,用来改变evtchn设备的运行状态和运行参数。在ioctl函数中按照ioctl系统调用命令码的格式定义了应用程序可能会用到的6种操作,包括虚拟IRQ绑定、域间通道绑定、未绑定事件通道分配、关闭事件通道、发送事件通知和重置evtchn设备缓冲区。在这些操作中,与事件通道相关的操作通过超级调用的方式实现(4-1)。例如,虚拟IRQ绑定操作通过调用超级调用的虚拟中断绑定操作(EVTCHNOP_bind_virq)实现。

表 41 ioctl函数命令码

命令码(cmd

超级调用操作

说明

IOCTL_EVTCHN_BIND_VIRQ

EVTCHNOP_bind_virq

虚拟中断(vIRQ)绑定

IOCTL_EVTCHN_BIND_INTERDOMAIN

EVTCHNOP_bind_interdomain

域间通道绑定

IOCTL_EVTCHN_BIND_UNBOUND_PORT

EVTCHNOP_alloc_unbound

未绑定事件通道绑定

IOCTL_EVTCHN_UNBIND

EVTCHNOP_close

关闭事件通道

IOCTL_EVTCHN_NOTIFY

EVTCHNOP_send

发送事件通知

IOCTL_EVTCHN_RESET

N/A

 

4.2 事件处理程序

事件通道产生事件通知,并交由指定的VCPU进行处理。VCPU根据不同的事件通知选择相应的事件处理程序(Events Handlier)处理事件通知。由于事件通道机制的异步性,使得VCPU在任何时刻都有可能接收到未处理的事件通知,其中包括在VCPU执行用户空间程序期间。因次,在VCPU处理事件通知之前,需要能够保存当前的状态信息,在VCPU执行完成后还原这些状态信息。在VCPU调用处理程序处理事件通知时,Xen将屏蔽到该VCPU的所有事件通知;打开(取消屏蔽)操作则由处理程序来完成。事件通道的处理过程与Linux系统中的中断处理过程类似。

X86平台上,从中断处理程序(Interrupt Handler)返回都是通过调用指令IRET完成IRET指令能够自动恢复中断前状态,继续执行原先的代码并重新打开中断。但是,直接通过IRET指令返回并不能适用于事件处理程序。与中断相比,Xen系统中的事件仅仅是一个定义的软件结构,IRET指令无法知道如何去重新打开这些事件。

通过IRET指令对应的超级调用HYPERVISOR_iret可以实现从事件处理程序返回。尽管超级调用IRET使用简便,但是由于超级调用的使用需要进行上下文切换(Context Switch),增大了系统的开销。因此, Xen系统中提供了另一个非自动的方案,即手动的保存和恢复相关的状态信息。

 

//linux-2.6-xen-sparse/arch/i386/kernel/entry-xen.S

scrit: /**** START OF CRITICAL REGION ****/

__TEST_PENDING

jnz  14f # 可能需要处理其它事件通知.

RESTORE_REGS

addl $4, %esp

CFI_ADJUST_CFA_OFFSET -4

1: iret

.section __ex_table,"a"

.align 4

.long 1b,iret_exc

.previous

14: __DISABLE_INTERRUPTS

TRACE_IRQS_OFF

jmp  11f

ecrit:  /**** END OF CRITICAL REGION ****/

 

ENTRY(hypervisor_callback)

RING0_INT_FRAME

pushl %eax

CFI_ADJUST_CFA_OFFSET 4

SAVE_ALL

movl EIP(%esp),%eax

cmpl $scrit,%eax

jb   11f

cmpl $ecrit,%eax

jb   critical_region_fixup

cmpl $sysexit_scrit,%eax

jb   11f

cmpl $sysexit_ecrit,%eax

ja   11f

addl $OLDESP,%esp

11: push %esp

CFI_ADJUST_CFA_OFFSET 4

call evtchn_do_upcall

add  $4,%esp

CFI_ADJUST_CFA_OFFSET -4

jmp  ret_from_intr

CFI_ENDPROC

 

事件通知在被送入各自的处理函数前,都要先交由一个简单的处理程序evtchn_do_upcall()进行处理,进而为不同的事件通知选择不同的处理路径。该函数将检查所有活动的事件通道(Active Event Channels)中是否存在未处理的事件通知,并将不同的事件通知送入不同的处理程序:与物理中断绑定的事件通知交由do_IRQ()处理,其它的则通过函数evtchn_device_upcall()交由设备evtchn进行处理。

 

//linux-2.6-xen-sparse/drivers/xen/core/evtchn.c 219-262

asmlinkage void evtchn_do_upcall(struct pt_regs *regs)

{

unsigned long  l1, l2;

unsigned int   l1i, l2i, port, count;

int            irq, cpu = smp_processor_id();

shared_info_t *s = HYPERVISOR_shared_info;

vcpu_info_t   *vcpu_info = &s->vcpu_info[cpu];

 

do {

/* 重新开启事件传递时,要避免回调风暴(可能会有大量的回调函数)*/

vcpu_info->evtchn_upcall_pending = 0;

 

if (unlikely(per_cpu(upcall_count, cpu)++))

return;

 

#ifndef CONFIG_X86

rmb();    /* 读内存屏障*/

#endif

/*清除evtchn_pending_sel 的值并返回原值*/

l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);

while (l1 != 0) {

/*evtchn_pending_sel 值的第一位,并将其清除*/

l1i = __ffs(l1);

l1 &= ~(1UL << l1i);

 

/*获取未屏蔽事件通知*/

while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {

l2i = __ffs(l2);

 

port = (l1i * BITS_PER_LONG) + l2i;

if ((irq = evtchn_to_irq[port]) != -1)

do_IRQ(irq, regs);

else {

exit_idle();

evtchn_device_upcall(port);

}

}

}

 

/*如果有内嵌的回调函数*/

count = per_cpu(upcall_count, cpu);

per_cpu(upcall_count, cpu) = 0;

} while (unlikely(count != 1));

}

通过函数evtchn_device_upcall()发送到设备evtchn的事件通知都将存入设备的环形缓冲区内等待处理。每个事件通知的处理函数都需要预先进行设置。不同的事件通道,其对应的设置函数亦不相同(表 42)。此外,系统还定义了设置函数bind_caller_port_to_irqhandler()用于Xen控制台(Xen Console)和Xenbus的事件通道处理函数的设定。

表 42 事件处理函数设置

事件通道类型(state

设置函数

说明

ECS_UNBOUND

bind_listening_port_to_irqhandler

未绑定(域间通道)

ECS_INTERDOMAIN

bind_interdomain_evtchn_to_irqhandler

域间通道

ECS_VIRQ

bind_virq_to_irqhandler

虚拟中断

ECS_IPI

bind_ipi_to_irqhandler

虚拟IPI

 

事件通道处理函数的使用借用了传统的中断处理机制,即将事件通道绑定到中断上,则相应的处理函数成为中断处理函数。利用中断处理函数的调用方法调用事件处理函数。因此,在设置函数中都需要调用中断处理注册函数request_irq()

0 0
原创粉丝点击