mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Avoid excessive Tgkill and wait operations.
The required states may simply not be observed by the thread running bounce, so track guest and user generations to ensure that at least one of the desired state transitions happens. Fixes #3532 PiperOrigin-RevId: 336908216
This commit is contained in:
committed by
gVisor bot
parent
60f159b558
commit
d9b32efb30
@@ -62,6 +62,9 @@ func bluepillArchContext(context unsafe.Pointer) *arch.SignalContext64 {
|
||||
//
|
||||
//go:nosplit
|
||||
func bluepillGuestExit(c *vCPU, context unsafe.Pointer) {
|
||||
// Increment our counter.
|
||||
atomic.AddUint64(&c.guestExits, 1)
|
||||
|
||||
// Copy out registers.
|
||||
bluepillArchExit(c, bluepillArchContext(context))
|
||||
|
||||
@@ -89,9 +92,6 @@ func bluepillHandler(context unsafe.Pointer) {
|
||||
// Sanitize the registers; interrupts must always be disabled.
|
||||
c := bluepillArchEnter(bluepillArchContext(context))
|
||||
|
||||
// Increment the number of switches.
|
||||
atomic.AddUint32(&c.switches, 1)
|
||||
|
||||
// Mark this as guest mode.
|
||||
switch atomic.SwapUint32(&c.state, vCPUGuest|vCPUUser) {
|
||||
case vCPUUser: // Expected case.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
pkgcontext "gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
@@ -75,6 +77,9 @@ func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac a
|
||||
// Clear the address space.
|
||||
cpu.active.set(nil)
|
||||
|
||||
// Increment the number of user exits.
|
||||
atomic.AddUint64(&cpu.userExits, 1)
|
||||
|
||||
// Release resources.
|
||||
c.machine.Put(cpu)
|
||||
|
||||
|
||||
@@ -412,9 +412,9 @@ func TestWrongVCPU(t *testing.T) {
|
||||
// Basic test, one then the other.
|
||||
bluepill(c1)
|
||||
bluepill(c2)
|
||||
if c2.switches == 0 {
|
||||
if c2.guestExits == 0 {
|
||||
// Don't allow the test to proceed if this fails.
|
||||
t.Fatalf("wrong vCPU#2 switches: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
t.Fatalf("wrong vCPU#2 exits: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
}
|
||||
|
||||
// Alternate vCPUs; we expect to need to trigger the
|
||||
@@ -423,11 +423,11 @@ func TestWrongVCPU(t *testing.T) {
|
||||
bluepill(c1)
|
||||
bluepill(c2)
|
||||
}
|
||||
if count := c1.switches; count < 90 {
|
||||
t.Errorf("wrong vCPU#1 switches: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
if count := c1.guestExits; count < 90 {
|
||||
t.Errorf("wrong vCPU#1 exits: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
}
|
||||
if count := c2.switches; count < 90 {
|
||||
t.Errorf("wrong vCPU#2 switches: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
if count := c2.guestExits; count < 90 {
|
||||
t.Errorf("wrong vCPU#2 exits: vCPU1=%+v,vCPU2=%+v", c1, c2)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -103,8 +103,11 @@ type vCPU struct {
|
||||
// tid is the last set tid.
|
||||
tid uint64
|
||||
|
||||
// switches is a count of world switches (informational only).
|
||||
switches uint32
|
||||
// userExits is the count of user exits.
|
||||
userExits uint64
|
||||
|
||||
// guestExits is the count of guest to host world switches.
|
||||
guestExits uint64
|
||||
|
||||
// faults is a count of world faults (informational only).
|
||||
faults uint32
|
||||
@@ -127,6 +130,7 @@ type vCPU struct {
|
||||
// vCPUArchState is the architecture-specific state.
|
||||
vCPUArchState
|
||||
|
||||
// dieState holds state related to vCPU death.
|
||||
dieState dieState
|
||||
}
|
||||
|
||||
@@ -540,6 +544,8 @@ var pid = syscall.Getpid()
|
||||
//
|
||||
// This effectively unwinds the state machine.
|
||||
func (c *vCPU) bounce(forceGuestExit bool) {
|
||||
origGuestExits := atomic.LoadUint64(&c.guestExits)
|
||||
origUserExits := atomic.LoadUint64(&c.userExits)
|
||||
for {
|
||||
switch state := atomic.LoadUint32(&c.state); state {
|
||||
case vCPUReady, vCPUWaiter:
|
||||
@@ -595,6 +601,14 @@ func (c *vCPU) bounce(forceGuestExit bool) {
|
||||
// Should not happen: the above is exhaustive.
|
||||
panic("invalid state")
|
||||
}
|
||||
|
||||
// Check if we've missed the state transition, but
|
||||
// we can safely return at this point in time.
|
||||
newGuestExits := atomic.LoadUint64(&c.guestExits)
|
||||
newUserExits := atomic.LoadUint64(&c.userExits)
|
||||
if newUserExits != origUserExits && (!forceGuestExit || newGuestExits != origGuestExits) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user