EmbeddedXEN VIRQ申请
为了说明VIRQ,拿时间中断的注册为例来说明Virq的申请过程,明白了VIRQ申请过程后,我们简单的看一下DOMU console的注册过程。
arch/arm/mach-pxa/time-xen.c
asmlinkage void __init start_kernel_lx0(void)
......
---------就这样pxa_timer_init登上舞台了---------
arch/arm/mach-pxa/time-xen.c
xen_guest/core/evtchn.c
--------看一下如何将virq绑定到irq上的@!-----------
----------
arch/arm/mach-pxa/time-xen.c
/*
* Kernel system timer support.
*/
void timer_tick(struct pt_regs *regs)
{
profile_tick(CPU_PROFILING, regs);
do_leds();
do_set_rtc();
do_timer(regs);
#ifndef CONFIG_SMP
update_process_times(user_mode(regs));
#endif
}
以后,当virq中断来了以后,就直接执行xen_virtual_timer_interrupt了。
在console模块注册的时候,申请了virq中断。我们可以在xen_guest/core/console.c里面找到
arch/arm/mach-pxa/time-xen.c
MACHINE_START(MAINSTONE, "Intel HCDDBBVA0 Development Platform (aka Mainstone) / EmbeddedXEN (xen-pxa270-qemu)")
/* Maintainer: MontaVista Software Inc. */
.phys_io = 0x40000000,
.boot_params = 0xa0000100, /* BLOB boot parameter setting */
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
.timer = &pxa_timer,
.init_machine = mainstone_init
MACHINE_END
struct sys_timer pxa_timer = {
.init = pxa_timer_init,
/*
.suspend = pxa_timer_suspend,
.resume = pxa_timer_resume,
*/
.offset = pxa_gettimeoffset,
};
-----------------好了,上面是数据结构,以及定义,现在看一下怎么用这些数据的-----
init/main-xen.c
asmlinkage void __init start_kernel_lx0(void)
{
......
time_init(); /* (DRE) xen guest */
......
}
arch/arm/kernel/time.c
void __init time_init(void)
{
if (system_timer->offset == NULL)
system_timer->offset = dummy_gettimeoffset;
system_timer->init();
#ifdef CONFIG_NO_IDLE_HZ
if (system_timer->dyn_tick)
system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
#endif
}
---------就这样pxa_timer_init登上舞台了---------
arch/arm/mach-pxa/time-xen.c
static void __init pxa_timer_init(void)
{
bind_virq_to_irqhandler(
VIRQ_TIMER, //VIRQ_TIMER:0
0,
xen_virtual_timer_interrupt,
SA_INTERRUPT,
"timer0",
/* "ost0",*/
NULL);
}
xen_guest/core/evtchn.c
int bind_virq_to_irqhandler(unsigned int virq,unsigned int cpu,irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id)
{
unsigned int irq;
int retval;
irq = bind_virq_to_irq(virq, cpu);
retval = request_irq(irq, handler, irqflags, devname, dev_id);
if (retval != 0) {
unbind_from_irq(irq);
return retval;
}
return irq;
}
--------看一下如何将virq绑定到irq上的@!-----------
文件路径同上
static int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
{
//evtchn_op_t op = { .cmd = EVTCHNOP_bind_virq };
evtchn_bind_virq_t op;
int evtchn, irq;
spin_lock(&irq_mapping_update_lock);
if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) {//说明,这个virq对应的irq还没有被分配,可以使用
op.virq = virq;
op.vcpu = cpu;
BUG_ON(HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op) != 0);//virq与evtchn相联系了。下面要让evtchn和irq相联系,这样,才能在DOMU这一层处理
evtchn = op.port;
irq = find_unbound_irq();
evtchn_to_irq[evtchn] = irq;
irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);
per_cpu(virq_to_irq, cpu)[virq] = irq;
bind_evtchn_to_cpu(evtchn, cpu);//在DOM中将其绑定的!@
}
irq_bindcount[irq]++;//找到一个被绑定的Irq以后,要将数组中的该元素加一的,这样以后再找Unbound_irq的时候,就不用找它了。
return irq;
}文件路径同上,在这里,也可以看出,DOMU里面同样也有Irq的概念。只不过描述符不同
//找到一个没有被绑定的的irq
static int find_unbound_irq(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
if (irq_bindcount[irq] == 0)//找到了
break;
if (irq == NR_IRQS)
panic("No available IRQ to bind to: increase NR_IRQS!\n");
return irq;
}HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &op)会根据CMD去调用evtchn_bind_virq
xen/common/event_channel.c
static long evtchn_bind_virq(evtchn_bind_virq_t *bind)
{
struct evtchn *chn;
struct vcpu *v;
struct domain *d = current->domain;
int port, virq = bind->virq, vcpu = bind->vcpu;
long rc = 0;
if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) )
return -EINVAL;
if ( virq_is_global(virq) && (vcpu != 0) )
return -EINVAL;
if ( (vcpu < 0) || (vcpu >= ARRAY_SIZE(d->vcpu)) ||
((v = d->vcpu[vcpu]) == NULL) )
return -ENOENT;
spin_lock(&d->evtchn_lock);
if ( v->virq_to_evtchn[virq] != 0 )
ERROR_EXIT(-EEXIST);
if ( (port = get_free_port(d)) < 0 )
ERROR_EXIT(port);
//分配evtchn号
chn = evtchn_from_port(d, port);
chn->state = ECS_VIRQ;
chn->notify_vcpu_id = vcpu;
chn->u.virq = virq;
v->virq_to_evtchn[virq] = bind->port = port;
out:
spin_unlock(&d->evtchn_lock);
return rc;
}
----------
将xen_virtual_timer_interrupt送到irq线上
arch/arm/mach-pxa/time-xen.c
/* (GCD) From Samsung. Added regs argument */
static irqreturn_t xen_virtual_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)//简单的就是更新墙上时间
{
write_seqlock(&xtime_lock);
//if (displayme)
// printk("### timer_tick\n");
timer_tick(regs);
write_sequnlock(&xtime_lock);
return IRQ_HANDLED;
}
对比一下,看看hypervisor层的时间中断处理函数:
static irqreturn_t pxa_ost0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
//struct clock_event_device *c = dev_id;
//static int cnt = 0;
/* Disarm the compare/match, signal the event. */
OIER &= ~OIER_E0;
OSSR = OSSR_M0;
timer_interrupt(irq, dev_id, regs);
return IRQ_HANDLED;
}
void timer_interrupt(int irq, void *dev_id, struct xen_cpu_user_regs *regs) {
ASSERT(local_irq_is_enabled());
/* Update jiffies counter. */
(*(volatile unsigned long *) &jiffies_64)++;
raise_softirq(TIMER_SOFTIRQ);
if (--plt_overflow_jiffies == 0)
plt_overflow();
}
arch/arm/kernel/time.c
/*
* Kernel system timer support.
*/
void timer_tick(struct pt_regs *regs)
{
profile_tick(CPU_PROFILING, regs);
do_leds();
do_set_rtc();
do_timer(regs);
#ifndef CONFIG_SMP
update_process_times(user_mode(regs));
#endif
}
以后,当virq中断来了以后,就直接执行xen_virtual_timer_interrupt了。
除了timer的virq申请之外,还有console的申请。
在console模块注册的时候,申请了virq中断。我们可以在xen_guest/core/console.c里面找到
static int __init xencons_init(void)
{
int rc;
if (xen_init() < 0)
return -ENODEV;
if (xc_mode == XC_OFF)
return 0;
if (!(xen_start_info->flags & SIF_INITDOMAIN)) {
rc=xencons_ring_init();
if(rc)
return rc;
}
xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ?
1 : MAX_NR_CONSOLES);
if (xencons_driver == NULL)
return -ENOMEM;
DRV(xencons_driver)->name = "xencons";
DRV(xencons_driver)->major = TTY_MAJOR;
DRV(xencons_driver)->type = TTY_DRIVER_TYPE_SERIAL;
DRV(xencons_driver)->subtype = SERIAL_TYPE_NORMAL;
DRV(xencons_driver)->init_termios = tty_std_termios;
DRV(xencons_driver)->flags =
TTY_DRIVER_REAL_RAW |
TTY_DRIVER_RESET_TERMIOS;
DRV(xencons_driver)->termios = xencons_termios;
DRV(xencons_driver)->termios_locked = xencons_termios_locked;
if (xc_mode == XC_SERIAL) {
DRV(xencons_driver)->name = "ttyS";
DRV(xencons_driver)->minor_start = 64 + xc_num;
DRV(xencons_driver)->name_base = 0 + xc_num;
} else {
DRV(xencons_driver)->name = "tty";
DRV(xencons_driver)->minor_start = xc_num;
DRV(xencons_driver)->name_base = xc_num;
}
tty_set_operations(xencons_driver, &xencons_ops);
if ((rc = tty_register_driver(DRV(xencons_driver))) != 0) {
printk("WARNING: Failed to register Xen virtual "
"console driver as '%s%d'\n",
DRV(xencons_driver)->name,
DRV(xencons_driver)->name_base);
put_tty_driver(xencons_driver);
xencons_driver = NULL;
return rc;
}
// tty_register_device(xencons_driver, 0, NULL);
if (xen_start_info->flags & SIF_INITDOMAIN) {
xencons_priv_irq = bind_virq_to_irqhandler(
VIRQ_CONSOLE,
0,
xencons_priv_interrupt,
0,
"console",
NULL);
BUG_ON(xencons_priv_irq < 0);
}
printk("Xen virtual console successfully installed as %s%d\n",
DRV(xencons_driver)->name,
DRV(xencons_driver)->name_base );
return 0;
}
module_init(xencons_init);
好了到此为止。
评论
发表评论