xen中外部中断处理分析

http://hi.baidu.com/%B0%B5%D4%C2%C1%F7%B9%E2/blog/item/995e9c2bdfdf5696023bf60a.html

 

一个search代码的好命令:grep "common_interrupt" . -nR

说明:自己所用系统为xen3.4 x86-64

 

补充:对于Xen Guest,IRQ的数量有所增长:

#define NR_DYNIRQS  256

#define NR_IRQS   (NR_PIRQS + NR_DYNIRQS)

简略笔记:
XEN
1.xen中断处理的公共入口为common_interrupt
2.common_interrupt中对中断现场进行保存之后,调用do_IRQ函数具体对中断进行处理。do_IRQ函数返回后调用中断返回函数ret_from_intr。
3.xen仅仅需要处理2个物理中断,即串口中断(ns16550)和计时器中断。
4.对于Guest OS中注册了的中断,do_IRQ里面会调用__do_IRQ_guest函数进行处理。
5.__do_IRQ_guest最终会对每一个注册了该中断的Domain调用send_guest_pirq,在send_guest_pirq 中设置对应vcpu(irq都是绑定在一个vcpu上)event-channel的pending位。

Guest OS
1.系统启动时在cpu_initialize_context函数中设置event_callback_eip地址为hypervisor_callback
2.hypervisor_callback实际处理的是do_hypervisor_callback
3.do_hypervisor_callback中进行堆栈等工作处理之后,调用evtchn_do_upcall对事件进行处理(所有的irq最终都转化为了事件)
4.evtchn_do_upcall调用do_IRQ进行处理


代码跟踪分析:
XEN

中断处理函数入口
/xen/include/asm/x86_64/asm_defns.h
117 asmlinkage void IRQ_NAME(nr);                   /
118 __asm__(                                        /
119 "/n"__ALIGN_STR"/n"                             /
120 STR(IRQ) #nr "_interrupt:/n/t"                  /
121     "pushq $0/n/t"                              /
122     "movl $"#nr",4(%rsp)/n/t"                   /
123     "jmp common_interrupt");

中断处理公共代码
/xen/include/asm/x86_64/asm_defns.h
105 __asm__(                                        /
106     "/n" __ALIGN_STR"/n"                        /
107     "common_interrupt:/n/t"                     /
108     STR(SAVE_ALL)                               /
109     "movq %rsp,%rdi/n/t"                        /
110     "callq " STR(do_IRQ) "/n/t"                 /
111     "jmp ret_from_intr/n");

do_IRQ中断处理
/xen/arch/x86/irq.c
112 asmlinkage void do_IRQ(struct cpu_user_regs *regs)
113 {
114     unsigned int      vector = regs->entry_vector;
115     irq_desc_t       *desc = &irq_desc[vector];
116     struct irqaction *action;
.................................................
123     if ( likely(desc->status & IRQ_GUEST) )
124     {
125         irq_enter();
126         __do_IRQ_guest(vector);
127         irq_exit();
128         spin_unlock(&desc->lock);
129         return;
130     }
...........................
160 }


/xen/arch/x86/irq.c
302 static void __do_IRQ_guest(int vector)
303 {
304     irq_desc_t         *desc = &irq_desc[vector];
305     irq_guest_action_t *action = (irq_guest_action_t *)desc->action;
306     struct domain      *d;
............................
330     for ( i = 0; i < action->nr_guests; i++ )
331     {
332         unsigned int irq;
333         d = action->guest[i];
334         irq = domain_vector_to_irq(d, vector);
............................
338         if ( hvm_do_IRQ_dpci(d, irq) )
339         {
.......................
345         }
346         else if ( send_guest_pirq(d, irq) &&
347                   (action->ack_type == ACKTYPE_NONE) )
348         {
349             already_pending++;
350         }
........................
351     }
375 }

/xen/common/event_channel.c
656 int send_guest_pirq(struct domain *d, int pirq)
657 {
658     int port = d->pirq_to_evtchn[pirq];
659     struct evtchn *chn;
660
661     /*
662      * It should not be possible to race with __evtchn_close():
663      * The caller of this function must synchronise with pirq_guest_unbind(     ).
664      */
665     ASSERT(port != 0);
666
667     chn = evtchn_from_port(d, port);
668     return evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port);
669 }


Guest OS


这个自己还不能特别确定hypervisor_callback是否确实由此处来引发的,如果是,那么这个函数又是在什么时候被调用的? 这个疑问可以留着后续分析vcpu和domain机制来细究
/linux-2.6.18-xen.hg/drivers/xen/core/smpboot.c
179 static void __cpuinit cpu_initialize_context(unsigned int cpu)
180 {
.............................
238     ctxt.event_callback_eip    = (unsigned long)hypervisor_callback;
.............................
251 }


/linux-2.6.18-xen.hg/arch/x86_64/kernel/entry-xen.S
901 ENTRY(hypervisor_callback)
902     zeroentry do_hypervisor_callback
903 END(hypervisor_callback)


