kvm 中断以及io虚拟化

开始中断:

  对于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[i], 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.csdn.net/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的问题

posted on 2013-05-27 15:15  Practicer..  阅读(2853)  评论(0编辑  收藏  举报

导航