EmbeddedXEN VIRQ申请

为了说明VIRQ,拿时间中断的注册为例来说明Virq的申请过程,明白了VIRQ申请过程后,我们简单的看一下DOMU console的注册过程。

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);

好了到此为止。

评论

此博客中的热门博文

Linux/ARM Page Table Entry 属性设置分析

提交了30次才AC ---【附】POJ 2488解题报告

笔记