cpuid: Initialize hostFeatureSet only when it's needed.

This moves the initialization of `cpuid.hostFeatureSet` out of package-level
`init` and instead moves it to an explicit `cpuid.Initialize()` function.

On AMD64, this saves about 512KiB of heap memory that would otherwise always
be live.

PiperOrigin-RevId: 514896323
This commit is contained in:
Etienne Perot
2023-03-07 18:27:49 -08:00
committed by gVisor bot
parent 38750cdedc
commit 0620c3b638
16 changed files with 74 additions and 10 deletions
+1
View File
@@ -21,6 +21,7 @@ go_library(
visibility = ["//:sandbox"],
deps = [
"//pkg/log",
"//pkg/sync",
],
)
+9
View File
@@ -35,6 +35,7 @@ import (
"strings"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sync"
)
// contextID is the package for context.Context.Value keys.
@@ -253,3 +254,11 @@ func initHWCap() {
hostFeatureSet.hwCap = c
}
}
var initOnce sync.Once
// Initialize initializes the global data structures used by this package.
// Must be called prior to using anything else in this package.
func Initialize() {
initOnce.Do(archInitialize)
}
+6
View File
@@ -16,6 +16,7 @@ package cpuid
import (
"io/ioutil"
"os"
"regexp"
"strings"
"testing"
@@ -67,3 +68,8 @@ func TestHostFeatureFlags(t *testing.T) {
}
}
}
func TestMain(m *testing.M) {
Initialize()
os.Exit(m.Run())
}
+5
View File
@@ -90,3 +90,8 @@ func TestReadingSelfProcAuxv(t *testing.T) {
t.Errorf("got %v, expected nil", err)
}
}
func TestMain(m *testing.M) {
Initialize()
os.Exit(m.Run())
}
+2 -1
View File
@@ -215,7 +215,8 @@ func readMaxCPUFreq() {
}
func init() {
// archInitialize initializes hostFeatureSet.
func archInitialize() {
hostFeatureSet = FeatureSet{
Function: &Native{},
}.Fixed()
+2 -1
View File
@@ -150,7 +150,8 @@ func initCPUInfo() {
}
}
func init() {
// archInitialize initializes hostFeatureSet.
func archInitialize() {
initCPUInfo()
initHWCap()
}
+6 -5
View File
@@ -93,9 +93,9 @@ var (
// Init sets function pointers based on architectural features.
//
// This must be called prior to using ring0. By default, it will be called by
// the init() function. However, it may be called at another time with a
// different FeatureSet.
// This must be called prior to using ring0. It may be called with the
// auto-detected feature set using InitDefault. It may also be called at
// another time with a different FeatureSet.
func Init(fs cpuid.FeatureSet) {
// Initialize all sizes.
VirtualAddressBits = uintptr(fs.VirtualAddressBits())
@@ -123,7 +123,8 @@ func Init(fs cpuid.FeatureSet) {
}
}
func init() {
// See Init, above.
// InitDefault initializes ring0 with the auto-detected host feature set.
func InitDefault() {
cpuid.Initialize()
Init(cpuid.HostFeatureSet())
}
+4
View File
@@ -76,3 +76,7 @@ func FPSIMDEnableTrap()
//
// This must be called prior to using ring0.
func Init() {}
// InitDefault calls Init with default parameters.
// On ARM, this is not much.
func InitDefault() {}
+1
View File
@@ -50,6 +50,7 @@ var (
// Boot initializes a new bare bones kernel for test.
func Boot() (*kernel.Kernel, error) {
cpuid.Initialize()
seccheck.Initialize()
platformCtr, err := platform.Lookup(*platformFlag)
+1
View File
@@ -219,6 +219,7 @@ func (c *cpuidEntries) Set(in cpuid.In, out cpuid.Out) {
// updateGlobalOnce does global initialization. It has to be called only once.
func updateGlobalOnce(fd int) error {
bitsForScaling = getBitsForScaling()
if err := updateSystemValues(int(fd)); err != nil {
return err
}
+10 -1
View File
@@ -16,12 +16,14 @@ package kvm
import (
"math/rand"
"os"
"reflect"
"testing"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/ring0/pagetables"
@@ -32,7 +34,8 @@ import (
ktime "gvisor.dev/gvisor/pkg/sentry/time"
)
var dummyFPState = fpu.NewState()
// dummyFPState is initialized in TestMain.
var dummyFPState fpu.State
type testHarness interface {
Errorf(format string, args ...any)
@@ -554,3 +557,9 @@ func BenchmarkWorldSwitchToUserRoundtrip(b *testing.B) {
b.Logf("ErrContextInterrupt occurred %d times (in %d iterations).", a, a+i)
}
}
func TestMain(m *testing.M) {
cpuid.Initialize()
dummyFPState = fpu.NewState()
os.Exit(m.Run())
}
+7 -2
View File
@@ -158,12 +158,17 @@ func (c *vCPU) initArchState() error {
}
// bitsForScaling returns the bits available for storing the fraction component
// of the TSC scaling ratio.
// It is set using getBitsForScaling when the KVM platform is initialized.
var bitsForScaling int64
// getBitsForScaling returns the bits available for storing the fraction component
// of the TSC scaling ratio. This allows us to replicate the (bad) math done by
// the kernel below in scaledTSC, and ensure we can compute an exact zero
// offset in setSystemTime.
//
// These constants correspond to kvm_tsc_scaling_ratio_frac_bits.
var bitsForScaling = func() int64 {
func getBitsForScaling() int64 {
fs := cpuid.HostFeatureSet()
if fs.Intel() {
return 48 // See vmx.c (kvm sources).
@@ -172,7 +177,7 @@ var bitsForScaling = func() int64 {
} else {
return 63 // Unknown: theoretical maximum.
}
}()
}
// scaledTSC returns the host TSC scaled by the given frequency.
//
+2
View File
@@ -137,8 +137,10 @@ go_test(
library = ":boot",
deps = [
"//pkg/control/server",
"//pkg/cpuid",
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/seccheck",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/unet",
+8
View File
@@ -26,8 +26,10 @@ import (
"github.com/syndtr/gocapability/capability"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/control/server"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/seccheck"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/unet"
@@ -496,3 +498,9 @@ func TestCreateMountNamespace(t *testing.T) {
})
}
}
func TestMain(m *testing.M) {
cpuid.Initialize()
seccheck.Initialize()
os.Exit(m.Run())
}
+2
View File
@@ -54,9 +54,11 @@ go_library(
"//pkg/atomicbitops",
"//pkg/coretag",
"//pkg/coverage",
"//pkg/cpuid",
"//pkg/log",
"//pkg/metric",
"//pkg/prometheus",
"//pkg/ring0",
"//pkg/sentry/control",
"//pkg/sentry/fsutil/chdir",
"//pkg/sentry/kernel",
+8
View File
@@ -28,8 +28,10 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/coretag"
"gvisor.dev/gvisor/pkg/cpuid"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/metric"
"gvisor.dev/gvisor/pkg/ring0"
"gvisor.dev/gvisor/pkg/sentry/fsutil/chdir"
"gvisor.dev/gvisor/pkg/sentry/platform"
"gvisor.dev/gvisor/runsc/boot"
@@ -208,6 +210,12 @@ func (b *Boot) Execute(_ context.Context, f *flag.FlagSet, args ...any) subcomma
}
}
// Initialize CPUID information.
cpuid.Initialize()
// Initialize ring0 library.
ring0.InitDefault()
if len(b.productName) == 0 {
// Do this before chroot takes effect, otherwise we can't read /sys.
if product, err := ioutil.ReadFile("/sys/devices/virtual/dmi/id/product_name"); err != nil {