mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
platform/kvm: map vdso and vvar into a guest address space
Right now, each vdso call triggers vmexit. VDSO and VVAR pages are
mapped with VM_IO and get_user_pages fails for such vma-s. KVM was not
able to handle this case up to the v4.8 kernel. This problem was fixed by
add6a0cd1c5ba ("KVM: MMU: try to fix up page faults before giving up").
For some unknown reasons, it still doesn't work in case of nested
virtualization.
Before:
BenchmarkKernelVDSO-6 252519 4598 ns/op
After:
BenchmarkKernelVDSO-6 34431957 34.91 ns/op
PiperOrigin-RevId: 405715941
This commit is contained in:
@@ -81,7 +81,7 @@ const (
|
||||
X86FeatureAVX
|
||||
X86FeatureF16C
|
||||
X86FeatureRDRAND
|
||||
_ // ecx bit 31 is reserved.
|
||||
X86FeatureHypervisor
|
||||
)
|
||||
|
||||
// Block 1 constants are all of the "basic" feature bits returned by a cpuid in
|
||||
|
||||
@@ -462,6 +462,22 @@ func TestRdtsc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestKernelVDSO(t *testing.T) {
|
||||
// Note that the target passed here is irrelevant, we never execute SwitchToUser.
|
||||
applicationTest(t, true, testutil.AddrOfGetpid(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
|
||||
// iteration does not include machine.Get() / machine.Put().
|
||||
const n = 100
|
||||
for i := 0; i < n; i++ {
|
||||
bluepill(c)
|
||||
time.Now()
|
||||
}
|
||||
if c.guestExits >= n {
|
||||
t.Errorf("vdso calls trigger vmexit")
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkApplicationSyscall(b *testing.B) {
|
||||
var (
|
||||
i int // Iteration includes machine.Get() / machine.Put().
|
||||
@@ -498,6 +514,18 @@ func BenchmarkKernelSyscall(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkKernelVDSO(b *testing.B) {
|
||||
// Note that the target passed here is irrelevant, we never execute SwitchToUser.
|
||||
applicationTest(b, true, testutil.AddrOfGetpid(), func(c *vCPU, regs *arch.Registers, pt *pagetables.PageTables) bool {
|
||||
// iteration does not include machine.Get() / machine.Put().
|
||||
for i := 0; i < b.N; i++ {
|
||||
bluepill(c)
|
||||
time.Now()
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkWorldSwitchToUserRoundtrip(b *testing.B) {
|
||||
// see BenchmarkApplicationSyscall.
|
||||
var (
|
||||
|
||||
@@ -279,10 +279,13 @@ func (c *vCPU) fault(signal int32, info *linux.SignalInfo) (hostarch.AccessType,
|
||||
// Reset the pointed SignalInfo.
|
||||
*info = linux.SignalInfo{Signo: signal}
|
||||
info.SetAddr(uint64(faultAddr))
|
||||
accessType := hostarch.AccessType{
|
||||
Read: code&(1<<1) == 0,
|
||||
Write: code&(1<<1) != 0,
|
||||
Execute: code&(1<<4) != 0,
|
||||
accessType := hostarch.AccessType{}
|
||||
if signal == int32(unix.SIGSEGV) {
|
||||
accessType = hostarch.AccessType{
|
||||
Read: code&(1<<1) == 0,
|
||||
Write: code&(1<<1) != 0,
|
||||
Execute: code&(1<<4) != 0,
|
||||
}
|
||||
}
|
||||
if !accessType.Write && !accessType.Execute {
|
||||
info.Code = 1 // SEGV_MAPERR.
|
||||
|
||||
@@ -40,14 +40,9 @@ var mapsLine = regexp.MustCompile("([0-9a-f]+)-([0-9a-f]+) ([r-][w-][x-][sp]) ([
|
||||
// physical map. Virtual regions need to be excluded if get_user_pages will
|
||||
// fail on those addresses, preventing KVM from satisfying EPT faults.
|
||||
//
|
||||
// This includes the VVAR page because the VVAR page may be mapped as I/O
|
||||
// memory. And the VDSO page is knocked out because the VVAR page is not even
|
||||
// recorded in /proc/self/maps on older kernels; knocking out the VDSO page
|
||||
// prevents code in the VDSO from accessing the VVAR address.
|
||||
//
|
||||
// This is called by the physical map functions, not applyVirtualRegions.
|
||||
func excludeVirtualRegion(r virtualRegion) bool {
|
||||
return r.filename == "[vvar]" || r.filename == "[vdso]"
|
||||
return false
|
||||
}
|
||||
|
||||
// applyVirtualRegions parses the process maps file.
|
||||
|
||||
Reference in New Issue
Block a user