APIC Virtualization (APICv and AVIC)
The local Advanced Programmable Interrupt Controller (APIC) is the per-CPU register block a guest kernel pokes constantly — to set its task priority, to acknowledge an interrupt with an End-Of-Interrupt (EOI) write, to send an inter-processor interrupt (IPI), to read which interrupts are pending. When that APIC is emulated in software, every one of those accesses is a privileged event that traps out of the guest into the hypervisor: a VM exit. For interrupt-heavy workloads this is the single hottest exit source in the whole machine. APIC virtualization moves the interrupt controller into the silicon so the guest reads and writes its own APIC registers, evaluates interrupt priority, and even acknowledges interrupts, all with zero exits. Intel calls its implementation APICv (a bundle of VMX features); AMD calls its equivalent the AVIC — Advanced Virtual Interrupt Controller. Both turn a per-interrupt exit into a near-zero-exit path, and both are the foundation that Posted Interrupts builds on to deliver device and IPI interrupts into a running vCPU.
This note is the architecture-and-mechanism deep dive for register-side APIC virtualization — how a guest’s own LAPIC accesses are resolved in hardware. It is pinned to the Linux 6.12 LTS kernel (released 2024-11-17) and cross-checked against the 6.18 LTS era (2025-11-30). The delivery side — how an external interrupt arrives without an exit — is its sibling, Posted Interrupts; the KVM-internal software model of the virtual APIC and how KVM injects when hardware is off lives in Interrupt Injection and the Virtual APIC.
Uncertain
Several precise hardware-semantic claims below (the exact set of VMCS triggering events for virtual-interrupt-delivery evaluation; the exact bit layout of EOI-virtualization behavior) are sourced from the Intel SDM Vol. 3C, Chapter 30 (“APIC Virtualization and Virtual Interrupts”) as summarized by web search, because a direct fetch of the SDM PDF and the KVM Forum 2012 APICv slide deck both failed (PDF not parseable / HTTP 403). The KVM source-code claims are verified directly against v6.12 blobs. Reason: primary architecture PDFs were not retrievable in this session. To resolve: read Intel SDM Vol. 3C §30.1–30.6 and AMD APM Vol. 2 §15.29 directly. uncertain
Mental Model — The APIC as a Shared Memory Page the CPU Owns
The conceptual leap is this: a virtual APIC is just a 4 KiB page of memory whose bytes have the layout of a real LAPIC’s memory-mapped register file (the Interrupt Request Register at offset 0x200, the In-Service Register at 0x100, the Task Priority Register at 0x80, the EOI register at 0xB0, and so on). In a software-emulated APIC, that page is owned by the hypervisor and any guest access traps. In a hardware-virtualized APIC, that same page — Intel’s virtual-APIC page, AMD’s AVIC backing page — is handed to the CPU, and the CPU itself reads, writes, and interprets those registers when the guest touches them. The guest believes it is talking to real hardware; it is in fact talking to a memory page the silicon manages on the hypervisor’s behalf.
flowchart TB subgraph SW["Software-emulated APIC (the slow baseline)"] G1["Guest writes EOI / reads ISR / sends IPI"] -->|"every access traps"| X1["VM exit"] X1 --> KVM1["KVM lapic.c emulates the register, re-enters guest"] end subgraph HWI["Intel APICv"] G2["Guest reads/writes LAPIC reg"] -->|"no exit"| VAP["virtual-APIC page<br/>(CPU resolves access)"] VAP --> VID["Virtual-interrupt delivery<br/>evaluates RVI vs SVI vs PPR"] end subgraph HWA["AMD AVIC"] G3["Guest reads/writes LAPIC reg"] -->|"no exit"| ABP["AVIC backing page<br/>(CPU resolves access)"] ABP --> DB["Doorbell + Physical/Logical<br/>APIC ID tables route IPIs"] end
The three regimes for guest APIC access. What it shows: in the software baseline (top), every guest LAPIC touch is a round trip through a VM exit into KVM’s lapic.c emulator. With Intel APICv (middle) or AMD AVIC (bottom), the access lands in a hardware-managed page and the CPU does the work in non-root mode. The insight to take: APICv/AVIC do not change what a LAPIC does — they change who executes it. The expensive part was never the register logic; it was the exit. Move the register logic into the CPU and the exit disappears.
Why the APIC Was the Worst Exit Source
To see why this matters, count exits in the naive design. A guest running a network-heavy or scheduler-heavy workload performs APIC operations on almost every interrupt and every scheduling decision. Consider the lifecycle of one delivered interrupt under software emulation: the hypervisor injects it (one event), the guest’s interrupt handler runs, and the handler finishes by writing the EOI register to tell the APIC “I’m done, deliver the next one” — and that EOI write is an MMIO access that traps (VM Exit Reasons and Handling). Sending an IPI to wake another vCPU is a write to the Interrupt Command Register (ICR), which traps. Reading the Processor Priority Register (PPR) to decide whether to take an interrupt traps. A guest that handles 100,000 interrupts per second can therefore generate hundreds of thousands of exits per second purely from APIC bookkeeping, each costing on the order of a thousand cycles of entry/exit overhead plus the emulation cost. This is the throughput killer that APICv and AVIC exist to remove.
The fix has two halves, and it is worth keeping them distinct because they are independently controllable in hardware:
- Register virtualization — let the guest read and write its own APIC registers (TPR, ISR, IRR, EOI, ICR for some IPI types) without trapping. This is the “no exit on access” half.
- Virtual-interrupt delivery — let the CPU itself evaluate interrupt priority and inject the highest-priority pending virtual interrupt into the guest, without the hypervisor in the loop. This is the “no exit on delivery” half.
Intel exposes these as separate VMCS execution controls; AMD bundles them more tightly into AVIC. We take each vendor in turn.
Intel APICv — Four VMX Controls Working Together
Intel’s APICv is not one feature but a cluster of VM-execution controls in the Virtual Machine Control Structure (VMCS) — the in-memory structure (VMCS and VMCB (Virtual Machine Control Structure)) that holds all per-vCPU control state. KVM’s arch/x86/kvm/vmx/vmx.c programs them. The relevant controls, named exactly as the kernel constants, are:
SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES— “virtualize APIC accesses.” Establishes the APIC-access page (APIC_ACCESS_ADDRin the VMCS) at the guest-physical address0xFEE00000. Guest reads/writes in the xAPIC MMIO window are recognized by the CPU and routed to the virtual-APIC page rather than faulting to memory.SECONDARY_EXEC_APIC_REGISTER_VIRT— “APIC-register virtualization.” Most reads and many writes to LAPIC registers are now fully resolved in hardware against the virtual-APIC page with no exit at all.SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY— “virtual-interrupt delivery” (VID). The CPU evaluates and delivers virtual interrupts itself, and virtualizes the EOI and self-IPI.PIN_BASED_POSTED_INTR— the pin-based control that enables Posted Interrupts (the delivery path for external interrupts).
In v6.12 these are toggled together. vmx_set_virtual_apic_mode() in vmx.c sets SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES when the guest is in xAPIC mode and flexpriority_enabled, or SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE when the guest runs x2APIC. And the capability check in vmx.c shows the dependency chain explicitly: if a CPU lacks virtual-interrupt-delivery, KVM strips PIN_BASED_POSTED_INTR as well (_pin_based_exec_control &= ~PIN_BASED_POSTED_INTR;), because posted-interrupt delivery is meaningless without the hardware that can evaluate the delivered vector — posted interrupts depend on virtual-interrupt delivery.
The virtual-APIC page and the TPR shadow
The virtual-APIC page is a 4 KiB page whose physical address KVM writes into the VIRTUAL_APIC_PAGE_ADDR VMCS field. In KVM this page is literally the LAPIC register file: lapic.c keeps vcpu->arch.apic->regs, and that same memory is what the CPU manipulates. The most important single register is the Task Priority Register (TPR): a guest constantly raises and lowers its TPR to mask lower-priority interrupts (Linux does this on every irq_enter/irq_exit). The original “use TPR shadow” VMX control already let TPR reads/writes hit the page without exiting; APIC-register virtualization generalizes that to most of the register file. The practical upshot, per the KVM Forum 2012 APICv design (Nakajima 2012, retrieved but unparseable — see uncertainty flag), is that the chattiest registers stop trapping.
Virtual-interrupt delivery: RVI, SVI, and the guest interrupt status
The deepest part of APICv is virtual-interrupt delivery. The CPU maintains, in the VMCS, a 16-bit guest interrupt status field split into two bytes that KVM reads and writes via vmx_set_rvi() / vmx_hwapic_isr_update() in vmx.c:
- RVI — Requesting Virtual Interrupt (the low byte) is “the vector of the highest priority virtual interrupt that is requesting service.” KVM updates it with
vmx_hwapic_irr_update()→vmx_set_rvi(), which writes the new high-IRR vector intoGUEST_INTR_STATUS. - SVI — Servicing Virtual Interrupt (the high byte) is “the vector of the highest priority virtual interrupt that is in service.” KVM updates it via
vmx_hwapic_isr_update(), which packsmax_isr << 8intoGUEST_INTR_STATUS.
With VID enabled, whenever the virtual-APIC page state changes (a new interrupt is requested, the guest writes EOI, the guest lowers TPR, a posted interrupt is processed), the CPU re-evaluates: it compares RVI against the Processor Priority Register (PPR) (derived in lapic.c’s __apic_update_ppr() as ppr = tpr & 0xff when TPR dominates, or ppr = isrv & 0xf0 when the in-service vector dominates) and, if a virtual interrupt outranks the current priority and the guest is interruptible, the CPU delivers it directly into the guest — sets the IRR/ISR bits, vectors to the guest IDT — with no exit. A telling comment in lapic.c captures the consequence for KVM’s bookkeeping:
“With APIC virtualization enabled, all caching is disabled because the processor can modify ISR under the hood. Instead just set SVI.”
That is, KVM can no longer cache “the highest in-service vector” in software, because the hardware mutates the ISR on the virtual-APIC page asynchronously. KVM’s job shrinks to keeping RVI/SVI in sync with what userspace or in-kernel devices request, and letting the CPU do the rest.
Virtualized EOI and the EOI-exit bitmap
EOI is the subtle one. A guest’s interrupt handler ends with an EOI write; under VID the CPU performs EOI virtualization entirely on the virtual-APIC page (clear the ISR bit, re-evaluate pending interrupts) with no exit. But there is one case where KVM must be told: level-triggered interrupts routed through a virtual I/O APIC need the EOI to be reflected back so the I/O APIC can re-assert or de-assert the line. Intel handles this with the EOI-exit bitmap — four 64-bit VMCS fields (EOI_EXIT_BITMAP0..3, 256 bits, one per vector) that “determine which virtualized writes to the APIC’s EOI register cause VM exits.” KVM populates them in vmx_load_eoi_exitmap():
void vmx_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)
{
if (!kvm_vcpu_apicv_active(vcpu))
return;
vmcs_write64(EOI_EXIT_BITMAP0, eoi_exit_bitmap[0]);
vmcs_write64(EOI_EXIT_BITMAP1, eoi_exit_bitmap[1]);
vmcs_write64(EOI_EXIT_BITMAP2, eoi_exit_bitmap[2]);
vmcs_write64(EOI_EXIT_BITMAP3, eoi_exit_bitmap[3]);
}Line-by-line: the function no-ops if APICv is inactive for this vCPU; otherwise it programs the 256-bit bitmap into the four VMCS fields. A 1 bit at vector N means “if the guest writes EOI for vector N, take an exit so KVM can notify the I/O APIC.” Edge-triggered interrupts leave their bit 0 and never exit on EOI. This is the elegant compromise: the common case (edge-triggered, e.g. MSI) is exit-free, and only the rare case that genuinely needs the hypervisor pays.
Syncing the posted-interrupt requests into the IRR
APICv’s register side and the posted-interrupt delivery side meet in vmx_sync_pir_to_irr(). When the guest is not running (a posted interrupt arrived while the vCPU was in root mode), KVM drains the Posted-Interrupt Request bitmap (PIR) into the virtual-APIC page’s IRR before re-entry:
int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
{
...
if (pi_test_on(&vmx->pi_desc)) {
pi_clear_on(&vmx->pi_desc);
smp_mb__after_atomic();
got_posted_interrupt =
kvm_apic_update_irr(vcpu, vmx->pi_desc.pir, &max_irr);
}
...
if (!is_guest_mode(vcpu) && kvm_vcpu_apicv_active(vcpu))
vmx_set_rvi(max_irr);
...
}This reads: if the posted-interrupt descriptor’s ON (Outstanding Notification) bit is set, clear it, issue a memory barrier (because an IOMMU can also write PID.ON), merge the PIR bits into the IRR with kvm_apic_update_irr(), and finally publish the highest pending vector into RVI so the hardware delivers it on the next entry. This function is precisely the seam between this note and Posted Interrupts.
AMD AVIC — The Backing Page and the APIC ID Tables
AMD’s AVIC (Advanced Virtual Interrupt Controller) solves the same problem with a different mechanism, centered on three memory structures programmed into the VMCB (Virtual Machine Control Block, AMD’s per-vCPU control structure — VMCS and VMCB (Virtual Machine Control Structure)) by avic_init_vmcb() in arch/x86/kvm/svm/avic.c:
-
The AVIC backing page — the per-vCPU virtual-APIC register file, the direct analogue of Intel’s virtual-APIC page.
avic_init_backing_page()setssvm->avic_backing_page = virt_to_page(vcpu->arch.apic->regs)— again, the sameapic->regsmemory KVM’slapic.cuses. Its physical address goes intovmcb->control.avic_backing_pagemasked byAVIC_HPA_MASK. -
The Physical APIC ID Table — a 4 KiB per-VM table, one 64-bit entry per vCPU, that maps a guest APIC ID to (a) the host-physical address of that vCPU’s backing page and (b) the host physical CPU the vCPU is currently running on. The entry’s bit layout (from
arch/x86/include/asm/svm.h) is the crux of how AVIC routes IPIs:AVIC_PHYSICAL_ID_ENTRY_BACKING_PAGE_MASK(bits 12–51) — pointer to the backing page.AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK(bits 0–11) — the host APIC ID of the pCPU.AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK(bit 62) — the IsRunning bit, the single most important flag in AVIC.AVIC_PHYSICAL_ID_ENTRY_VALID_MASK(bit 63) — entry valid.
-
The Logical APIC ID Table — a 4 KiB per-VM table that maps guest logical APIC IDs to physical IDs, so logically-addressed IPIs (the common Linux flat/cluster mode) can be resolved in hardware. KVM keeps it current via
avic_handle_ldr_update()(Logical Destination Register changes) andavic_handle_dfr_update()(Destination Format Register changes), both invoked fromavic_unaccelerated_access_interception()— the trap handler for the few APIC registers AVIC cannot virtualize directly.
IPI virtualization and the doorbell
The headline AVIC feature is IPI virtualization. When a guest vCPU sends an IPI by writing the Interrupt Command Register on its backing page, AVIC hardware looks up the target in the Physical/Logical APIC ID tables. If the target vCPU’s entry has IsRunning = 1, the hardware writes the vector directly into the target’s backing-page IRR and rings a doorbell to the host pCPU the target is running on — and the target receives the interrupt with no exit on either side. KVM models the doorbell in avic_ring_doorbell():
void avic_ring_doorbell(struct kvm_vcpu *vcpu)
{
int cpu = READ_ONCE(vcpu->cpu);
if (cpu != get_cpu()) {
wrmsrl(MSR_AMD64_SVM_AVIC_DOORBELL, kvm_cpu_get_apicid(cpu));
...
}
put_cpu();
}This writes the target’s host APIC ID to the MSR_AMD64_SVM_AVIC_DOORBELL model-specific register, signalling the remote core to pick up the freshly-posted interrupt without a VM exit. The IsRunning bit is the gate: the entire fast path works only while the target vCPU is actually executing on a physical core.
IsRunning, vcpu_load/put, and the GA log
KVM toggles IsRunning on every vCPU schedule in/out. avic_vcpu_load() sets it and records the current pCPU:
void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
int h_physical_id = kvm_cpu_get_apicid(cpu);
...
if (kvm_vcpu_is_blocking(vcpu))
return;
...
entry &= ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK;
entry |= (h_physical_id & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK);
entry |= AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK;
WRITE_ONCE(*(svm->avic_physical_id_cache), entry);
avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, true);
}It stamps the host APIC ID into bits 0–11, sets IsRunning (bit 62), and tells the IOMMU where the vCPU now lives (for device interrupt posting). avic_vcpu_put() does the inverse, clearing IsRunning before the vCPU blocks. Now the key question: what happens to an IPI (or a device interrupt) whose target is not running? Two paths handle it:
- For guest IPIs, the hardware raises an incomplete-IPI intercept,
avic_incomplete_ipi_interception(). OnAVIC_IPI_FAILURE_TARGET_NOT_RUNNINGit callsavic_kick_target_vcpus()to wake the sleeping target (“the AVIC HW has already set the appropriate IRR bits on the valid target vcpus. So, we just need to kick the appropriate vcpu”). For IPI types AVIC does not virtualize (it “only virtualizes Fixed, Edge-Triggered INTRs”), it falls back to software emulation viakvm_apic_send_ipi(). - For device interrupts via the IOMMU, the GA (Guest APIC) log mechanism fires
avic_ga_log_notifier(), which decodes thega_tag(AVIC_GATAG_TO_VMID/AVIC_GATAG_TO_VCPUID) to find the target vCPU andkvm_vcpu_wake_up()s it — see Posted Interrupts for that path.
x2AVIC and hybrid-AVIC
Plain AVIC only supports xAPIC mode, capping a guest at 255 vCPUs. x2AVIC extends hardware virtualization to x2APIC mode, raising the ceiling to 512 vCPUs (LWN 2022); the kernel sets a global x2avic_enabled flag in avic_hardware_setup() when the CPU advertises X86_FEATURE_X2AVIC, and X2AVIC_MAX_PHYSICAL_ID is 0x1FF (511) versus AVIC’s 0xFE (254). A third mode, hybrid-AVIC, “traps APIC register accesses (i.e. not virtualized by hardware), but leverages the AVIC doorbell for interrupt injection” — useful on hardware without x2AVIC so a guest need not be forced out of x2APIC mode (LWN 2022). As of the 6.18 era, “KVM on AMD CPUs now enables AVIC by default for Zen 4 and newer where x2AVIC is supported” (Phoronix, Linux 6.18 KVM).
Configuration and Enabling
On Intel, APICv is controlled by the kvm_intel.enable_apicv module parameter, exposed in vmx.c as module_param(enable_apicv, bool, 0444); it is on by default where the CPU supports it. On AMD, AVIC is not on by default historically and is enabled with kvm_amd.avic=1; per the search-corroborated kernel behavior, “IOMMU AVIC will be enabled by default when kvm_amd.avic=1,” and “the SVM driver can support both AVIC and x2AVIC modes when [you] load the kvm_amd driver with avic=1.”
Verify it is active at runtime:
# Intel: is APICv enabled?
cat /sys/module/kvm_intel/parameters/enable_apicv # Y means APICv usable
# AMD: is AVIC enabled?
cat /sys/module/kvm_amd/parameters/avic # Y / 1 means AVIC requested
# Per-VM/per-vCPU truth: trace the accept-IRQ fast path
# (fires when an interrupt is delivered via APICv, not via injection)
perf record -e kvm:kvm_apicv_accept_irq -a sleep 5The kvm:kvm_apicv_accept_irq tracepoint (emitted from vmx_deliver_interrupt() when vmx_deliver_posted_interrupt() succeeds) firing means interrupts are taking the hardware path; its absence under interrupt load means APICv is inhibited.
Failure Modes and APICv Inhibition
APICv/AVIC is not always usable even when the hardware supports it. KVM tracks inhibit reasons and silently falls back to software emulation when any apply. The two most common:
- Nested virtualization. When a vCPU enters guest mode of a nested hypervisor, AVIC is deactivated:
avic_vcpu_get_apicv_inhibit_reasons()returnsAPICV_INHIBIT_REASON_NESTEDforis_guest_mode(vcpu). So AVIC and nested KVM are mutually exclusive on a given vCPU — a frequent surprise for people running nested setups who find their “AVIC-enabled” guest still trapping on every APIC access. - APIC ID aliasing / non-identity mapping. APICv requires APIC ID == vCPU ID (an identity map); if a guest reprograms an APIC ID such that the optimized APIC map breaks, KVM sets an inhibit.
lapic.cnotes: “The optimized map is effectively KVM’s internal version of APICv, and all unwanted aliasing that results in disabling the optimized map also applies to APICv.” Hyper-V’s synthetic interrupt controller (SynIC) and certain x2APIC configurations also inhibit it.
The diagnostic symptom is uniform: high kvm_exit rates with reason APIC_ACCESS / MSR_WRITE (for x2APIC) / EOI_INDUCED, and an absent kvm_apicv_accept_irq tracepoint, despite the CPU advertising the feature. The fix is to remove the inhibit cause (don’t nest, don’t alias APIC IDs, ensure x2APIC alignment).
A second-order subtlety: because hardware mutates the ISR/IRR on the backing page asynchronously, KVM cannot trust its software caches. The lapic.c comment “isr_count is always 1 … with APIC virtualization enabled” reflects a deliberate simplification — bugs in this area historically manifested as lost or duplicated interrupts, which is why the EOI-exit bitmap and the PIR-to-IRR sync are written so defensively (explicit memory barriers pairing with possible IOMMU writes).
Alternatives and When to Choose Them
The honest comparison runs along an axis of how much hardware does for you:
- Software-emulated LAPIC (no APICv/AVIC). Maximum portability — works on any VMX/SVM CPU, composes freely with nesting. The cost is an exit per APIC access. Choose it when you must (nested guests, ancient hardware, or when an inhibit forces it).
- Intel APICv / AMD AVIC (this note). Near-zero exits for guest-originated APIC operations and CPU-driven delivery. Choose it for every interrupt-heavy or IPI-heavy guest on modern hardware. The cost is the inhibit constraints above.
- APICv/AVIC + Posted Interrupts. Adds exit-free delivery of external (device/IPI) interrupts on top of register virtualization. This is the full stack and the only way to get truly exit-free I/O interrupt handling for passthrough devices. Choose it whenever you do device passthrough with high interrupt rates.
There is no scenario where you’d prefer software emulation for performance — the choice is purely about whether a constraint (nesting, aliasing) forces you off the hardware path.
Production Notes
The headline real-world result, from AMD’s own engineering and corroborated by the Oracle Linux team, is that AVIC dramatically cuts IPI and timer overhead for many-vCPU guests — the workloads where software APIC emulation hurt most were exactly databases and JVMs that thrash IPIs. The Oracle Linux post “How to enable AMD AVIC and speed up your VMs” frames AVIC precisely as letting “guest operating systems access APIC registers directly, improving performance by reducing the overhead associated with interrupt delivery.”
Uncertain
Specific AVIC performance numbers (percentage IPI-latency or throughput improvements) were not retrievable: the Oracle blog fetch returned HTTP 403 and the AMD XenSummit slides were not fetched in full. Reason: source blocked. To resolve: read blogs.oracle.com/linux/amd-avic and the AMD AVIC XenSummit 2012 deck for the benchmark figures. uncertain
Two operational gotchas dominate real deployments. First, nested virtualization silently disables AVIC (the inhibit above), so a cloud guest that itself runs KVM loses the benefit — this is why nested-heavy CI fleets historically saw less gain. Second, on AMD, AVIC long required disabling x2APIC in the guest until x2AVIC/hybrid-AVIC landed; mismatched expectations here (a guest booting in x2APIC mode on AVIC-only hardware) produced the well-known “kvm_amd avic=1 requires x2apic disabled” class of bug reports. As of the 6.12/6.18 LTS line with x2AVIC on Zen 4+, this is largely resolved and AVIC is increasingly default-on.
See Also
- Posted Interrupts — the delivery sibling: how external (device/IPI) interrupts reach a running vCPU with no exit, built directly on the virtual-APIC page / backing page this note describes
- Interrupt Injection and the Virtual APIC — KVM’s software model of the virtual APIC and how it injects when APICv/AVIC is off or inhibited
- irqfd and ioeventfd — the eventfd plumbing that ties virtio/VFIO interrupt sources to the APIC path
- VFIO Framework — passthrough devices whose interrupts benefit most from APICv + posted interrupts
- Intel VMX and AMD SVM — the root/non-root and host/guest modes APICv/AVIC operate within
- VMCS and VMCB (Virtual Machine Control Structure) — where the APICv execution controls and AVIC table pointers live
- VM Exit Reasons and Handling — the exit costs APICv exists to eliminate (
APIC_ACCESS,EOI_INDUCED,MSR_WRITE) - KVM vCPU Run Loop — where
sync_pir_to_irrruns on entry, draining posted interrupts into the IRR - Linux Virtualization MOC — the parent map; this note sits in §8, the “eliminate the VM exit” interrupt-virtualization section