Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 7 additions & 14 deletions libcontainer/apparmor/apparmor_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,14 @@ import (
"github.com/opencontainers/runc/internal/pathrs"
)

var (
appArmorEnabled bool
checkAppArmor sync.Once
)

// isEnabled returns true if apparmor is enabled for the host.
func isEnabled() bool {
checkAppArmor.Do(func() {
if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil {
buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled")
appArmorEnabled = err == nil && len(buf) > 1 && buf[0] == 'Y'
}
})
return appArmorEnabled
}
var isEnabled = sync.OnceValue(func() bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one pattern I also used in some places is to have a regular function (instead of the rather ad-hoc style for these), and then wrap it in a sync.OnceValue - sometimes useful for testing (where you may not want the "once") and to keep it as a "function", not a variable that happens to be a function, i.e.;

var something = sync.OnceValue(doSomething)

func doSomething() bool {
    return true
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if the bare (unwrapped) functionality is needed for e.g. test or a benchmark.

Not the case here so I'd rather keep this as is.

if _, err := os.Stat("/sys/kernel/security/apparmor"); err != nil {
return false
}
buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled")
return err == nil && len(buf) > 0 && buf[0] == 'Y'
})

func setProcAttr(attr, value string) error {
attr = pathrs.LexicallyCleanPath(attr)
Expand Down
41 changes: 6 additions & 35 deletions libcontainer/configs/validate/intelrdt.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,12 @@
package validate

import (
"sync"

"github.com/opencontainers/runc/libcontainer/intelrdt"
)

// Cache the result of intelrdt IsEnabled functions to avoid repeated sysfs
// access and enable mocking for unit tests.
type intelRdtStatus struct {
sync.Once
rdtEnabled bool
catEnabled bool
mbaEnabled bool
}

var intelRdt = &intelRdtStatus{}

func (i *intelRdtStatus) init() {
i.Do(func() {
i.rdtEnabled = intelrdt.IsEnabled()
i.catEnabled = intelrdt.IsCATEnabled()
i.mbaEnabled = intelrdt.IsMBAEnabled()
})
}

func (i *intelRdtStatus) isEnabled() bool {
i.init()
return i.rdtEnabled
}

func (i *intelRdtStatus) isCATEnabled() bool {
i.init()
return i.catEnabled
}

func (i *intelRdtStatus) isMBAEnabled() bool {
i.init()
return i.mbaEnabled
}
// Allow mocking for TestValidateIntelRdt.
var (
intelRdtIsEnabled = intelrdt.IsEnabled
intelRdtIsCATEnabled = intelrdt.IsCATEnabled
intelRdtIsMBAEnabled = intelrdt.IsMBAEnabled
)
16 changes: 10 additions & 6 deletions libcontainer/configs/validate/intelrdt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ import (
"testing"

"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/intelrdt"
)

func TestValidateIntelRdt(t *testing.T) {
// Call init to trigger the sync.Once and enable overriding the rdt status
intelRdt.init()

testCases := []struct {
name string
rdtEnabled bool
Expand Down Expand Up @@ -90,11 +88,17 @@ func TestValidateIntelRdt(t *testing.T) {
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
intelRdt.rdtEnabled = tc.rdtEnabled
intelRdt.catEnabled = tc.catEnabled
intelRdt.mbaEnabled = tc.mbaEnabled
t.Cleanup(func() {
intelRdtIsEnabled = intelrdt.IsEnabled
intelRdtIsCATEnabled = intelrdt.IsCATEnabled
intelRdtIsMBAEnabled = intelrdt.IsMBAEnabled
})
intelRdtIsEnabled = func() bool { return tc.rdtEnabled }
intelRdtIsCATEnabled = func() bool { return tc.catEnabled }
intelRdtIsMBAEnabled = func() bool { return tc.mbaEnabled }

config := &configs.Config{
Rootfs: "/var",
Expand Down
30 changes: 15 additions & 15 deletions libcontainer/configs/validate/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,22 +306,22 @@ func sysctl(config *configs.Config) error {
}

func intelrdtCheck(config *configs.Config) error {
if config.IntelRdt != nil {
if !intelRdt.isEnabled() {
return fmt.Errorf("intelRdt is specified in config, but Intel RDT is not enabled")
}

switch clos := config.IntelRdt.ClosID; {
case clos == ".", clos == "..", len(clos) > 1 && strings.Contains(clos, "/"):
return fmt.Errorf("invalid intelRdt.ClosID %q", clos)
}
if config.IntelRdt == nil {
return nil
}
if !intelRdtIsEnabled() {
return fmt.Errorf("intelRdt is specified in config, but Intel RDT is not enabled")
}

if !intelRdt.isCATEnabled() && config.IntelRdt.L3CacheSchema != "" {
return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled")
}
if !intelRdt.isMBAEnabled() && config.IntelRdt.MemBwSchema != "" {
return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled")
}
switch clos := config.IntelRdt.ClosID; {
case clos == ".", clos == "..", len(clos) > 1 && strings.Contains(clos, "/"):
return fmt.Errorf("invalid intelRdt.ClosID %q", clos)
}
if !intelRdtIsCATEnabled() && config.IntelRdt.L3CacheSchema != "" {
return errors.New("intelRdt.l3CacheSchema is specified in config, but Intel RDT/CAT is not enabled")
}
if !intelRdtIsMBAEnabled() && config.IntelRdt.MemBwSchema != "" {
return errors.New("intelRdt.memBwSchema is specified in config, but Intel RDT/MBA is not enabled")
}

return nil
Expand Down
128 changes: 54 additions & 74 deletions libcontainer/intelrdt/intelrdt.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func NewManager(config *configs.Config, id, path string) *Manager {
return nil
}

rootPath, err := Root()
rootPath, err := root()
if err != nil {
return nil
}
Expand All @@ -180,12 +180,6 @@ func NewManager(config *configs.Config, id, path string) *Manager {
}
}

return newManager(config, id, path)
}

// newManager is the same as NewManager, except it does not check if the feature
// is actually available. Used by unit tests that mock intelrdt paths.
func newManager(config *configs.Config, id, path string) *Manager {
return &Manager{
config: config,
id: id,
Expand All @@ -203,47 +197,42 @@ var (
// The flag to indicate if Intel RDT/MBA is enabled
mbaEnabled bool

// For Intel RDT initialization
initOnce sync.Once

errNotFound = errors.New("Intel RDT not available")
)

// Check if Intel RDT sub-features are enabled in featuresInit()
func featuresInit() {
initOnce.Do(func() {
// 1. Check if Intel RDT "resource control" filesystem is available.
// The user guarantees to mount the filesystem.
root, err := Root()
if err != nil {
return
}
var featuresInit = sync.OnceFunc(func() {
// 1. Check if Intel RDT "resource control" filesystem is available.
// The user guarantees to mount the filesystem.
root, err := root()
if err != nil {
return
}

// 2. Check if Intel RDT sub-features are available in "resource
// control" filesystem. Intel RDT sub-features can be
// selectively disabled or enabled by kernel command line
// (e.g., rdt=!l3cat,mba) in 4.14 and newer kernel
if _, err := os.Stat(filepath.Join(root, "info", "L3")); err == nil {
catEnabled = true
}
if _, err := os.Stat(filepath.Join(root, "info", "MB")); err == nil {
mbaEnabled = true
}
if _, err := os.Stat(filepath.Join(root, "info", "L3_MON")); err != nil {
return
}
enabledMonFeatures, err = getMonFeatures(root)
if err != nil {
return
}
if enabledMonFeatures.mbmTotalBytes || enabledMonFeatures.mbmLocalBytes {
mbmEnabled = true
}
if enabledMonFeatures.llcOccupancy {
cmtEnabled = true
}
})
}
// 2. Check if Intel RDT sub-features are available in "resource
// control" filesystem. Intel RDT sub-features can be
// selectively disabled or enabled by kernel command line
// (e.g., rdt=!l3cat,mba) in 4.14 and newer kernel
if _, err := os.Stat(filepath.Join(root, "info", "L3")); err == nil {
catEnabled = true
}
if _, err := os.Stat(filepath.Join(root, "info", "MB")); err == nil {
mbaEnabled = true
}
if _, err := os.Stat(filepath.Join(root, "info", "L3_MON")); err != nil {
return
}
enabledMonFeatures, err = getMonFeatures(root)
if err != nil {
return
}
if enabledMonFeatures.mbmTotalBytes || enabledMonFeatures.mbmLocalBytes {
mbmEnabled = true
}
if enabledMonFeatures.llcOccupancy {
cmtEnabled = true
}
})

// findIntelRdtMountpointDir returns the mount point of the Intel RDT "resource control" filesystem.
func findIntelRdtMountpointDir() (string, error) {
Expand All @@ -264,43 +253,34 @@ func findIntelRdtMountpointDir() (string, error) {
return mi[0].Mountpoint, nil
}

// For Root() use only.
var (
intelRdtRoot string
intelRdtRootErr error
rootOnce sync.Once
)

// The kernel creates this (empty) directory if resctrl is supported by the
// hardware and kernel. The user is responsible for mounting the resctrl
// filesystem, and they could mount it somewhere else if they wanted to.
const defaultResctrlMountpoint = "/sys/fs/resctrl"

// Root returns the Intel RDT "resource control" filesystem mount point.
func Root() (string, error) {
rootOnce.Do(func() {
// Does this system support resctrl?
var statfs unix.Statfs_t
if err := unix.Statfs(defaultResctrlMountpoint, &statfs); err != nil {
if errors.Is(err, unix.ENOENT) {
err = errNotFound
}
intelRdtRootErr = err
return
}
return root()
}

// Has the resctrl fs been mounted to the default mount point?
if statfs.Type == unix.RDTGROUP_SUPER_MAGIC {
intelRdtRoot = defaultResctrlMountpoint
return
var root = sync.OnceValues(func() (string, error) {
// Does this system support resctrl?
var statfs unix.Statfs_t
if err := unix.Statfs(defaultResctrlMountpoint, &statfs); err != nil {
if errors.Is(err, unix.ENOENT) {
err = errNotFound
}
return "", err
}

// The resctrl fs could have been mounted somewhere nonstandard.
intelRdtRoot, intelRdtRootErr = findIntelRdtMountpointDir()
})
// Has the resctrl fs been mounted to the default mount point?
if statfs.Type == unix.RDTGROUP_SUPER_MAGIC {
return defaultResctrlMountpoint, nil
}

return intelRdtRoot, intelRdtRootErr
}
// The resctrl fs could have been mounted somewhere nonstandard.
return findIntelRdtMountpointDir()
})

// Gets a single uint64 value from the specified file.
func getIntelRdtParamUint(path, file string) (uint64, error) {
Expand Down Expand Up @@ -331,7 +311,7 @@ func getIntelRdtParamString(path, file string) (string, error) {
func getL3CacheInfo() (*L3CacheInfo, error) {
l3CacheInfo := &L3CacheInfo{}

rootPath, err := Root()
rootPath, err := root()
if err != nil {
return l3CacheInfo, err
}
Expand Down Expand Up @@ -361,7 +341,7 @@ func getL3CacheInfo() (*L3CacheInfo, error) {
func getMemBwInfo() (*MemBwInfo, error) {
memBwInfo := &MemBwInfo{}

rootPath, err := Root()
rootPath, err := root()
if err != nil {
return memBwInfo, err
}
Expand Down Expand Up @@ -394,7 +374,7 @@ func getMemBwInfo() (*MemBwInfo, error) {

// Get diagnostics for last filesystem operation error from file info/last_cmd_status
func getLastCmdStatus() (string, error) {
rootPath, err := Root()
rootPath, err := root()
if err != nil {
return "", err
}
Expand Down Expand Up @@ -425,7 +405,7 @@ func WriteIntelRdtTasks(dir string, pid int) error {

// IsEnabled checks if Intel RDT is enabled.
func IsEnabled() bool {
fsroot, err := Root()
fsroot, err := root()
return err == nil && fsroot != ""
}

Expand Down Expand Up @@ -544,7 +524,7 @@ func (m *Manager) GetStats() (*Stats, error) {
defer m.mu.Unlock()
stats := newStats()

rootPath, err := Root()
rootPath, err := root()
if err != nil {
return nil, err
}
Expand Down
Loading
Loading