do_hypervisor_callback里面出现了有evtchn_do_upcall,这个函数就是Guest OS中用于处理事件的函数
/linux-2.6.18-xen.hg/arch/x86_64/kernel/entry-xen.S
919 ENTRY(do_hypervisor_callback)   # do_hypervisor_callback(struct *pt_regs)
920     CFI_STARTPROC
921 # Since we don't modify %rdi, evtchn_do_upall(struct *pt_regs) will
922 # see the correct pointer to the pt_regs
923     movq %rdi, %rsp            # we don't return, adjust the stack frame
924     CFI_ENDPROC
925     CFI_DEFAULT_STACK
926 11: incl %gs:pda_irqcount
927     movq %rsp,%rbp
928     CFI_DEF_CFA_REGISTER rbp
929     cmovzq %gs:pda_irqstackptr,%rsp
930     pushq %rbp          # backlink for old unwinder
931     call evtchn_do_upcall
932     popq %rsp
933     CFI_DEF_CFA_REGISTER rsp
934     decl %gs:pda_irqcount
935     jmp error_exit
936     CFI_ENDPROC
937 END(do_hypervisor_callback)


/linux-2.6.18-xen.hg/drivers/xen/core/evtchn.c
237 asmlinkage void evtchn_do_upcall(struct pt_regs *regs)
238 {
.....................
250     do {
.....................
267         while (l1 != 0) {
.....................
279             do {
.....................
294                 if ((irq = evtchn_to_irq[port]) != -1)
295                     do_IRQ(irq, regs);
296                 else
297                     evtchn_device_upcall(port);
......................
303             } while (l2i != BITS_PER_LONG - 1);
.....................
309         }
314     } while (unlikely(count != 1));
......................
317 }


do_IRQ在i386和x86_64目录下都有相应函数,根据当前系统是64位还是32位而定。不过这两个没有太大区别,最终都是调用__do_IRQ将中断交由上层去处理
/linux-2.6.18-xen.hg/arch/x86_64/kernel/irq-xen.c
116 asmlinkage unsigned int do_IRQ(struct pt_regs *regs)
117 {
.....................
132     __do_IRQ(irq, regs);
.....................
135     return 1;
136 }


/linux-2.6.18-xen.hg/kernel/irq/handle.c
157 /**
158 * __do_IRQ - original all in one highlevel IRQ handler
159 * @irq:    the interrupt number
160 * @regs:   pointer to a register structure
161 *
162 * __do_IRQ handles all normal device IRQ's (the special
163 * SMP cross-CPU interrupts have their own specific
164 * handlers).
165 *
166 * This is the original x86 implementation which is used for every
167 * interrupt type.
168 */
169 fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
170 {
..................
176     if (CHECK_IRQ_PER_CPU(desc->status)) {
177         irqreturn_t action_ret;
178
179         /*
180          * No locking required for CPU-local interrupts:
181          */
182         if (desc->chip->ack)
183             desc->chip->ack(irq);
184         action_ret = handle_IRQ_event(irq, regs, desc->action);
185         desc->chip->end(irq);
186         return 1;
187     }
...................
230     for (;;) {
231         irqreturn_t action_ret;
232
233         spin_unlock(&desc->lock);
234
235         action_ret = handle_IRQ_event(irq, regs, action);
236
237         spin_lock(&desc->lock);
238         if (!noirqdebug)
239             note_interrupt(irq, desc, action_ret, regs);
240         if (likely(!(desc->status & IRQ_PENDING)))
241             break;
242         desc->status &= ~IRQ_PENDING;
243     }
244     desc->status &= ~IRQ_INPROGRESS;
..................
254     return 1;
255 }


/linux-2.6.18-xen.hg/kernel/irq/handle.c
123 /**
124 * handle_IRQ_event - irq action chain handler
125 * @irq:    the interrupt number
126 * @regs:   pointer to a register structure
127 * @action: the interrupt action chain for this irq
128 *
129 * Handles the action chain of an irq event
130 */
131 irqreturn_t handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
132                  struct irqaction *action)
133 {
..................
142     do {
143         ret = action->handler(irq, action->dev_id, regs);
144         if (ret == IRQ_HANDLED)
145             status |= action->flags;
146         retval |= ret;
147         action = action->next;
148     } while (action);
...................
155 }

---------------------------------------xen---------------------------
IRQn_interrupt(v)
|
do_IRQ
|
IRQ_GUEST? —否—>中断服务程序
|

|
__do_IRQ_guest
|
send_guest_pirq
|
evtchn_set_pending
-|-------------------------------------Guest OS-------------------------------
hypervisor_callback
|
do_hypervisor_callback
|
evtchn_do_upcall
|
do_IRQ
|
__do_IRQ
|
handle_IRQ_event
|
注册的中断服务例程(handler)

 

 

evtchn_do_upcall ()

{

/* process port */
    port = (l1i * BITS_PER_LONG) + l2i;
    if ((irq = evtchn_to_irq[port]) != -1)
     do_IRQ(irq, regs);
    else
     evtchn_device_upcall(port);

}

 

irqreturn_t handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
        struct irqaction *action)
{
 irqreturn_t ret, retval = IRQ_NONE;
 unsigned int status = 0;

 handle_dynamic_tick(action);

 if (!(action->flags & IRQF_DISABLED))
  local_irq_enable_in_hardirq();

 do {
  ret = action->handler(irq, action->dev_id, regs);
  if (ret == IRQ_HANDLED)
   status |= action->flags;
  retval |= ret;
  action = action->next;
 } while (action);

 if (status & IRQF_SAMPLE_RANDOM)
  add_interrupt_randomness(irq);
 local_irq_disable();

 return retval;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值