• KVM虚拟机IO处理过程(二) ----QEMU/KVM I/O 处理过程


       接着KVM虚拟机IO处理过程中Guest Vm IO处理过程(http://blog.csdn.net/dashulu/article/details/16820281),本篇文章主要描述IO从guest vm跳转到kvm和qemu后的处理过程.

        首先回顾一下kvm的启动过程(http://blog.csdn.net/dashulu/article/details/17074675).qemu通过调用kvm提供的一系列接口来启动kvm. qemu的入口为vl.c中的main函数,main函数通过调用kvm_init 和 machine->init来初始化kvm. 其中, machine->init会创建vcpu, 用一个线程去模拟vcpu, 该线程执行的函数为qemu_kvm_cpu_thread_fn, 并且该线程最终kvm_cpu_exec,该函数调用kvm_vcpu_ioctl切换到kvm中,下次从kvm中返回时,会接着执行kvm_vcpu_ioctl之后的代码,判断exit_reason,然后进行相应处理.

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. int kvm_cpu_exec(CPUState *cpu)  
    2. {  
    3.     struct kvm_run *run = cpu->kvm_run;  
    4.     int ret, run_ret;  
    5.   
    6.     DPRINTF("kvm_cpu_exec() ");  
    7.   
    8.     if (kvm_arch_process_async_events(cpu)) {  
    9.         cpu->exit_request = 0;  
    10.         return EXCP_HLT;  
    11.     }  
    12.   
    13.     do {  
    14.         if (cpu->kvm_vcpu_dirty) {  
    15.             kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE);  
    16.             cpu->kvm_vcpu_dirty = false;  
    17.         }  
    18.   
    19.         kvm_arch_pre_run(cpu, run);  
    20.         if (cpu->exit_request) {  
    21.             DPRINTF("interrupt exit requested ");  
    22.             /* 
    23.              * KVM requires us to reenter the kernel after IO exits to complete 
    24.              * instruction emulation. This self-signal will ensure that we 
    25.              * leave ASAP again. 
    26.              */  
    27.             qemu_cpu_kick_self();  
    28.         }  
    29.         qemu_mutex_unlock_iothread();  
    30.   
    31.         run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0);  
    32.   
    33.         qemu_mutex_lock_iothread();  
    34.         kvm_arch_post_run(cpu, run);  
    35.   
    36.         if (run_ret < 0) {  
    37.             if (run_ret == -EINTR || run_ret == -EAGAIN) {  
    38.                 DPRINTF("io window exit ");  
    39.                 ret = EXCP_INTERRUPT;  
    40.                 break;  
    41.             }  
    42.             fprintf(stderr, "error: kvm run failed %s ",  
    43.                     strerror(-run_ret));  
    44.             abort();  
    45.         }  
    46.   
    47.         trace_kvm_run_exit(cpu->cpu_index, run->exit_reason);  
    48.         switch (run->exit_reason) {  
    49.         case KVM_EXIT_IO:  
    50.             DPRINTF("handle_io ");  
    51.             kvm_handle_io(run->io.port,  
    52.                           (uint8_t *)run + run->io.data_offset,  
    53.                           run->io.direction,  
    54.                           run->io.size,  
    55.                           run->io.count);  
    56.             ret = 0;  
    57.             break;  
    58.         case KVM_EXIT_MMIO:  
    59.             DPRINTF("handle_mmio ");  
    60.             cpu_physical_memory_rw(run->mmio.phys_addr,  
    61.                                    run->mmio.data,  
    62.                                    run->mmio.len,  
    63.                                    run->mmio.is_write);  
    64.             ret = 0;  
    65.             break;  
    66.         case KVM_EXIT_IRQ_WINDOW_OPEN:  
    67.             DPRINTF("irq_window_open ");  
    68.             ret = EXCP_INTERRUPT;  
    69.             break;  
    70.         case KVM_EXIT_SHUTDOWN:  
    71.             DPRINTF("shutdown ");  
    72.             qemu_system_reset_request();  
    73.             ret = EXCP_INTERRUPT;  
    74.             break;  
    75.         case KVM_EXIT_UNKNOWN:  
    76.             fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 " ",  
    77.                     (uint64_t)run->hw.hardware_exit_reason);  
    78.             ret = -1;  
    79.             break;  
    80.         case KVM_EXIT_INTERNAL_ERROR:  
    81.             ret = kvm_handle_internal_error(cpu, run);  
    82.             break;  
    83.         default:  
    84.             DPRINTF("kvm_arch_handle_exit ");  
    85.             ret = kvm_arch_handle_exit(cpu, run);  
    86.             break;  
    87.         }  
    88.     } while (ret == 0);  
    89.   
    90.     if (ret < 0) {  
    91.         cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_CODE);  
    92.         vm_stop(RUN_STATE_INTERNAL_ERROR);  
    93.     }  
    94.   
    95.     cpu->exit_request = 0;  
    96.     return ret;  
    97. }  

    kvm_vcpu_ioctl执行时,调用的kvm函数是virt/kvm/kvm-main.c中的kvm_vcpu_ioctl.c函数.当传入参数为KVM_RUN时,最终会调用到vcpu_enter_guest函数, vcpu_enter_guest函数中调用了kvm_x86_ops->run(vcpu),在intel处理器架构中该函数对应的实现为vmx_vcpu_run, vmx_vcpu_run设置好寄存器状态之后调用VM_LAUNCH或者VM_RESUME进入guest vm, 一旦发生vm exit则从此处继续执行下去.

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)  
    2. {  
    3.     struct vcpu_vmx *vmx = to_vmx(vcpu);  
    4.     unsigned long debugctlmsr;  
    5.   
    6.     /*...此处省略n行代码...*/  
    7.     vmx->__launched = vmx->loaded_vmcs->launched;  
    8.     asm(  
    9.         /* Store host registers */  
    10.         "push %%" _ASM_DX "; push %%" _ASM_BP ";"  
    11.         "push %%" _ASM_CX "  " /* placeholder for guest rcx */  
    12.         "push %%" _ASM_CX "  "  
    13.         "cmp %%" _ASM_SP ", %c[host_rsp](%0)  "  
    14.         "je 1f  "  
    15.         "mov %%" _ASM_SP ", %c[host_rsp](%0)  "  
    16.         __ex(ASM_VMX_VMWRITE_RSP_RDX) " "  
    17.         "1:  "  
    18.         /* Reload cr2 if changed */  
    19.         "mov %c[cr2](%0), %%" _ASM_AX "  "  
    20.         "mov %%cr2, %%" _ASM_DX "  "  
    21.         "cmp %%" _ASM_AX ", %%" _ASM_DX "  "  
    22.         "je 2f  "  
    23.         "mov %%" _ASM_AX", %%cr2  "  
    24.         "2:  "  
    25.         /* Check if vmlaunch of vmresume is needed */  
    26.         "cmpl $0, %c[launched](%0)  "  
    27.         /* Load guest registers.  Don't clobber flags. */  
    28.         "mov %c[rax](%0), %%" _ASM_AX "  "  
    29.         "mov %c[rbx](%0), %%" _ASM_BX "  "  
    30.         "mov %c[rdx](%0), %%" _ASM_DX "  "  
    31.         "mov %c[rsi](%0), %%" _ASM_SI "  "  
    32.         "mov %c[rdi](%0), %%" _ASM_DI "  "  
    33.         "mov %c[rbp](%0), %%" _ASM_BP "  "  
    34. #ifdef CONFIG_X86_64  
    35.         "mov %c[r8](%0),  %%r8   "  
    36.         "mov %c[r9](%0),  %%r9   "  
    37.         "mov %c[r10](%0), %%r10  "  
    38.         "mov %c[r11](%0), %%r11  "  
    39.         "mov %c[r12](%0), %%r12  "  
    40.         "mov %c[r13](%0), %%r13  "  
    41.         "mov %c[r14](%0), %%r14  "  
    42.         "mov %c[r15](%0), %%r15  "  
    43. #endif  
    44.         "mov %c[rcx](%0), %%" _ASM_CX "  " /* kills %0 (ecx) */  
    45.   
    46.         /* Enter guest mode */  
    47.         "jne 1f  "  
    48.         __ex(ASM_VMX_VMLAUNCH) " "  
    49.         "jmp 2f  "  
    50.         "1: " __ex(ASM_VMX_VMRESUME) " "  
    51.         "2: "  
    52.         /* Save guest registers, load host registers, keep flags */  
    53.         "mov %0, %c[wordsize](%%" _ASM_SP ")  "  
    54.         "pop %0  "  
    55.         "mov %%" _ASM_AX ", %c[rax](%0)  "  
    56.         "mov %%" _ASM_BX ", %c[rbx](%0)  "  
    57.         __ASM_SIZE(pop) " %c[rcx](%0)  "  
    58.         "mov %%" _ASM_DX ", %c[rdx](%0)  "  
    59.         "mov %%" _ASM_SI ", %c[rsi](%0)  "  
    60.         "mov %%" _ASM_DI ", %c[rdi](%0)  "  
    61.         "mov %%" _ASM_BP ", %c[rbp](%0)  "  
    62. #ifdef CONFIG_X86_64  
    63.         "mov %%r8,  %c[r8](%0)  "  
    64.         "mov %%r9,  %c[r9](%0)  "  
    65.         "mov %%r10, %c[r10](%0)  "  
    66.         "mov %%r11, %c[r11](%0)  "  
    67.         "mov %%r12, %c[r12](%0)  "  
    68.         "mov %%r13, %c[r13](%0)  "  
    69.         "mov %%r14, %c[r14](%0)  "  
    70.         "mov %%r15, %c[r15](%0)  "  
    71. #endif  
    72.         "mov %%cr2, %%" _ASM_AX "    "  
    73.         "mov %%" _ASM_AX ", %c[cr2](%0)  "  
    74.   
    75.         "pop  %%" _ASM_BP "; pop  %%" _ASM_DX "  "  
    76.         "setbe %c[fail](%0)  "  
    77.         ".pushsection .rodata  "  
    78.         ".global vmx_return  "  
    79.         "vmx_return: " _ASM_PTR " 2b  "  
    80.         ".popsection"  
    81.           : : "c"(vmx), "d"((unsigned long)HOST_RSP),  
    82.         [launched]"i"(offsetof(struct vcpu_vmx, __launched)),  
    83.         [fail]"i"(offsetof(struct vcpu_vmx, fail)),  
    84.         [host_rsp]"i"(offsetof(struct vcpu_vmx, host_rsp)),  
    85.         [rax]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RAX])),  
    86.         [rbx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBX])),  
    87.         [rcx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RCX])),  
    88.         [rdx]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDX])),  
    89.         [rsi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RSI])),  
    90.         [rdi]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RDI])),  
    91.         [rbp]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_RBP])),  
    92. #ifdef CONFIG_X86_64  
    93.         [r8]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R8])),  
    94.         [r9]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R9])),  
    95.         [r10]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R10])),  
    96.         [r11]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R11])),  
    97.         [r12]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R12])),  
    98.         [r13]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R13])),  
    99.         [r14]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R14])),  
    100.         [r15]"i"(offsetof(struct vcpu_vmx, vcpu.arch.regs[VCPU_REGS_R15])),  
    101. #endif  
    102.         [cr2]"i"(offsetof(struct vcpu_vmx, vcpu.arch.cr2)),  
    103.         [wordsize]"i"(sizeof(ulong))  
    104.           : "cc", "memory"  
    105. #ifdef CONFIG_X86_64  
    106.         , "rax", "rbx", "rdi", "rsi"  
    107.         , "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"  
    108. #else  
    109.         , "eax", "ebx", "edi", "esi"  
    110. #endif  
    111.           );  
    112.   
    113.     /* MSR_IA32_DEBUGCTLMSR is zeroed on vmexit. Restore it if needed */  
    114.     if (debugctlmsr)  
    115.         update_debugctlmsr(debugctlmsr);  
    116.   
    117. #ifndef CONFIG_X86_64  
    118.     /* 
    119.      * The sysexit path does not restore ds/es, so we must set them to 
    120.      * a reasonable value ourselves. 
    121.      * 
    122.      * We can't defer this to vmx_load_host_state() since that function 
    123.      * may be executed in interrupt context, which saves and restore segments 
    124.      * around it, nullifying its effect. 
    125.      */  
    126.     loadsegment(ds, __USER_DS);  
    127.     loadsegment(es, __USER_DS);  
    128. #endif  
    129.   
    130.     vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)  
    131.                   | (1 << VCPU_EXREG_RFLAGS)  
    132.                   | (1 << VCPU_EXREG_CPL)  
    133.                   | (1 << VCPU_EXREG_PDPTR)  
    134.                   | (1 << VCPU_EXREG_SEGMENTS)  
    135.                   | (1 << VCPU_EXREG_CR3));  
    136.     vcpu->arch.regs_dirty = 0;  
    137.   
    138.     vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);  
    139.   
    140.     if (is_guest_mode(vcpu)) {  
    141.         struct vmcs12 *vmcs12 = get_vmcs12(vcpu);  
    142.         vmcs12->idt_vectoring_info_field = vmx->idt_vectoring_info;  
    143.         if (vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK) {  
    144.             vmcs12->idt_vectoring_error_code =  
    145.                 vmcs_read32(IDT_VECTORING_ERROR_CODE);  
    146.             vmcs12->vm_exit_instruction_len =  
    147.                 vmcs_read32(VM_EXIT_INSTRUCTION_LEN);  
    148.         }  
    149.     }  
    150.   
    151.     vmx->loaded_vmcs->launched = 1;  
    152.   
    153.     vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);  
    154.     trace_kvm_exit(vmx->exit_reason, vcpu, KVM_ISA_VMX);  
    155.   
    156.     vmx_complete_atomic_exit(vmx);  
    157.     vmx_recover_nmi_blocking(vmx);  
    158.     vmx_complete_interrupts(vmx);  
    159. }  

        介绍完初始化的流程,可以介绍IO在kvm和qemu中的处理流程了. 当Guest Vm进行IO操作需要访问设备时,就会触发vm exit 返回到vmx_vcpu_run, vmx保存好vmcs并且记录下VM_ExIT_REASON后返回到调用该函数的vcpu_enter_guest, 在vcpu_enter_guest函数末尾调用了r = kvm_x86_ops->handle_exit(vcpu), 该函数对应于vmx_handle_exit函数(intel cpu架构对应关系可以查看vmx.c文件中static struct kvm_x86_ops vmx_x86_ops), vmx_handle_exit 调用kvm_vmx_exit_handlers[exit_reason](vcpu),该语句根据exit_reason调用不同的函数,该数据结构定义如下:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {  
    2.     [EXIT_REASON_EXCEPTION_NMI]           = handle_exception,  
    3.     [EXIT_REASON_EXTERNAL_INTERRUPT]      = handle_external_interrupt,  
    4.     [EXIT_REASON_TRIPLE_FAULT]            = handle_triple_fault,  
    5.     [EXIT_REASON_NMI_WINDOW]          = handle_nmi_window,  
    6.     [EXIT_REASON_IO_INSTRUCTION]          = handle_io,  
    7.     [EXIT_REASON_CR_ACCESS]               = handle_cr,  
    8.     [EXIT_REASON_DR_ACCESS]               = handle_dr,  
    9.     [EXIT_REASON_CPUID]                   = handle_cpuid,  
    10.     [EXIT_REASON_MSR_READ]                = handle_rdmsr,  
    11.     [EXIT_REASON_MSR_WRITE]               = handle_wrmsr,  
    12.     [EXIT_REASON_PENDING_INTERRUPT]       = handle_interrupt_window,  
    13.     [EXIT_REASON_HLT]                     = handle_halt,  
    14.     [EXIT_REASON_INVD]            = handle_invd,  
    15.     [EXIT_REASON_INVLPG]              = handle_invlpg,  
    16.     [EXIT_REASON_RDPMC]                   = handle_rdpmc,  
    17.     [EXIT_REASON_VMCALL]                  = handle_vmcall,  
    18.     [EXIT_REASON_VMCLEAR]                 = handle_vmclear,  
    19.     [EXIT_REASON_VMLAUNCH]                = handle_vmlaunch,  
    20.     [EXIT_REASON_VMPTRLD]                 = handle_vmptrld,  
    21.     [EXIT_REASON_VMPTRST]                 = handle_vmptrst,  
    22.     [EXIT_REASON_VMREAD]                  = handle_vmread,  
    23.     [EXIT_REASON_VMRESUME]                = handle_vmresume,  
    24.     [EXIT_REASON_VMWRITE]                 = handle_vmwrite,  
    25.     [EXIT_REASON_VMOFF]                   = handle_vmoff,  
    26.     [EXIT_REASON_VMON]                    = handle_vmon,  
    27.     [EXIT_REASON_TPR_BELOW_THRESHOLD]     = handle_tpr_below_threshold,  
    28.     [EXIT_REASON_APIC_ACCESS]             = handle_apic_access,  
    29.     [EXIT_REASON_WBINVD]                  = handle_wbinvd,  
    30.     [EXIT_REASON_XSETBV]                  = handle_xsetbv,  
    31.     [EXIT_REASON_TASK_SWITCH]             = handle_task_switch,  
    32.     [EXIT_REASON_MCE_DURING_VMENTRY]      = handle_machine_check,  
    33.     [EXIT_REASON_EPT_VIOLATION]       = handle_ept_violation,  
    34.     [EXIT_REASON_EPT_MISCONFIG]           = handle_ept_misconfig,  
    35.     [EXIT_REASON_PAUSE_INSTRUCTION]       = handle_pause,  
    36.     [EXIT_REASON_MWAIT_INSTRUCTION]       = handle_invalid_op,  
    37.     [EXIT_REASON_MONITOR_INSTRUCTION]     = handle_invalid_op,  
    38. };  

    如果是因为IO原因导致的vm exit,则调用的处理函数为handle_io,handle_io的处理可以查看(http://blog.csdn.net/fanwenyi/article/details/12748613), 该过程结束之后需要qemu去处理IO,这时候会返回到qemu, 在kvm_cpu_exec中继续执行下去,看上面kvm_cpu_exec的代码,如果是因为IO原因返回到qemu,会调用kvm_handle_io函数.

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. switch (run->exit_reason) {  
    2.         case KVM_EXIT_IO:  
    3.             DPRINTF("handle_io ");  
    4.             kvm_handle_io(run->io.port,  
    5.                           (uint8_t *)run + run->io.data_offset,  
    6.                           run->io.direction,  
    7.                           run->io.size,  
    8.                           run->io.count);  
    9.             ret = 0;  
    10.             break;  

    kvm_handle_io调用cpu_outb, cpu_outw等指令处理IO操作.

        假设虚拟机是用raw格式的磁盘,则IO在qemu中处理时经过的函数栈如下所示:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #0 bdrv_aio_writev (bs=0x55555629e9b0, sector_num=870456,   
    2. qiov=0x555556715ab0, nb_sectors=1,   
    3. cb=0x55555570161b <ide_sector_write_cb>, opaque=0x5555567157b8)  
    4. at block.c:3408  
    5. #1 0x0000555555701960 in ide_sector_write (s=0x5555567157b8)  
    6. at hw/ide/core.c:798  
    7. #2 0x00005555557047ae in ide_data_writew (opaque=0x555556715740, addr=496,   
    8. val=8995) at hw/ide/core.c:1907  
    9. #3 0x00005555558d9e4c in portio_write (opaque=0x5555565c0670, addr=0,   
    10. data=8995, size=2) at /home/dashu/kvm/qemu/qemu-dev-zwu/ioport.c:174  
    11. #4 0x00005555558e13d5 in memory_region_write_accessor (mr=0x5555565c0670,   
    12. addr=0, value=0x7fffb4dbd528, size=2, shift=0, mask=65535)  
    13. at /home/dashu/kvm/qemu/qemu-dev-zwu/memory.c:440  
    14. #5 0x00005555558e151d in access_with_adjusted_size (addr=0,   
    15. value=0x7fffb4dbd528, size=2, access_size_min=1, access_size_max=4,   
    16. access=0x5555558e1341 <memory_region_write_accessor>, mr=0x5555565c0670)  
    17. at /home/dashu/kvm/qemu/qemu-dev-zwu/memory.c:477  
    18. #6 0x00005555558e3dfb in memory_region_dispatch_write (mr=0x5555565c0670,   
    19. addr=0, data=8995, size=2)  
    20. at /home/dashu/kvm/qemu/qemu-dev-zwu/memory.c:984  
    21. #7 0x00005555558e7384 in io_mem_write (mr=0x5555565c0670, addr=0, val=8995,   
    22. size=2) at /home/dashu/kvm/qemu/qemu-dev-zwu/memory.c:1748  
    23. #8 0x000055555586a18e in address_space_rw (as=0x555556216d80, addr=496,   
    24. buf=0x7fffb4dbd670 "##", len=2, is_write=true)  
    25. at /home/dashu/kvm/qemu/qemu-dev-zwu/exec.c:1968  
    26. #9 0x000055555586a474 in address_space_write (as=0x555556216d80, addr=496,   
    27. buf=0x7fffb4dbd670 "##", len=2)  
    28. at /home/dashu/kvm/qemu/qemu-dev-zwu/exec.c:2030  
    29. #10 0x00005555558d98c9 in cpu_outw (addr=496, val=8995)  
    30. at /home/dashu/kvm/qemu/qemu-dev-zwu/ioport.c:61  

    bdrv_aio_writev最终调用bdrv_co_aio_rw_vector函数, 该函数调用co = qemu_coroutine_create(bdrv_co_do_rw) 创建一个协程去执行bdrv_co_do_rw函数,bdrv_co_wo_rw函数的函数栈如下:

    [cpp] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. #1 0x000055555563653c in paio_submit (bs=0x5555562a13d0, fd=10, sector_num=2,   
    2. qiov=0x555556715ab0, nb_sectors=1,   
    3. cb=0x5555556028b1 <bdrv_co_io_em_complete>, opaque=0x555556964e30, type=1)  
    4. at block/raw-posix.c:825  
    5. #2 0x0000555555636659 in raw_aio_submit (bs=0x5555562a13d0, sector_num=2,   
    6. qiov=0x555556715ab0, nb_sectors=1,   
    7. cb=0x5555556028b1 <bdrv_co_io_em_complete>, opaque=0x555556964e30, type=1)  
    8. at block/raw-posix.c:853  
    9. #3 0x00005555556366c9 in raw_aio_readv (bs=0x5555562a13d0, sector_num=2,   
    10. qiov=0x555556715ab0, nb_sectors=1,   
    11. cb=0x5555556028b1 <bdrv_co_io_em_complete>, opaque=0x555556964e30)  
    12. at block/raw-posix.c:861  
    13. #4 0x00005555556029b8 in bdrv_co_io_em (bs=0x5555562a13d0, sector_num=2,   
    14. nb_sectors=1, iov=0x555556715ab0, is_write=false) at block.c:4038  
    15. #5 0x0000555555602a49 in bdrv_co_readv_em (bs=0x5555562a13d0, sector_num=2,   
    16. nb_sectors=1, iov=0x555556715ab0) at block.c:4055  
    17. #6 0x00005555555fed61 in bdrv_co_do_readv (bs=0x5555562a13d0, sector_num=2,   
    18. nb_sectors=1, qiov=0x555556715ab0, flags=0) at block.c:2547  
    19. #7 0x00005555555fee03 in bdrv_co_readv (bs=0x5555562a13d0, sector_num=2,   
    20. nb_sectors=1, qiov=0x555556715ab0) at block.c:2573  
    21. #8 0x0000555555637d8c in raw_co_readv (bs=0x55555629e9b0, sector_num=2,   
    22. nb_sectors=1, qiov=0x555556715ab0) at block/raw.c:47  
    23. #9 0x00005555555fed61 in bdrv_co_do_readv (bs=0x55555629e9b0, sector_num=2,   
    24. nb_sectors=1, qiov=0x555556715ab0, flags=0) at block.c:2547  
    25. #10 0x00005555556023af in bdrv_co_do_rw   

    最终在paio_summit中会往线程池中提交一个请求thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque), 由调度器去执行aio_worker函数,aio_worker是真正做IO操作的函数,它通过pwrite和pread去读取磁盘.

        当qemu完成IO操作后,会在kvm_cpu_exec函数的循环中,调用kvm_vcpu_ioctl重新进入kvm.

        以上阐述了IO操作在kvm和qemu中处理的整个过程.

    参考资料:

    1. kvm代码解析连载(二):io的虚拟化:http://blog.csdn.net/fanwenyi/article/details/12748613

  • 相关阅读:
    软件测试 测试路径覆盖
    软件测试Lab Junit&Eclemma
    软件项目管理 名词解释
    使用 async/ await 进行 异步 编程
    基于任务的编程模型TAP
    异步编程(二)基于事件的异步编程模式 (EAP)
    C# 异步编程学习(一)
    C# 设计模式
    C# 读取硬盘信息 ManagementClass类
    C# show和showdialog区别
  • 原文地址:https://www.cnblogs.com/wuchanming/p/4690421.html
Copyright © 2020-2023  润新知