设为首页 收藏本站
查看: 2391|回复: 0

[经验分享] kvm 中断以及io虚拟化

[复制链接]

尚未签到

发表于 2015-4-10 15:46:33 | 显示全部楼层 |阅读模式
  开始中断:
  对于apic来说:
  kvm_set_ioapic_irq->kvm_ioapic_set_irq->ioapic_service->ioapic_deliver->kvm_irq_delivery_to_apic->kvm_apic_set_irq(vcpu, irq)->__apic_accept_irq->apic_set_vector
  1.这里需要注意的问题是,在最后的函数设置了相应的vcpu中的Lapic后,是如何被实际的vcpu知道的
  2.kvm_set_ioapic_irq是被谁调用的,也就是实际中断传递的过程是什么。
  设置kvm_set_ioapic_irq 是在函数kvm_set_irq_routing->setup_routing_entry(new, &new->rt_entries, ue) 在这里设置的函数kvm_set_ioapic_irq,(这里还是有问题)
  在
  int kvm_setup_default_irq_routing(struct kvm *kvm)
{
return kvm_set_irq_routing(kvm, default_routing,
   ARRAY_SIZE(default_routing), 0);
}
  中设置了相应的default_routing
  
  vcpu_enter_guest->inject_pending_event->中检查是否有中断到来(其检测的为vcpu->arch.interrupt.pending)
  这里有个问题,设置相应的中断是在apic_set_vector(vector, apic->regs + APIC_TMR)设置,这里设置完了后在哪里被解析出来,这里和上面检测的不一样,这个函数代表的是写相应的寄存器
  kvm_vcpu_ioctl_x86_get_vcpu_events,kvm_vcpu_ioctl_x86_set_vcpu_events这两个函数可能使得vcpu->arch.interrupt.pending和event连接起来?可以注意一下
  qemu设备向vcpu注入中断的方式
  1. kvm_arch_vcpu_ioctl->kvm_vcpu_ioctl_x86_set_vcpu_events->kvm_make_request(设置相应的中断)这些函数的参数events是从用户空间传递过来的,
  2.(切换VCPU时)kvm_arch_vcpu_ioctl_run->__vcpu_run->vcpu_enter_guest(在要进入VCPU前)->inject_pending_event-> ->vmx_inject_irq检查是否有中断到来这个是对应qemu外设发出中断,将中断注入到客户机中,注入方式应该是直接写VMCS(vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr);)类似这个函数
  
  
  
  
  问题kvm_x86_ops->run(vcpu);//vmx_vcpu_run退出是否代表着相应的VCPU  exit了,然后在vmx_vcpu_run回处理相应的
  
  硬件处理的情况参考的
  http://zhangjun2915.blog.163.com/blog/static/3808623620105744035432/后面的评论很重要
  其中有句话是 因为硬件会将中断类型和那个中断号写入到VMCS中,这里为什么?
  外部中断来时是否会引起vm_exit?
  Documents/vitual/kvm/api.txt这个是很重要的文件
  
  
  I/O
  客户机退出后回调用vmx_handle_exit() 这里应该是处理的IO请求或者其他
  
  
  基本的函数调用过程:
  kvm_vcpu_ioctl->kvm_arch_vcpu_ioctl_run(vcpu, vcpu->runkvm_arch_vcpu_ioctl_run(vcpu, vcpu->run)->__vcpu_run(vcpu)->vcpu_enter_guest(vcpu)->
  ----------kvm_x86_ops->run(vcpu)(vmx_vcpu_run){这个函数完成的是真正的开启VCPU,该函数返回时代表的是VCPU EXIT ,实际在__vmx_complete_interrupts实际在__vmx_complete_interrupts调用前VCPU已经退出了}
  ----------kvm_x86_ops->handle_exit(vcpu)( 已IO指令出错为例子)-> vmx_handle_exit{kvm_vmx_exit_handlers[exit_reason](vcpu);}->handle_io->kvm_fast_pio_out->emulator_pio_in_out(这里实际完成的是填充相应的结构体字段)
  {



vcpu->run->exit_reason = KVM_EXIT_IO;
vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT;
vcpu->run->io.size = size;
vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE;
vcpu->run->io.count = count;
vcpu->run->io.port = port;
  }
  实际是直接返回到了qemu中文件kvm_all.c中

kvm_cpu_exec-〉kvm_handle_io
下一步需要搞明白的是, 返回了需要qemu模拟的指令,但是数据传输时怎样进行的,也就是搞明白了,kvm传出来的地址是什么地址是虚拟地址还是物理地址,而qemu是如何得到往这些地址里写入数据的?这个问题在下面的内容中有解释了
这里通过论文看到了一个IO共享页的概念


  " 使用QEMU模拟I/O的情况下,当客户机中的设备驱动程序(device driver)发起I/O操作请求之时,KVM模块中的I/O操作捕获代码会拦截这次I/O请求,然后经过处理后将本次I/O请求的信息存放到I/O共享页,并通知用户控件的QEMU程序。QEMU模拟程序获得I/O操作的具体信息之后,交由硬件模拟代码来模拟出本次的I/O操作,完成之后,将结果放回到I/O共享页,并通知KVM模块中的I/O操作捕获代码。最后,由KVM模块中的捕获代码读取I/O共享页中的操作结果,并把结果返回到客户机中。当然,这个操作过程中客户机作为一个QEMU进程在等待I/O时也可能被阻塞。另外,当客户机通过DMA(Direct Memory Access)访问大块I/O之时,QEMU模拟程序将不会把操作结果放到I/O共享页中,而是通过内存映射的方式将结果直接写到客户机的内存中去,然后通过KVM模块告诉客户机DMA操作已经完成。"上面是引用的http://ju.outofmemory.cn/entry/6402文章
  
  
  kvm 中的kvm_run 是如何与qemu中的kvm_run联系到一起的
  在qemu下面的函数中,完成了 env->kvm_run的映射.
  int kvm_init_vcpu(CPUArchState *env)

env->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
238                         env->kvm_fd, 0);
  下面是kvm 与qemu设备中交互使用的I/O共享页的分析
  kvm_run应该就是所谓的I/O共享页的概念
  应该查找kvm_cpu_exec在哪里被利用,应为由程序可以看书来的书kvm_run这个结构对应的数据是可以共享的,qemu在调用函数kvm_cpu_exec以前一定会和内核交互相应关于KVM_RUM的信息 ,在kvm_init_vcpu中有
  env->kvm_run=mmap(NULL,mmap_size,PROT_READ|....)这里应该是连接kvm和qemu的IO共享页
  这里的mmap应该是利用的是下面结构中的函数:



