platform/kvm: Get max vcpu number dynamically by ioctl

The old kernel version, such as 4.4, only support 255 vcpus.
While gvisor is ran on these kernels, it could panic because the
vcpu id and vcpu number beyond max_vcpus.
Use ioctl(vmfd, _KVM_CHECK_EXTENSION, _KVM_CAP_MAX_VCPUS) to get max
vcpus number dynamically.

Change-Id: I50dd859a11b1c2cea854a8e27d4bf11a411aa45c
PiperOrigin-RevId: 212929704
This commit is contained in:
Chenggang
2018-09-13 21:47:11 -07:00
committed by Shentubot
parent 29a7271f5d
commit faa34a0738
4 changed files with 47 additions and 21 deletions
+12 -17
View File
@@ -26,31 +26,26 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
)
type vCPUBitArray [(_KVM_NR_VCPUS + 63) / 64]uint64
// dirtySet tracks vCPUs for invalidation.
type dirtySet struct {
vCPUs vCPUBitArray
vCPUs []uint64
}
// forEach iterates over all CPUs in the dirty set.
func (ds *dirtySet) forEach(m *machine, fn func(c *vCPU)) {
var localSet vCPUBitArray
for index := 0; index < len(ds.vCPUs); index++ {
// Clear the dirty set, copy to the local one.
localSet[index] = atomic.SwapUint64(&ds.vCPUs[index], 0)
}
m.mu.RLock()
defer m.mu.RUnlock()
for _, c := range m.vCPUs {
index := uint64(c.id) / 64
bit := uint64(1) << uint(c.id%64)
// Call the function if it was set.
if localSet[index]&bit != 0 {
fn(c)
for index := range ds.vCPUs {
mask := atomic.SwapUint64(&ds.vCPUs[index], 0)
if mask != 0 {
for bit := 0; bit < 64; bit++ {
if mask&(1<<uint64(bit)) == 0 {
continue
}
id := 64*index + bit
fn(m.vCPUsByID[id])
}
}
}
}
@@ -92,7 +87,7 @@ type addressSpace struct {
pageTables *pagetables.PageTables
// dirtySet is the set of dirty vCPUs.
dirtySet dirtySet
dirtySet *dirtySet
// files contains files mapped in the host address space.
//
+1
View File
@@ -143,6 +143,7 @@ func (k *KVM) NewAddressSpace(_ interface{}) (platform.AddressSpace, <-chan stru
filemem: k.FileMem,
machine: k.machine,
pageTables: pageTables,
dirtySet: k.machine.newDirtySet(),
}, nil, nil
}
+7 -1
View File
@@ -25,6 +25,7 @@ const (
_KVM_SET_TSS_ADDR = 0xae47
_KVM_RUN = 0xae80
_KVM_NMI = 0xae9a
_KVM_CHECK_EXTENSION = 0xae03
_KVM_INTERRUPT = 0x4004ae86
_KVM_SET_MSRS = 0x4008ae89
_KVM_SET_USER_MEMORY_REGION = 0x4020ae46
@@ -49,9 +50,14 @@ const (
_KVM_EXIT_INTERNAL_ERROR = 0x11
)
// KVM capability options.
const (
_KVM_CAP_MAX_VCPUS = 0x42
)
// KVM limits.
const (
_KVM_NR_VCPUS = 0x100
_KVM_NR_VCPUS = 0xff
_KVM_NR_INTERRUPTS = 0x100
_KVM_NR_CPUID_ENTRIES = 0x100
)
+27 -3
View File
@@ -22,6 +22,7 @@ import (
"syscall"
"gvisor.googlesource.com/gvisor/pkg/atomicbitops"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform/procid"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform/ring0"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform/ring0/pagetables"
@@ -55,6 +56,12 @@ type machine struct {
//
// These are populated dynamically.
vCPUs map[uint64]*vCPU
// vCPUsByID are the machine vCPUs, can be indexed by the vCPU's ID.
vCPUsByID map[int]*vCPU
// maxVCPUs is the maximum number of vCPUs supported by the machine.
maxVCPUs int
}
const (
@@ -135,6 +142,7 @@ func (m *machine) newVCPU() *vCPU {
c.CPU.Init(&m.kernel)
c.CPU.KernelSyscall = bluepillSyscall
c.CPU.KernelException = bluepillException
m.vCPUsByID[c.id] = c
// Ensure the signal mask is correct.
if err := c.setSignalMask(); err != nil {
@@ -160,14 +168,23 @@ func (m *machine) newVCPU() *vCPU {
func newMachine(vm int) (*machine, error) {
// Create the machine.
m := &machine{
fd: vm,
vCPUs: make(map[uint64]*vCPU),
fd: vm,
vCPUs: make(map[uint64]*vCPU),
vCPUsByID: make(map[int]*vCPU),
}
m.available.L = &m.mu
m.kernel.Init(ring0.KernelOpts{
PageTables: pagetables.New(newAllocator()),
})
maxVCPUs, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(m.fd), _KVM_CHECK_EXTENSION, _KVM_CAP_MAX_VCPUS)
if errno != 0 {
m.maxVCPUs = _KVM_NR_VCPUS
} else {
m.maxVCPUs = int(maxVCPUs)
}
log.Debugf("The maximum number of vCPUs is %d.", m.maxVCPUs)
// Apply the physical mappings. Note that these mappings may point to
// guest physical addresses that are not actually available. These
// physical pages are mapped on demand, see kernel_unsafe.go.
@@ -315,7 +332,7 @@ func (m *machine) Get() *vCPU {
}
// Create a new vCPU (maybe).
if len(m.vCPUs) < _KVM_NR_VCPUS {
if len(m.vCPUs) < m.maxVCPUs {
c := m.newVCPU()
c.lock()
m.vCPUs[tid] = c
@@ -365,6 +382,13 @@ func (m *machine) Put(c *vCPU) {
m.available.Signal()
}
// newDirtySet returns a new dirty set.
func (m *machine) newDirtySet() *dirtySet {
return &dirtySet{
vCPUs: make([]uint64, (m.maxVCPUs+63)/64, (m.maxVCPUs+63)/64),
}
}
// lock marks the vCPU as in user mode.
//
// This should only be called directly when known to be safe, i.e. when