QEMUコードリーディングメモ
主にイベントループとKVM周り.
QEMU Main Loop
- QEMUの主処理はGLibを使ったイベントループ.
- ゲストのvCPUは別のスレッドで実行(後述)
vl.c:main() => vl.c:main_loop() => util/main-loop.c:main_loop_wait() => loop { slirp_pollfds_fill(); os_host_main_loop_wait() => g_main_context_acquire(context) glib_pollfds_fill() => g_main_context_prepare() g_main_context_query() qemu_mutex_unlock_iothread() qemu_poll_ns() => g_poll() qemu_mutex_lock_iothread() glib_pollfds_poll() => g_main_cotext_disapatch() g_main_context_release(context) slirp_pollfds_poll() }
- QEMUはiothreadに関してlockを持っている
- イベントのpoll中はこのiothreadのlockを外す
- イベントループ処理の流れを理解するにはGLibの動作を知る必要がある.
GLibメモ
GSource
- http://maemo.org/api_refs/4.0/glib/glib-The-Main-Event-Loop.html#GSource
- GLibにおけるイベントを表現
- file descriptors
- file
- pipe
- sockets
- ...
- timeout event
- file descriptors
- sourceの主要な関数
prepare()
poll()
の前に呼ばれる- sourceが(pollすることなく)準備完了であればtrueを返す
- 例
- timeout eventでtimeoutしてたらtrue
- file descriptorの場合は,通常はpollするのでfalse
- 例
check()
- poll()後に呼ばれる
- dispatchすることができるならtrueを返す
dispatch()
- イベントに対応付けられたcallback関数を実行
- 各sourceにはpriorityが存在
基本的なイベント処理の流れ
g_source_attach()
でcontextに対してsourceを追加- メインループ
g_main_context_prepare(context, &max_priority)
- コンテキストが持つ各GSourceのprepare()を呼ぶ
- ここで,max_priorityにprepare()がtrueの中から最大のpriorityが設定される.
g_main_context_query(context, max_priority, ..., fds)
- max_priorityを持つsourceのfile descriptorを取得
- この関数によって実際にpoll()するfdを選択する
g_poll()
- poll
g_main_context_check()
- dispatch可能なfdをチェック
g_main_cotext_disapatch()
- コールバック関数を呼ぶ
I/O handler
- 仮想デバイスを処理するためのハンドラ (GSource)
- 初期化
util/main-loop.c:qemu_init_main_loop()
iohandler_init() => util/async.c:aio_context_new()
g_source_attach()
でiohandlerをdefault contextのsourceに追加
- コールバック関数の登録
util/iohandler.c:qemu_set_fd_handler()
KVM 初期化
- 全体の初期化
accel/kvm/kvm_all.c:kvm_init()
vl.c:main() => accel/accel.c:configure_accelerator() => accel_init_machine() => acc->init_machine() = accel/kvm/kvm_all.c:kvm_init()
- マシン初期化時に,vCPUごとにスレッドが作成される.
target/i386/cpu.c:x86_cpu_realizefn() => cpus.c:qemu_init_vcpu() => qemu_kvm_start_vcpu() qemu_kvm_start_vcpu() => qemu_thread_create(..., qemu_kvm_cpu_thread_fn, ...)
KVM vCPU event loop
cpus.c:qemu_kvm_cpu_thread_fn => qemu_mutex_lock_iothread() loop { accel/kvm/kvm-all.c:kvm_cpu_exec() => qemu_mutex_unlock_iothread() do { kvm_vmrun_ioctl(cpu, KVM_RUN, 0) ret = handle_vmexit() }while(ret == 0); qemu_mutex_lock_iothread() qemu_wait_io_event() } qemu_mutex_unlock_iothread()
- 各vCPUは
iothread
のロックを持つ KVM_RUN
実行中はiothread
のロックを外す
KVMRUN
- ※ これはLinux側の処理
virt/kvm/kvm_main.c:kvm_vcpu_ioctl() => arch/x86/kvm/x86.c:kvm_arch_vcpu_ioctl_run() => vcpu_run() => loop { ret = { vcpu_enter_guest() => kvm_x86_ops->run(vcpu) = arch/x86/kvm/vmx.c:vmx_vcpu_run() => VMLAUNCH kvm_x86_ops->handle_exit(vcpu) = arch/x86/kvm/vmx.c:vmx_handle_exit() => kvm_vmx_exit_handlers[exit_reason](vcpu) } if (ret >= 0) break }
- VMCALLの処理
arch/x86/kvm/vmx.c:handle_vmcall() => arch/x86/kvm/x86.c:kvm_emulate_hypercall() => handle_vm_call or kvm_skip_emulated_instruction()
続く?