drivers: hv: SNP perf fixes - #156
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves Hyper-V VTL (VTL2) performance for SEV-SNP guests by reducing avoidable user/kernel transitions and unnecessary IPIs, and by adding a kernel fast-path for select synthetic timer operations.
Changes:
- Add an SNP per-vCPU context block to the shared
mshv_vtl_runpage, including a VMSA tweak bitmap and synthetic timer (stimer0) state. - Coalesce proxy-IRR scan wakeups so CPUs aren’t sent reschedule IPIs when a scan is already pending.
- Add SNP kernel handling for interrupt-entry and for
HV_X64_MSR_STIMER0_COUNTwrites (including an hrtimer-backed wakeup path).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| drivers/hv/mshv_vtl.h | Introduces snp_vp_context in the run-page union and defines stimer0 flag bits used by the SNP fast-path. |
| drivers/hv/mshv_vtl_main.c | Implements IPI coalescing for proxy IRR scans, adds VMSA register decode helpers, and adds an hrtimer-backed kernel fast-path for stimer0 count writes and interrupt-entry handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b03f589 to
c9d4f91
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
drivers/hv/mshv_vtl_main.c:566
wake_cpusis a fullstruct cpumaskon the stack. This adds another potentially large cpumask stack allocation on top of callers that already have a stack cpumask (e.g.mshv_snp_handle_simple_icr_write()/mshv_tdx_handle_simple_icr_write()), which can push stack usage over kernel frame size limits on configs with largeNR_CPUS.
Consider avoiding a second on-stack cpumask here (e.g., reuse/mutate local_mask to hold only CPUs that actually need waking, or send per-CPU IPIs when xchg(&run->scan_proxy_irr, 1) transitions 0->1).
struct cpumask wake_cpus;
unsigned int cpu;
drivers/hv/mshv_vtl_main.c:2334
stimer0_flagsis modified via atomic operations elsewhere (e.g.atomic_or()/atomic_set()), but this check usesREAD_ONCE(). Useatomic_read()for consistency with the atomic updates and to avoid mixed atomic/non-atomic access patterns.
if (!(READ_ONCE(run->snp_context.stimer0_flags) &
MSHV_VTL_SNP_STIMER0_KERNEL_UPDATE))
return;
c9d4f91 to
22e8b5b
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (4)
drivers/hv/mshv_vtl_main.c:2117
- Same issue as the read helper:
byte_indexis au8and can wrap or go out of bounds when derived fromoffset, leading to incorrect tweak decisions or out-of-bounds bitmap access. Use a wider index type and validate bounds before indexing.
u8 byte_index = offset / 64;
u8 bit_index = (offset % 64) / 8;
if (READ_ONCE(run->snp_context.vmsa_tweak_bitmap[byte_index]) & BIT(bit_index))
value ^= READ_ONCE(*(u64 *)vmsa->reserved_0x300);
drivers/hv/mshv_vtl_main.c:569
wake_cpusadds another full-sizestruct cpumaskon the stack in a hot path that already has stack-allocated cpumasks in the call chain (e.g.mshv_snp_handle_simple_icr_write()), which can become a significant stack footprint on large-NR_CPUS builds. Consider avoiding an additional stack cpumask (e.g. reuse/mutatelocal_maskas the wake mask, or send IPIs directly for the CPUs that need waking).
struct cpumask wake_cpus;
unsigned int cpu;
cpumask_clear(&wake_cpus);
for_each_cpu(cpu, local_mask) {
drivers/hv/mshv_vtl_main.c:2104
byte_indexis au8, so large offsets will silently wrap (and can also index pastvmsa_tweak_bitmap[]), potentially causing incorrect tweak handling or out-of-bounds reads. Use a wider type for the index and add a bounds check before indexing the bitmap.
This issue also appears on line 2113 of the same file.
u8 byte_index = offset / 64;
u8 bit_index = (offset % 64) / 8;
u64 value = READ_ONCE(*(u64 *)((u8 *)vmsa + offset));
if (READ_ONCE(run->snp_context.vmsa_tweak_bitmap[byte_index]) & BIT(bit_index)) {
drivers/hv/mshv_vtl_main.c:2334
stimer0_flagsis treated as anatomic_telsewhere (atomic_or/atomic_set), but this check usesREAD_ONCE(). Useatomic_read()here for consistency and to avoid mixing atomic/non-atomic accesses to the same variable.
if (!(READ_ONCE(run->snp_context.stimer0_flags) &
MSHV_VTL_SNP_STIMER0_KERNEL_UPDATE))
return;
Share the VMSA key with userspace to allow decoding register values. Return to VTL0 if the only operation was a kernel handled interrupt. Don't resend IPI to a processor for interrupt processing. Signed-off-by: Brian Perkins <brian.perkins@microsoft.com>
22e8b5b to
b45091a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
drivers/hv/mshv_vtl_main.c:1065
- The stimer0 hrtimer callback only sets
run->cancel = 1, but that doesn’t necessarily wake the VTL thread if it is sleeping in theidle2vtl0(schedule) path. That can prevent userspace from being woken for timer delivery as the comment intends.
static enum hrtimer_restart mshv_snp_stimer0_timer_fn(struct hrtimer *timer)
{
struct mshv_vtl_per_cpu *per_cpu =
container_of(timer, struct mshv_vtl_per_cpu, snp_stimer0_timer);
struct mshv_vtl_run *run = READ_ONCE(per_cpu->run);
if (run) {
/* Wake userspace to handle timer delivery. */
atomic_or(MSHV_VTL_SNP_STIMER0_EXPIRED,
(atomic_t *)&run->snp_context.stimer0_flags);
WRITE_ONCE(run->cancel, 1);
}
drivers/hv/mshv_vtl.h:128
struct snp_vp_contextrelies on implicit tail padding (4 bytes) to reach 1024 bytes. For a fixed-size context shared with user mode, make the padding explicit so the layout doesn’t depend on trailing alignment rules.
struct snp_vp_context {
__u8 vmsa_tweak_bitmap[64];
__u64 stimer0_config;
__u64 stimer0_count;
__u64 stimer0_programmed_ref_time;
__u32 stimer0_flags;
__u8 pad[928];
};
Signed-off-by: Brian Perkins <brian.perkins@microsoft.com>
b45091a to
44cd525
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
drivers/hv/mshv_vtl_main.c:2121
- Same alignment concern in
mshv_snp_write_vmsa_reg(): dereferencing au64 *derived from au8 *can trigger-Wcast-align. Cast throughvoid *to avoid alignment warnings and keep the access intent clear.
if (READ_ONCE(run->snp_context.vmsa_tweak_bitmap[byte_index]) & BIT(bit_index))
value ^= READ_ONCE(*(u64 *)vmsa->reserved_0x300);
WRITE_ONCE(*(u64 *)((u8 *)vmsa + offset), value);
drivers/hv/mshv_vtl_main.c:2105
mshv_snp_read_vmsa_reg()casts au8 *tou64 *and dereferences it. This can trigger-Wcast-align(and makes the alignment assumption implicit). Cast throughvoid *(or use an unaligned helper) to make the intent explicit and avoid alignment warnings.
This issue also appears on line 2118 of the same file.
u64 value = READ_ONCE(*(u64 *)((u8 *)vmsa + offset));
if (READ_ONCE(run->snp_context.vmsa_tweak_bitmap[byte_index]) & BIT(bit_index)) {
static_assert(offsetof(struct sev_es_save_area, reserved_0x300) == 0x300);
value ^= READ_ONCE(*(u64 *)vmsa->reserved_0x300);
dc23d48
into
microsoft:product/hcl-main/6.18
Decode register values with vmsa key.
Don't send ipi to cpu that is already pending an interrupt scan.
Check for interrupts handled entirely in the kernel.
Add kernel fast-path for some stimer0 operations.