shenhp 发表于 2016-1-9 14:36:03

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_DYNIRQS256
  #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 "jmpcommon_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;
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;
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;
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;
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, 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 zeroentrydo_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 callevtchn_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) != -1)
295do_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) != -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;
}
页: [1]
查看完整版本: xen中外部中断处理分析