static struct file_operations kvm_vcpu_fops = {
.release        = kvm_vcpu_release,
.unlocked_ioctl = kvm_vcpu_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl   = kvm_vcpu_compat_ioctl,
#endif
.mmap           = kvm_vcpu_mmap,//
    .llseek        = noop_llseek,
};

static int kvm_vcpu_mmap(struct file *file, struct vm_area_struct *vma)
{
vma->vm_ops = &kvm_vcpu_vm_ops;
return 0;
}//
  最后应该是 kvm_vcpu_vm_ops中的函数 kvm_vcpu_fault被调用,这里关于页面出错处理的情况还是没有清晰,在kvm_vcpu_fault中确实有关于利用



if (vmf->pgoff == 0)
page = virt_to_page(vcpu->run);
#ifdef CONFIG_X86
else if (vmf->pgoff == KVM_PIO_PAGE_OFFSET)
page = virt_to_page(vcpu->arch.pio_data);
  上面的代码猜想是完成mmap时调用的。还需要仔细再跟踪下,下一步是选取一个设备进行跟踪
  在qemu 中的
  kvm_cpu_exec调用kvm_arch_pre_run完成中断等注入,在这个函数中有下面一段代码是关于中断窗口的,这里需要在处理下.
  



if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
1704             run->request_interrupt_window = 1;
1705         } else {
1706             run->request_interrupt_window = 0;
1707         }
  
  调用run_ret = kvm_vcpu_ioctl(env, KVM_RUN, 0);运行虚拟机
  
  举个例子处理io exit的时候.
  kvm_cpu_exec 调用函数kvm_handle_io -〉stb_p(ptr, cpu_inb(port));直接向内存共享页写数据。并且是直接写的地址

