Merge pull request #5758 from zhlhahaha:2125

PiperOrigin-RevId: 372608247
This commit is contained in:
gVisor bot
2021-05-07 12:39:14 -07:00
5 changed files with 160 additions and 42 deletions
+6 -9
View File
@@ -75,6 +75,9 @@ type machine struct {
// nextID is the next vCPU ID.
nextID uint32
// machineArchState is the architecture-specific state.
machineArchState
}
const (
@@ -196,12 +199,7 @@ func newMachine(vm int) (*machine, error) {
m.available.L = &m.mu
// Pull the maximum vCPUs.
maxVCPUs, _, errno := unix.RawSyscall(unix.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)
}
m.getMaxVCPU()
log.Debugf("The maximum number of vCPUs is %d.", m.maxVCPUs)
m.vCPUsByTID = make(map[uint64]*vCPU)
m.vCPUsByID = make([]*vCPU, m.maxVCPUs)
@@ -427,9 +425,8 @@ func (m *machine) Get() *vCPU {
}
}
// Create a new vCPU (maybe).
if int(m.nextID) < m.maxVCPUs {
c := m.newVCPU()
// Get a new vCPU (maybe).
if c := m.getNewVCPU(); c != nil {
c.lock()
m.vCPUsByTID[tid] = c
m.mu.Unlock()
+22
View File
@@ -63,6 +63,9 @@ func (m *machine) initArchState() error {
return nil
}
type machineArchState struct {
}
type vCPUArchState struct {
// PCIDs is the set of PCIDs for this vCPU.
//
@@ -499,3 +502,22 @@ func (m *machine) mapUpperHalf(pageTable *pagetables.PageTables) {
physical)
}
}
// getMaxVCPU get max vCPU number
func (m *machine) getMaxVCPU() {
maxVCPUs, _, errno := unix.RawSyscall(unix.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)
}
}
// getNewVCPU create a new vCPU (maybe)
func (m *machine) getNewVCPU() *vCPU {
if int(m.nextID) < m.maxVCPUs {
c := m.newVCPU()
return c
}
return nil
}
+36
View File
@@ -17,6 +17,10 @@
package kvm
import (
"runtime"
"sync/atomic"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
@@ -25,6 +29,11 @@ import (
"gvisor.dev/gvisor/pkg/sentry/platform"
)
type machineArchState struct {
//initialvCPUs is the machine vCPUs which has initialized but not used
initialvCPUs map[int]*vCPU
}
type vCPUArchState struct {
// PCIDs is the set of PCIDs for this vCPU.
//
@@ -182,3 +191,30 @@ func (c *vCPU) fault(signal int32, info *arch.SignalInfo) (hostarch.AccessType,
return accessType, platform.ErrContextSignal
}
// getMaxVCPU get max vCPU number
func (m *machine) getMaxVCPU() {
rmaxVCPUs := runtime.NumCPU()
smaxVCPUs, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(m.fd), _KVM_CHECK_EXTENSION, _KVM_CAP_MAX_VCPUS)
// compare the max vcpu number from runtime and syscall, use smaller one.
if errno != 0 {
m.maxVCPUs = rmaxVCPUs
} else {
if rmaxVCPUs < int(smaxVCPUs) {
m.maxVCPUs = rmaxVCPUs
} else {
m.maxVCPUs = int(smaxVCPUs)
}
}
}
// getNewVCPU() scan for an available vCPU from initialvCPUs
func (m *machine) getNewVCPU() *vCPU {
for CID, c := range m.initialvCPUs {
if atomic.CompareAndSwapUint32(&c.state, vCPUReady, vCPUUser) {
delete(m.initialvCPUs, CID)
return c
}
}
return nil
}
@@ -29,6 +29,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/arch/fpu"
"gvisor.dev/gvisor/pkg/sentry/platform"
ktime "gvisor.dev/gvisor/pkg/sentry/time"
)
type kvmVcpuInit struct {
@@ -47,6 +48,19 @@ func (m *machine) initArchState() error {
uintptr(unsafe.Pointer(&vcpuInit))); errno != 0 {
panic(fmt.Sprintf("error setting KVM_ARM_PREFERRED_TARGET failed: %v", errno))
}
// Initialize all vCPUs on ARM64, while this does not happen on x86_64.
// The reason for the difference is that ARM64 and x86_64 have different KVM timer mechanisms.
// If we create vCPU dynamically on ARM64, the timer for vCPU would mess up for a short time.
// For more detail, please refer to https://github.com/google/gvisor/issues/5739
m.initialvCPUs = make(map[int]*vCPU)
m.mu.Lock()
for int(m.nextID) < m.maxVCPUs-1 {
c := m.newVCPU()
c.state = 0
m.initialvCPUs[c.id] = c
}
m.mu.Unlock()
return nil
}
@@ -174,9 +188,58 @@ func (c *vCPU) setTSC(value uint64) error {
return nil
}
// getTSC gets the counter Physical Counter minus Virtual Offset.
func (c *vCPU) getTSC() error {
var (
reg kvmOneReg
data uint64
)
reg.addr = uint64(reflect.ValueOf(&data).Pointer())
reg.id = _KVM_ARM64_REGS_TIMER_CNT
if err := c.getOneRegister(&reg); err != nil {
return err
}
return nil
}
// setSystemTime sets the vCPU to the system time.
func (c *vCPU) setSystemTime() error {
return c.setSystemTimeLegacy()
const minIterations = 10
minimum := uint64(0)
for iter := 0; ; iter++ {
// Use get the TSC to an estimate of where it will be
// on the host during a "fast" system call iteration.
// replace getTSC to another setOneRegister syscall can get more accurate value?
start := uint64(ktime.Rdtsc())
if err := c.getTSC(); err != nil {
return err
}
// See if this is our new minimum call time. Note that this
// serves two functions: one, we make sure that we are
// accurately predicting the offset we need to set. Second, we
// don't want to do the final set on a slow call, which could
// produce a really bad result.
end := uint64(ktime.Rdtsc())
if end < start {
continue // Totally bogus: unstable TSC?
}
current := end - start
if current < minimum || iter == 0 {
minimum = current // Set our new minimum.
}
// Is this past minIterations and within ~10% of minimum?
upperThreshold := (((minimum << 3) + minimum) >> 3)
if iter >= minIterations && (current <= upperThreshold || minimum < 50) {
// Try to set the TSC
if err := c.setTSC(end + (minimum / 2)); err != nil {
return err
}
return nil
}
}
}
//go:nosplit
@@ -203,7 +266,7 @@ func (c *vCPU) getOneRegister(reg *kvmOneReg) error {
uintptr(c.fd),
_KVM_GET_ONE_REG,
uintptr(unsafe.Pointer(reg))); errno != 0 {
return fmt.Errorf("error setting one register: %v", errno)
return fmt.Errorf("error getting one register: %v", errno)
}
return nil
}
+31 -31
View File
@@ -230,6 +230,33 @@ func New(args Args) (*Loader, error) {
vfs2.Override()
}
// Make host FDs stable between invocations. Host FDs must map to the exact
// same number when the sandbox is restored. Otherwise the wrong FD will be
// used.
info := containerInfo{}
newfd := startingStdioFD
for _, stdioFD := range args.StdioFDs {
// Check that newfd is unused to avoid clobbering over it.
if _, err := unix.FcntlInt(uintptr(newfd), unix.F_GETFD, 0); !errors.Is(err, unix.EBADF) {
if err != nil {
return nil, fmt.Errorf("error checking for FD (%d) conflict: %w", newfd, err)
}
return nil, fmt.Errorf("unable to remap stdios, FD %d is already in use", newfd)
}
err := unix.Dup3(stdioFD, newfd, unix.O_CLOEXEC)
if err != nil {
return nil, fmt.Errorf("dup3 of stdios failed: %w", err)
}
info.stdioFDs = append(info.stdioFDs, fd.New(newfd))
_ = unix.Close(stdioFD)
newfd++
}
for _, goferFD := range args.GoferFDs {
info.goferFDs = append(info.goferFDs, fd.New(goferFD))
}
// Create kernel and platform.
p, err := createPlatform(args.Conf, args.Device)
if err != nil {
@@ -349,6 +376,7 @@ func New(args Args) (*Loader, error) {
if err != nil {
return nil, fmt.Errorf("creating init process for root container: %v", err)
}
info.procArgs = procArgs
if err := initCompatLogs(args.UserLogFD); err != nil {
return nil, fmt.Errorf("initializing compat logs: %v", err)
@@ -359,6 +387,9 @@ func New(args Args) (*Loader, error) {
return nil, fmt.Errorf("creating pod mount hints: %v", err)
}
info.conf = args.Conf
info.spec = args.Spec
if kernel.VFS2Enabled {
// Set up host mount that will be used for imported fds.
hostFilesystem, err := hostvfs2.NewFilesystem(k.VFS())
@@ -373,37 +404,6 @@ func New(args Args) (*Loader, error) {
k.SetHostMount(hostMount)
}
info := containerInfo{
conf: args.Conf,
spec: args.Spec,
procArgs: procArgs,
}
// Make host FDs stable between invocations. Host FDs must map to the exact
// same number when the sandbox is restored. Otherwise the wrong FD will be
// used.
newfd := startingStdioFD
for _, stdioFD := range args.StdioFDs {
// Check that newfd is unused to avoid clobbering over it.
if _, err := unix.FcntlInt(uintptr(newfd), unix.F_GETFD, 0); !errors.Is(err, unix.EBADF) {
if err != nil {
return nil, fmt.Errorf("error checking for FD (%d) conflict: %w", newfd, err)
}
return nil, fmt.Errorf("unable to remap stdios, FD %d is already in use", newfd)
}
err := unix.Dup3(stdioFD, newfd, unix.O_CLOEXEC)
if err != nil {
return nil, fmt.Errorf("dup3 of stdios failed: %w", err)
}
info.stdioFDs = append(info.stdioFDs, fd.New(newfd))
_ = unix.Close(stdioFD)
newfd++
}
for _, goferFD := range args.GoferFDs {
info.goferFDs = append(info.goferFDs, fd.New(goferFD))
}
eid := execID{cid: args.ID}
l := &Loader{
k: k,