Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: x86: Use "checked" versions of get_user() and put_user()

Use the normal, checked versions for get_user() and put_user() instead of
the double-underscore versions that omit range checks, as the checked
versions are actually measurably faster on modern CPUs (12%+ on Intel,
25%+ on AMD).

The performance hit on the unchecked versions is almost entirely due to
the added LFENCE on CPUs where LFENCE is serializing (which is effectively
all modern CPUs), which was added by commit 304ec1b05031 ("x86/uaccess:
Use __uaccess_begin_nospec() and uaccess_try_nospec"). The small
optimizations done by commit b19b74bc99b1 ("x86/mm: Rework address range
check in get_user() and put_user()") likely shave a few cycles off, but
the bulk of the extra latency comes from the LFENCE.

Don't bother trying to open-code an equivalent for performance reasons, as
the loss of inlining (e.g. see commit ea6f043fc984 ("x86: Make __get_user()
generate an out-of-line call") is largely a non-factor (ignoring setups
where RET is something entirely different),

As measured across tens of millions of calls of guest PTE reads in
FNAME(walk_addr_generic):

__get_user() get_user() open-coded open-coded, no LFENCE
Intel (EMR) 75.1 67.6 75.3 65.5
AMD (Turin) 68.1 51.1 67.5 49.3

Note, Hyper-V MSR emulation is not a remotely hot path, but convert it
anyways for consistency, and because there is a general desire to remove
__{get,put}_user() entirely.

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Closes: https://lore.kernel.org/all/CAHk-=wimh_3jM9Xe8Zx0rpuf8CPDu6DkRCGb44azk0Sz5yqSnw@mail.gmail.com
Cc: Borislav Petkov <bp@alien8.de>
Link: https://patch.msgid.link/20251106210206.221558-1-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>

+2 -2
+1 -1
arch/x86/kvm/hyperv.c
··· 1568 1568 * only, there can be valuable data in the rest which needs 1569 1569 * to be preserved e.g. on migration. 1570 1570 */ 1571 - if (__put_user(0, (u32 __user *)addr)) 1571 + if (put_user(0, (u32 __user *)addr)) 1572 1572 return 1; 1573 1573 hv_vcpu->hv_vapic = data; 1574 1574 kvm_vcpu_mark_page_dirty(vcpu, gfn);
+1 -1
arch/x86/kvm/mmu/paging_tmpl.h
··· 402 402 goto error; 403 403 404 404 ptep_user = (pt_element_t __user *)((void *)host_addr + offset); 405 - if (unlikely(__get_user(pte, ptep_user))) 405 + if (unlikely(get_user(pte, ptep_user))) 406 406 goto error; 407 407 walker->ptep_user[walker->level - 1] = ptep_user; 408 408