Overview
========
Modern AMD processors provide hardware-assisted LAPIC virtualization
support known as AVIC (Advanced Virtual Interrupt Controller). See
"15.29 Advanced Virtual Interrupt Controller" in [1].
The main objective is to improve interrupt delivery for a given VM
by avoiding expensive LAPIC emulation in hypervisor.
At a high-level, there are several types of interrupts needed to be
managed for a Xen domain:
(a) IPIs
(b) Posted interrupts (Xen-to-domain, such as event channels)
(c) External devices (MSI, needs coordination with IOMMU)
This initial implementation addresses (a) and (b).
Current limitations:
- Only vLAPIC in xAPIC mode with AVIC enabled
- No GALog support
Please see [2] for more details on the scope.
This work is loosely based on previously submitted work [3] and [4],
including AVIC support in Linux KVM.
The feature is experimental and disabled in the default configuration.
Patch Series
============
Basically the entire work is divided into three big series:
(A) Current series: initial SVM AVIC [5]
(B) XTF interrupt latency test [6]
(C) XTF infra in public CI [5]
For this AVIC series:
Patches 1, 5, 6, 9, 11-16 introduce various cleanups and fixups.
Patch 20 changes XEN_DOMCTL_debug_op implementation to allow
more debug operations added (like enabling/disabling AVIC at
run-time - used by the XTF test).
Patches 2, 3, 4, 7, 8, 10, 17, 18-25 represent AVIC implementation
including performance counters and run-time controls via debug
hypercall.
Patches 26-28 cover various documentation updates.
Testing
=======
Mainly, the patch series has been tested through public hardware runners.
Full CI run (including manually scheduled jobs) is available at [10].
Approach: I decided to write my own synthetic XTF [6] which runs as dom0
on a hardware target to capture some fine-grained metrics for IPIs and
posted interrupts (event channel).
System | Test results
----------------------------------------+-------------
Zen2: Family 23 (0x17), Model 96 (0x60) | [7]
Zen3: Family 25 (0x19), Model 68 (0x44) | [8]
Zen5: Family 26 (0x1a), Model 36 (0x24) | [9]
Synthetic test:
vCPU0 vCPU1
executes some workload
+-> t0
| |
| write ICR2 [*]
r write ICR
e |---------------- interrupt -----------> t0' = recv_tsc
p return from APIC emulation [*'] ISR
e | t1' = handler_tsc
a t1
t |
| sync against ISR
| |
+---+
First 1K samples is dropped, total number of samples is 20M.
IRQ delivery latency : t0' - t0
IRQ issue latency : t1 - t0
IRQ handler latency : t1' - t0'
[*] For event channel that is EVTCHNOP_send hypercall.
[*'] For event channel that is return from the EVTCHNOP_send hypercall.
For all tested Zen2/Zen3/Zen5 targets there are overall improvements
w/ AVIC enabled: the test duration decreased, IPI delivery improved,
ISR latency improved.
Basically, AVIC optimizes long tails in the latency distribution leading
to more deterministic workload.
There's some peculiar behavior w.r.t. doorbell MSR which roughly takes 2us on
Zen5 - fallback to vcpu_kick() turned out to be faster. I added a run-time
configuration knob for a fallback, just in case.
There are also some outliers in the latency distributions which I haven't
ruled out yet: like look at max latency w/ and w/o AVIC in [7], [8], [9]: once
it a blue moon I've seen AVIC giving p99.999 values as a maximum (~15us vs
~45us for the entire 20M population).
Results for Zen5 target are more pronouced, so I am duplicating them below.
Zen5: Workload #1: IPI
----------------------
no AVIC | AVIC
---------------------------------------------+------------------------------------
A: noaccel: IPI send latency (VCPU#0) (20000000 samples)
B: accel: IPI send latency (VCPU#0) (20000000 samples)
metric cycles A time A | cycles B time B
min 12240 6.131 us | 140 70 ns
avg 12955 6.489 us | 155 77 ns
p50 < 13000 6.512 us | < 170 85 ns
p99 < 14000 7.013 us | < 170 85 ns
p99.9 < 34000 17.031 us | < 170 85 ns
p99.99 < 35000 17.532 us | < 190 95 ns
p99.999 < 98000 49.092 us | < 23000 11.521 us
max 99100 49.643 us | 86660 43.411 us
A: noaccel: IPI delivery latency (VCPU#0 -> VCPU#1) (20000000 samples)
B: accel: IPI delivery latency (VCPU#0 -> VCPU#1) (20000000 samples)
metric cycles A time A | cycles B time B
min 0 0 ns | 620 310 ns
avg 1402 702 ns | 626 313 ns
p50 < 1400 701 ns | < 650 325 ns
p99 < 1500 751 ns | < 700 350 ns
p99.9 < 20000 10.018 us | < 700 350 ns
p99.99 < 20000 10.018 us | < 700 350 ns
p99.999 < 71000 35.566 us | < 23000 11.521 us
max 87740 43.952 us | 87000 43.581 us
A: noaccel: IPI handler latency (VCPU#1) (20000000 samples)
B: accel: IPI handler latency (VCPU#1) (20000000 samples)
metric cycles A time A | cycles B time B
min 15520 7.774 us | 160 80 ns
avg 16256 8.143 us | 174 87 ns
p50 < 17000 8.515 us | < 190 95 ns
p99 < 17000 8.515 us | < 190 95 ns
p99.9 < 17000 8.515 us | < 190 95 ns
p99.99 < 17000 8.515 us | < 190 95 ns
p99.999 < 23000 11.521 us | < 190 95 ns
max 33880 16.971 us | 200 100 ns
Workload time 182552 ms 9919 ms
(20000000 samples) (~18x boost)
Zen5: Workload #2: evtchn
-------------------------
no AVIC | AVIC
---------------------------------------------+------------------------------------
A: noaccel: evtchn send latency (VCPU#0) (20000000 samples)
B: accel: evtchn send latency (VCPU#0) (20000000 samples)
metric cycles A time A | cycles B time B
min 5800 2.905 us | 6020 3.015 us
avg 5988 2.999 us | 6204 3.107 us
p50 < 6000 3.005 us | < 6200 3.105 us
p99 < 6100 3.055 us | < 6200 3.105 us
p99.9 < 27000 13.525 us | < 27000 13.525 us
p99.99 < 28000 14.026 us | < 29000 14.527 us
p99.999 < 28000 14.026 us | < 92000 46.086 us
max 92160 46.166 us | 92680 46.427 us
A: noaccel: evtchn delivery latency (VCPU#0 -> VCPU#1) (20000000 samples)
B: accel: evtchn delivery latency (VCPU#0 -> VCPU#1) (20000000 samples)
metric cycles A time A | cycles B time B
min 0 0 ns | 5440 2.725 us
avg 1412 707 ns | 5497 2.753 us
p50 < 1400 701 ns | < 5600 2.805 us
p99 < 1500 751 ns | < 5600 2.805 us
p99.9 < 19000 9.517 us | < 6000 3.005 us
p99.99 < 19000 9.517 us | < 6200 3.105 us
p99.999 < 71000 35.566 us | < 28000 14.026 us
max 87600 43.882 us | 91760 45.966 us
A: noaccel: evtchn handler latency (VCPU#1) (20000000 samples)
B: accel: evtchn handler latency (VCPU#1) (20000000 samples)
metric cycles A time A | cycles B time B
min 15540 7.784 us | 180 90 ns
avg 15803 7.916 us | 184 92 ns
p50 < 16000 8.015 us | < 190 95 ns
p99 < 16000 8.015 us | < 210 105 ns
p99.9 < 16000 8.015 us | < 210 105 ns
p99.99 < 16000 8.015 us | < 210 105 ns
p99.999 < 24000 12.022 us | < 210 105 ns
max 33120 16.591 us | 200 100 ns
metric cycles A time A | cycles B time B
min 0 0 ns | 5400 2.705 us
avg 1425 713 ns | 5467 2.738 us
p50 < 1500 751 ns | < 5500 2.755 us
p99 < 1500 751 ns | < 5600 2.805 us
p99.9 < 19000 9.517 us | < 6000 3.005 us
p99.99 < 19000 9.517 us | < 6200 3.105 us
p99.999 < 71000 35.566 us | < 28000 14.026 us
max 87800 43.981 us | 91940 46.055 us
Workload time 175951 ms 62951 ms
(20000000 samples) (~2.5x boost)
Thanks a lot to Marek for hooking Zen5 runner to public CI.
Thanks a lot to Stefano and Andrew with advices and fixing the access to the
fleet of hardware runners.
References
==========
[1] AMD APM Volumes 1-5:
https://docs.amd.com/v/u/en-US/40332_4.11_APM_Vol1-5
[2] Xen Summit 2026 SVM AVIC:
- YouTube: https://www.youtube.com/watch?v=RhaJsOU_PIA&list=PLGe6RWOETgTQ
- Slides: https://hosted-files.sched.co/xensummit2026/05/dmukhin_2026_x86_SVM_AVIC.pdf?_gl=1*158gk36*_gcl_au*MjU3OTQwMDQ2LjE3ODg0NzE3NDQ.*FPAU*MjU3OTQwMDQ2LjE3ODg0NzE3NDQ
[3] Archive v1:
https://lore.kernel.org/xen-devel/1474264368-4104-1-git-send-email-suravee.suthikulpanit@amd.com/
[4] Archive v2:
https://lore.kernel.org/xen-devel/20180507210753.2280-1-Janakarajan.Natarajan@amd.com/
[5] Current patch series:
https://gitlab.com/xen-project/people/dmukhin/xen/-/commits/svm-avic-v3
[6] XTF interrupt latency test:
https://gitlab.com/xen-project/people/dmukhin/xtf/-/tree/x86_64-ipi-latency?ref_type=heads
[7] Zen2: Family 23 (0x17), Model 96 (0x60): interrupt latency results:
https://gist.github.com/dmkhn/289f1af656cce74c0fbc81642727a2a4
[8] Zen3: Family 25 (0x19), Model 68 (0x44): interrupt latency results:
https://gist.github.com/dmkhn/9192105efc49cfedd15e2820b8ad23fd
[9] Zen5: Family 26 (0x1a), Model 36 (0x24): interrupt latency results:
https://gist.github.com/dmkhn/e9f50b43cbc48b82175ec517d6cfe6f5
[10] CI:
https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/3025987819
Denis Mukhin (28):
x86/hvm: move SVM feature printout to a separate function
x86/hvm: report SVM AVIC support
x86/hvm: report SVM x2AVIC support
x86/hvm: avic: add domain state initialization
x86/hvm: cleanup svm_create_vmcb()
x86/hvm: introduce vlapic_reg_read()
x86/hvm: avic: add VMCB state initialization
x86/hvm: avic: add AVIC_INCOMP_IPI VMEXIT handler
x86/hvm: fix hvm_emulate_one_vm_event() definition
x86/hvm: avic: add AVIC_NOACCEL VMEXIT handler
x86/hvm: introduce vcpu_is_vlapic_tpr_emulated()
x86: apic: use APIC_EIO_ACK instead of open-coded 0
x86/time: fix typo s/interruopt/interrupt/
x86/hvm: vlapic: update signatures of both vlapic_{clear,test}_*
x86/hvm: vlapic: add checks before posted interrupt delivery
x86/hvm: move VINTR disable to svm_clear_intr_window()
x86/hvm: avic: route interrupt delivery to vCPU
x86/hvm: add facility to print hardware-assisted vLAPIC info
x86/hvm: avic: enable SVM AVIC statistics reporting
x86/hvm: rework XEN_DOMCTL_debug_op
x86/hvm: enable debug hypercall for posted interrupts
x86/hvm: avic: ensure SVM AVIC supports xAPIC operation only
x86/hvm: avic: enable vLAPIC mode switch hook
x86/hvm: avic: ensure SVM AVIC is disabled with nested guest
x86/hvm: avic: account for AMD erratum 1235
docs: add coverage for svm_avic=
CHANGELOG.md: add note on initial SVM AVIC support
MAINTAINERS: add myself as SVM AVIC reviewer
CHANGELOG.md | 3 +
MAINTAINERS | 11 +
docs/misc/xen-command-line.pandoc | 23 +-
xen/arch/x86/cpu-policy.c | 3 +-
xen/arch/x86/domctl.c | 24 +-
xen/arch/x86/hvm/emulate.c | 5 +
xen/arch/x86/hvm/hvm.c | 74 +-
xen/arch/x86/hvm/svm/Makefile | 1 +
xen/arch/x86/hvm/svm/avic.c | 921 +++++++++++++++++++++++
xen/arch/x86/hvm/svm/avic.h | 41 +
xen/arch/x86/hvm/svm/intr.c | 22 +
xen/arch/x86/hvm/svm/nestedsvm.c | 9 +-
xen/arch/x86/hvm/svm/svm.c | 186 ++++-
xen/arch/x86/hvm/svm/svm.h | 2 +
xen/arch/x86/hvm/svm/vmcb.c | 29 +-
xen/arch/x86/hvm/svm/vmcb.h | 27 +-
xen/arch/x86/hvm/vlapic.c | 25 +-
xen/arch/x86/hvm/vmx/vmx.c | 4 +
xen/arch/x86/include/asm/apic.h | 3 +-
xen/arch/x86/include/asm/apicdef.h | 2 +-
xen/arch/x86/include/asm/hvm/hvm.h | 47 +-
xen/arch/x86/include/asm/hvm/svm-types.h | 31 +
xen/arch/x86/include/asm/hvm/svm.h | 4 +
xen/arch/x86/include/asm/hvm/vlapic.h | 1 +
xen/arch/x86/include/asm/msr-index.h | 1 +
xen/arch/x86/irq.c | 24 +
xen/arch/x86/time.c | 2 +-
xen/include/public/domctl.h | 2 +
28 files changed, 1411 insertions(+), 116 deletions(-)
create mode 100644 xen/arch/x86/hvm/svm/avic.c
create mode 100644 xen/arch/x86/hvm/svm/avic.h
--
2.55.0