Dramatically improve handling of KVM vCPU pool.

Especially in situations with small numbers of vCPUs, the existing
system resulted in excessive thrashing. Now, execution contexts
co-ordinate as smoothly as they can to share a small number of cores.

PiperOrigin-RevId: 197483323
Change-Id: I0afc0c5363ea9386994355baf3904bf5fe08c56c
This commit is contained in:
Adin Scannell 2018-05-21 16:48:41 -07:00 committed by Shentubot
parent d4c81b7a21
commit 61b0b19497
3 changed files with 48 additions and 33 deletions

View File

@ -41,10 +41,7 @@ func (c *context) Switch(as platform.AddressSpace, ac arch.Context, _ int32) (*a
fp := (*byte)(ac.FloatingPointData())
// Grab a vCPU.
cpu, err := c.machine.Get()
if err != nil {
return nil, usermem.NoAccess, err
}
cpu := c.machine.Get()
// Enable interrupts (i.e. calls to vCPU.Notify).
if !c.interrupt.Enable(cpu) {

View File

@ -59,10 +59,7 @@ func kvmTest(t testHarness, setup func(*KVM), fn func(*vCPU) bool) {
}
}()
for {
c, err = k.machine.Get()
if err != nil {
t.Fatalf("error getting vCPU: %v", err)
}
c = k.machine.Get()
if !fn(c) {
break
}

View File

@ -48,6 +48,9 @@ type machine struct {
// mu protects vCPUs.
mu sync.Mutex
// available is notified when vCPUs are available.
available sync.Cond
// vCPUs are the machine vCPUs.
//
// This is eventually keyed by system TID, but is initially indexed by
@ -118,6 +121,7 @@ func newMachine(vm int, vCPUs int) (*machine, error) {
fd: vm,
vCPUs: make(map[uint64]*vCPU),
}
m.available.L = &m.mu
if vCPUs > _KVM_NR_VCPUS {
// Hard cap at KVM's limit.
vCPUs = _KVM_NR_VCPUS
@ -284,25 +288,21 @@ func (m *machine) Destroy() {
}
// Get gets an available vCPU.
func (m *machine) Get() (*vCPU, error) {
func (m *machine) Get() *vCPU {
runtime.LockOSThread()
tid := procid.Current()
m.mu.Lock()
for {
// Check for an exact match.
if c := m.vCPUs[tid]; c != nil {
c.lock()
m.mu.Unlock()
return c, nil
}
// Check for an exact match.
if c := m.vCPUs[tid]; c != nil {
c.lock()
m.mu.Unlock()
return c
}
for {
// Scan for an available vCPU.
for origTID, c := range m.vCPUs {
// We can only steal a vCPU that is the vCPUReady
// state. That is, it must not be heading to user mode
// with some other thread, have a waiter registered, or
// be in guest mode already.
if atomic.CompareAndSwapUint32(&c.state, vCPUReady, vCPUUser) {
delete(m.vCPUs, origTID)
m.vCPUs[tid] = c
@ -313,24 +313,44 @@ func (m *machine) Get() (*vCPU, error) {
// may be stale.
c.loadSegments()
atomic.StoreUint64(&c.tid, tid)
return c, nil
return c
}
}
// Everything is already in guest mode.
//
// We hold the pool lock here, so we should be able to kick
// something out of kernel mode and have it bounce into host
// mode when it tries to grab the vCPU again.
for _, c := range m.vCPUs {
c.BounceToHost()
// Scan for something not in user mode.
for origTID, c := range m.vCPUs {
if !atomic.CompareAndSwapUint32(&c.state, vCPUGuest, vCPUGuest|vCPUWaiter) {
continue
}
// The vCPU is not be able to transition to
// vCPUGuest|vCPUUser or to vCPUUser because that
// transition requires holding the machine mutex, as we
// do now. There is no path to register a waiter on
// just the vCPUReady state.
for {
c.waitUntilNot(vCPUGuest | vCPUWaiter)
if atomic.CompareAndSwapUint32(&c.state, vCPUReady, vCPUUser) {
break
}
}
// Steal the vCPU.
delete(m.vCPUs, origTID)
m.vCPUs[tid] = c
m.mu.Unlock()
// See above.
c.loadSegments()
atomic.StoreUint64(&c.tid, tid)
return c
}
// Give other threads an opportunity to run. We don't yield the
// pool lock above, so if they try to regrab the lock we will
// serialize at this point. This is extreme, but we don't
// expect to exhaust all vCPUs frequently.
yield()
// Everything is executing in user mode. Wait until something
// is available. Note that signaling the condition variable
// will have the extra effect of kicking the vCPUs out of guest
// mode if that's where they were.
m.available.Wait()
}
}
@ -338,6 +358,7 @@ func (m *machine) Get() (*vCPU, error) {
func (m *machine) Put(c *vCPU) {
c.unlock()
runtime.UnlockOSThread()
m.available.Signal()
}
// lock marks the vCPU as in user mode.