Currently, this is a DRAFT.
1. Structures
struct hv_device
在Hyper-V里,这是最基本的设备对象,其他的每个设备都有一个该结构。相当于基类。
struct net_device
这个是Linux 内核中对网络设备的描述。
struct netvsc_device
在Hyper-V中,该结构表示一个网络设备.
struct rndis_device
在HyperV中,网络相关的设备都是用RNDIS(https://en.wikipedia.org/wiki/RNDIS) 与Host交互,该结构既是与之相关的设置。
2. Relationship
struct netvsc_device {
...
struct netvsc_channel chann_table[] {
...
struct vmbus_channel *channel;
struct netvsc_device *net_device;
struct napi_struct napi;
...
}
...
struct rndis_device *extension; {
struct net_device *ndev;
...
}
}
hv_device -> device -> driver_data == net_device
struct net_device_context – private data for netvsc device, at the bottom of net_device.
net_device net_device_context
3. Hyper-V Initialization
start_kernel() setup_arch() __init init_hypervisor_platorm() (arch/x86/kernel/cpu/hypervisor.c) detect_hypervisor_vendor() foreach vendor in array hypervisors[], run its detect function init x86_init.hyper, x86_platform.hyper, x86_hyper_type run XXX.init_platorm() (ms_hyperv_init_platform()) init ms_hyperv, include features, misc_features, hints. extract host build information register_nmi_handler() Setup the hook to get control post apic initialization(hyperv_init()) hyperv_setup_mmu_ops() Setup the IDT for hypervisor callback(alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector))
late_time_init()(actually, x86_late_time_init())
x86_init.irqs.intr_mode_init()(apic_intr_mode_init())
default_setup_apic_routing()
x86_platform.apic_post_init() (For Hyper-V, it is hyperv_init())
Setup the hypercall page and enable hypercalls
hyper_alloc_mmu()
Register Hyperv-V specific clocksource.
x86_hyper_ms_hyperv = { .name = “Micorsoft Hyper-V” .detect = ms_hyperv_platform, .type = X86_HYPER_MS_HYPERV, .init.init_platform = ms_hyperv_init_platrom };
So when there is a interrupt, hyperv_callback_vector() will be called. HYPERVSIOR_CALLBACK_VECTOR is 0xf3, the IRQ vector. If Hyper-V is enabled, it will be defined in arch/x86/entry/entry_64.S by apicinterrupt3(macro). Its actual handler is hyperv_vector_handler()(arch/x86/kernel/cpu/mshyperv.c).
hyperv_vector_handler() vmbus_handler()(will be set to ‘vmbus_isr’ later in register vmbus)
4. How Hyper-V driver load
VMBUS is registered as a acpi bus:
static const struct acpi_device_id vmbus_acpi_device_ids[] = {
{"VMBUS", 0},
{"VMBus", 0},
{"", 0},
}
MODULE_DEVICE_TABLE(acpi, vmbus_acpi_driver_ids);
static struct acpi_driver vmbus_acpi_driver = {
.name = "vmbus",
.ids = vmbus_acpi_device_ids,
.ops = {
.add = vmbus_acpi_add,
.remove = vmbus_acpi_remove,
},
};
So when kernel boots, acpi will enumerate devices/buses. Then hv_acpi_init() will run.
hv_acpi_init() acpi_bus_register_driver(&vmbus_acpi_driver); vmbus_bus_init() hv_init()(allocate hv_context.cpu_context) bus_register(&hv_bus) hv_setup_vmbus_irq(vmbus_isr)(setup ‘vmbus_handler’ to ‘vmbus_isr’) hv_synic_alloc() allocate ‘hv_context.hv_numa_map’ foreach present cpu init msg dispacth tasklet (handler is vmbus_on_msg_dpc) init clockent device allocate synic message page allocate synic event page allocate post msg page init channel list vmbus_connect() vmbus_request_offers() hv_setup_kexec_handler() hv_setup_crash_handler()
vmbus_request_offers() will send a request to get all our pending offers. This usaully happends when bootup, after Hyper-V setup running environment. so we send a request to get all pending offers.
5. How interrupt is delivered and processed
INT—> IDT table, find handler entry –> pre proess –> hyperv_vector_handler()
hyperv_vector_handler() vmbus_handler() vmbus_isr()
vmbus_isr() get synic event flags -> hv_cpu->synic_event_page + VMBUS_MESSAGE_SINT if already handled -> vmbus_chan_sched(hv_cpu) get synic message page -> hv_cpu->synic_message_page + VMBUS_MESSAGE_SINT tasklet_schedule(&hv_cpu->msg_dpc); add_interrupt_randomness()