kvm_handle_io(run->io.port,
1635                          (uint8_t *)run + run->io.data_offset,
1636                          run->io.direction,
1637                          run->io.size,
1638                          run->io.count);
这里的run地址代表的是虚拟地址,这里写的数据实际是调用了stb_p以及其他函数,这个函数的功能是调用该Port对应的设备然后处理得到port的数据,写入到run对应的内存地址中,这里run对应的虚拟地址,映射的物理地址和vcpu中的kvm_run对应的物理地址是一样的.
  http://blog.iyunv.com/zhuriyuxiao/article/details/9233955
  连接中的一篇文章写的不错
  
   问题在于 kvm_vcpu_fault 这个函数怎么被调用?下面代码红色的地方,需要在看看mmap的实现,找出如何映射的,



static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct kvm_vcpu *vcpu = vma->vm_file->private_data;
struct page *page;
if (vmf->pgoff == 0)
page = virt_to_page(vcpu->run);////这里返回的是 run 对应的物理地址,也就是说 vcpu->run 对应的为内核虚拟地址(因为实际上cpu发出的地址都是虚拟地址)
#ifdef CONFIG_X86
else if (vmf->pgoff == KVM_PIO_PAGE_OFFSET)
page = virt_to_page(vcpu->arch.pio_data);
#endif
#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
else if (vmf->pgoff == KVM_COALESCED_MMIO_PAGE_OFFSET)
page = virt_to_page(vcpu->kvm->coalesced_mmio_ring);
#endif
else
return kvm_arch_vcpu_fault(vcpu, vmf);
get_page(page);
vmf->page = page;// 1这里赋值可以认为在某个地方一定存在另外的一个客户虚拟地址到这个内核实地址的映射
return 0;  /   //回答上面的问题应该就是在返回以后,也就是返回到qemu的虚拟地址对应着这个物理地址.
}
  
  mmio的形式如何处理
  
  
  

unsigned long __phys_addr(unsigned long x)
12 {
13         if (x >= __START_KERNEL_map) {
14                 x -= __START_KERNEL_map;
15                 VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE);
16                 x += phys_base;
17         } else {
18                 VIRTUAL_BUG_ON(x < PAGE_OFFSET);
19                 x -= PAGE_OFFSET;//PAGE_OFFSET=__PAGE_OFFSET这个#define __PAGE_OFFSET           _AC(CONFIG_PAGE_OFFSET, UL),而这里这里__PAGE_OFFSET代表的是3GB,0xc0000000,也就是所x-=PAGE_OFFSET实际代表的是内核虚拟地址对应的实际的物理地址,因为内核虚拟地址和实际的物理地址存在3G的固定偏差,所以x为实际的物理地址
20 VIRTUAL_BUG_ON(!phys_addr_valid(x)); 21 } 22 return x; 23 }
简单分析下这个函数得到虚拟地址对应的物理地址,上面的一种情况没有分析
  
  kvm_vm_ioctl()这里是针对的设备的问题
  r = kvm_vm_ioctl_assigned_device(kvm, ioctl, arg);
  这里上面的关于device assign的应该对应的是支持vt-D技术的虚拟机。
  有几篇文章可以看下,在download/kvm中。
  下一步需要作的
  1.看下这个
  2.mmio
  3.了解清除现在到底使用的是什么设备模型
  
  
  下面的问题是关于apic的问题

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-55746-1-1.html 上篇帖子: kvm 实际使用(转) 下篇帖子: Xen、Openvz、KVM有什么区别?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表