mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
Merge pull request #4915 from cyphar/1.4-memory-policy
[1.4] Add memory policy and schemata support
This commit is contained in:
@@ -173,6 +173,8 @@ func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
|
|||||||
if intelrdt.IsCMTEnabled() {
|
if intelrdt.IsCMTEnabled() {
|
||||||
s.IntelRdt.CMTStats = is.CMTStats
|
s.IntelRdt.CMTStats = is.CMTStats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.IntelRdt.Schemata = is.Schemata
|
||||||
}
|
}
|
||||||
|
|
||||||
s.NetworkInterfaces = ls.Interfaces
|
s.NetworkInterfaces = ls.Interfaces
|
||||||
|
|||||||
+6
-1
@@ -56,7 +56,12 @@ var featuresCommand = cli.Command{
|
|||||||
Enabled: &t,
|
Enabled: &t,
|
||||||
},
|
},
|
||||||
IntelRdt: &features.IntelRdt{
|
IntelRdt: &features.IntelRdt{
|
||||||
Enabled: &t,
|
Enabled: &t,
|
||||||
|
Schemata: &t,
|
||||||
|
},
|
||||||
|
MemoryPolicy: &features.MemoryPolicy{
|
||||||
|
Modes: specconv.KnownMemoryPolicyModes(),
|
||||||
|
Flags: specconv.KnownMemoryPolicyFlags(),
|
||||||
},
|
},
|
||||||
MountExtensions: &features.MountExtensions{
|
MountExtensions: &features.MountExtensions{
|
||||||
IDMap: &features.IDMap{
|
IDMap: &features.IDMap{
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ require (
|
|||||||
github.com/moby/sys/userns v0.1.0
|
github.com/moby/sys/userns v0.1.0
|
||||||
github.com/mrunalp/fileutils v0.5.1
|
github.com/mrunalp/fileutils v0.5.1
|
||||||
github.com/opencontainers/cgroups v0.0.4
|
github.com/opencontainers/cgroups v0.0.4
|
||||||
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67
|
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0
|
||||||
github.com/opencontainers/selinux v1.12.0
|
github.com/opencontainers/selinux v1.12.0
|
||||||
github.com/seccomp/libseccomp-golang v0.11.1
|
github.com/seccomp/libseccomp-golang v0.11.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm
|
|||||||
github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||||
github.com/opencontainers/cgroups v0.0.4 h1:XVj8P/IHVms/j+7eh8ggdkTLAxjz84ZzuFyGoE28DR4=
|
github.com/opencontainers/cgroups v0.0.4 h1:XVj8P/IHVms/j+7eh8ggdkTLAxjz84ZzuFyGoE28DR4=
|
||||||
github.com/opencontainers/cgroups v0.0.4/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
|
github.com/opencontainers/cgroups v0.0.4/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
|
||||||
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA=
|
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0 h1:RLn0YfUWkiqPGtgUANvJrcjIkCHGRl3jcz/c557M28M=
|
||||||
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package linux
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -72,3 +73,15 @@ func Sendmsg(fd int, p, oob []byte, to unix.Sockaddr, flags int) error {
|
|||||||
})
|
})
|
||||||
return os.NewSyscallError("sendmsg", err)
|
return os.NewSyscallError("sendmsg", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMempolicy wraps set_mempolicy.
|
||||||
|
func SetMempolicy(mode uint, mask *unix.CPUSet) error {
|
||||||
|
err := retryOnEINTR(func() error {
|
||||||
|
_, _, errno := unix.Syscall(unix.SYS_SET_MEMPOLICY, uintptr(mode), uintptr(unsafe.Pointer(mask)), unsafe.Sizeof(*mask)*8)
|
||||||
|
if errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return os.NewSyscallError("set_mempolicy", err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -214,6 +214,9 @@ type Config struct {
|
|||||||
// to limit the resources (e.g., L3 cache, memory bandwidth) the container has available
|
// to limit the resources (e.g., L3 cache, memory bandwidth) the container has available
|
||||||
IntelRdt *IntelRdt `json:"intel_rdt,omitempty"`
|
IntelRdt *IntelRdt `json:"intel_rdt,omitempty"`
|
||||||
|
|
||||||
|
// MemoryPolicy specifies NUMA memory policy for the container.
|
||||||
|
MemoryPolicy *LinuxMemoryPolicy `json:"memory_policy,omitempty"`
|
||||||
|
|
||||||
// RootlessEUID is set when the runc was launched with non-zero EUID.
|
// RootlessEUID is set when the runc was launched with non-zero EUID.
|
||||||
// Note that RootlessEUID is set to false when launched with EUID=0 in userns.
|
// Note that RootlessEUID is set to false when launched with EUID=0 in userns.
|
||||||
// When RootlessEUID is set, runc creates a new userns for the container.
|
// When RootlessEUID is set, runc creates a new userns for the container.
|
||||||
@@ -305,7 +308,8 @@ type CPUAffinity struct {
|
|||||||
Initial, Final *unix.CPUSet
|
Initial, Final *unix.CPUSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func toCPUSet(str string) (*unix.CPUSet, error) {
|
// ToCPUSet parses a string in list format into a unix.CPUSet, e.g. "0-3,5,7-9".
|
||||||
|
func ToCPUSet(str string) (*unix.CPUSet, error) {
|
||||||
if str == "" {
|
if str == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -356,7 +360,7 @@ func toCPUSet(str string) (*unix.CPUSet, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.Count() == 0 {
|
if s.Count() == 0 {
|
||||||
return nil, fmt.Errorf("no CPUs found in %q", str)
|
return nil, fmt.Errorf("no members found in set %q", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
@@ -367,11 +371,11 @@ func ConvertCPUAffinity(sa *specs.CPUAffinity) (*CPUAffinity, error) {
|
|||||||
if sa == nil {
|
if sa == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
initial, err := toCPUSet(sa.Initial)
|
initial, err := ToCPUSet(sa.Initial)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad CPUAffinity.Initial: %w", err)
|
return nil, fmt.Errorf("bad CPUAffinity.Initial: %w", err)
|
||||||
}
|
}
|
||||||
final, err := toCPUSet(sa.Final)
|
final, err := ToCPUSet(sa.Final)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("bad CPUAffinity.Final: %w", err)
|
return nil, fmt.Errorf("bad CPUAffinity.Final: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ type IntelRdt struct {
|
|||||||
// The identity for RDT Class of Service
|
// The identity for RDT Class of Service
|
||||||
ClosID string `json:"closID,omitempty"`
|
ClosID string `json:"closID,omitempty"`
|
||||||
|
|
||||||
|
// Schemata is a generic field to specify schemata file in the resctrl
|
||||||
|
// filesystem. Each element represents one line written to the schemata file.
|
||||||
|
Schemata []string `json:"schemata,omitempty"`
|
||||||
|
|
||||||
// The schema for L3 cache id and capacity bitmask (CBM)
|
// The schema for L3 cache id and capacity bitmask (CBM)
|
||||||
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
|
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
|
||||||
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
|
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package configs
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// Memory policy modes and flags as defined in /usr/include/linux/mempolicy.h
|
||||||
|
|
||||||
|
//nolint:revive,staticcheck,nolintlint // ignore ALL_CAPS errors in consts from numaif.h, will match unix.* in the future
|
||||||
|
const (
|
||||||
|
MPOL_DEFAULT = 0
|
||||||
|
MPOL_PREFERRED = 1
|
||||||
|
MPOL_BIND = 2
|
||||||
|
MPOL_INTERLEAVE = 3
|
||||||
|
MPOL_LOCAL = 4
|
||||||
|
MPOL_PREFERRED_MANY = 5
|
||||||
|
MPOL_WEIGHTED_INTERLEAVE = 6
|
||||||
|
|
||||||
|
MPOL_F_STATIC_NODES = 1 << 15
|
||||||
|
MPOL_F_RELATIVE_NODES = 1 << 14
|
||||||
|
MPOL_F_NUMA_BALANCING = 1 << 13
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinuxMemoryPolicy contains memory policy configuration.
|
||||||
|
type LinuxMemoryPolicy struct {
|
||||||
|
// Mode specifies memory policy mode without mode flags. See
|
||||||
|
// set_mempolicy() documentation for details.
|
||||||
|
Mode uint `json:"mode,omitempty"`
|
||||||
|
// Flags contains mode flags.
|
||||||
|
Flags uint `json:"flags,omitempty"`
|
||||||
|
// Nodes contains NUMA nodes to which the mode applies.
|
||||||
|
Nodes *unix.CPUSet `json:"nodes,omitempty"`
|
||||||
|
}
|
||||||
@@ -58,8 +58,8 @@ func TestToCPUSet(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.in, func(t *testing.T) {
|
t.Run(tc.in, func(t *testing.T) {
|
||||||
out, err := toCPUSet(tc.in)
|
out, err := ToCPUSet(tc.in)
|
||||||
t.Logf("toCPUSet(%q) = %v (error: %v)", tc.in, out, err)
|
t.Logf("ToCPUSet(%q) = %v (error: %v)", tc.in, out, err)
|
||||||
// Check the error.
|
// Check the error.
|
||||||
if tc.isErr {
|
if tc.isErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ func Validate(config *configs.Config) error {
|
|||||||
mountsStrict,
|
mountsStrict,
|
||||||
scheduler,
|
scheduler,
|
||||||
ioPriority,
|
ioPriority,
|
||||||
|
memoryPolicy,
|
||||||
}
|
}
|
||||||
for _, c := range checks {
|
for _, c := range checks {
|
||||||
if err := c(config); err != nil {
|
if err := c(config); err != nil {
|
||||||
@@ -482,3 +483,26 @@ func ioPriority(config *configs.Config) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func memoryPolicy(config *configs.Config) error {
|
||||||
|
mpol := config.MemoryPolicy
|
||||||
|
if mpol == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch mpol.Mode {
|
||||||
|
case configs.MPOL_DEFAULT, configs.MPOL_LOCAL:
|
||||||
|
if mpol.Nodes != nil && mpol.Nodes.Count() != 0 {
|
||||||
|
return fmt.Errorf("memory policy mode requires 0 nodes but got %d", mpol.Nodes.Count())
|
||||||
|
}
|
||||||
|
case configs.MPOL_BIND, configs.MPOL_INTERLEAVE,
|
||||||
|
configs.MPOL_PREFERRED_MANY, configs.MPOL_WEIGHTED_INTERLEAVE:
|
||||||
|
if mpol.Nodes == nil || mpol.Nodes.Count() == 0 {
|
||||||
|
return fmt.Errorf("memory policy mode requires at least one node but got 0")
|
||||||
|
}
|
||||||
|
case configs.MPOL_PREFERRED:
|
||||||
|
// Zero or more nodes are allowed by the kernel.
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid memory policy mode: %d", mpol.Mode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -659,6 +659,14 @@ func setupIOPriority(config *initConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupMemoryPolicy(config *configs.Config) error {
|
||||||
|
mpol := config.MemoryPolicy
|
||||||
|
if mpol == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return linux.SetMempolicy(mpol.Mode|mpol.Flags, config.MemoryPolicy.Nodes)
|
||||||
|
}
|
||||||
|
|
||||||
func setupPersonality(config *configs.Config) error {
|
func setupPersonality(config *configs.Config) error {
|
||||||
return system.SetLinuxPersonality(config.Personality.Domain)
|
return system.SetLinuxPersonality(config.Personality.Domain)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,16 +326,6 @@ func getIntelRdtParamString(path, file string) (string, error) {
|
|||||||
return string(bytes.TrimSpace(contents)), nil
|
return string(bytes.TrimSpace(contents)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(dir, file, data string) error {
|
|
||||||
if dir == "" {
|
|
||||||
return fmt.Errorf("no such directory for %s", file)
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(filepath.Join(dir, file), []byte(data+"\n"), 0o600); err != nil {
|
|
||||||
return newLastCmdError(fmt.Errorf("intelrdt: unable to write %v: %w", data, err))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the read-only L3 cache information
|
// Get the read-only L3 cache information
|
||||||
func getL3CacheInfo() (*L3CacheInfo, error) {
|
func getL3CacheInfo() (*L3CacheInfo, error) {
|
||||||
l3CacheInfo := &L3CacheInfo{}
|
l3CacheInfo := &L3CacheInfo{}
|
||||||
@@ -462,11 +452,11 @@ func (m *Manager) Apply(pid int) (err error) {
|
|||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" {
|
if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" && len(m.config.IntelRdt.Schemata) == 0 {
|
||||||
// Check that the CLOS exists, i.e. it has been pre-configured to
|
// Check that the CLOS exists, i.e. it has been pre-configured to
|
||||||
// conform with the runtime spec
|
// conform with the runtime spec
|
||||||
if _, err := os.Stat(path); err != nil {
|
if _, err := os.Stat(path); err != nil {
|
||||||
return fmt.Errorf("clos dir not accessible (must be pre-created when l3CacheSchema and memBwSchema are empty): %w", err)
|
return fmt.Errorf("clos dir not accessible (must be pre-created when schemata, l3CacheSchema and memBwSchema are empty): %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,6 +524,8 @@ func (m *Manager) GetStats() (*Stats, error) {
|
|||||||
}
|
}
|
||||||
schemaStrings := strings.Split(tmpStrings, "\n")
|
schemaStrings := strings.Split(tmpStrings, "\n")
|
||||||
|
|
||||||
|
stats.Schemata = schemaStrings
|
||||||
|
|
||||||
if IsCATEnabled() {
|
if IsCATEnabled() {
|
||||||
// The read-only L3 cache information
|
// The read-only L3 cache information
|
||||||
l3CacheInfo, err := getL3CacheInfo()
|
l3CacheInfo, err := getL3CacheInfo()
|
||||||
@@ -637,35 +629,24 @@ func (m *Manager) Set(container *configs.Config) error {
|
|||||||
// For example, on a two-socket machine, the schema line could be
|
// For example, on a two-socket machine, the schema line could be
|
||||||
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
|
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
|
||||||
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
|
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
|
||||||
if container.IntelRdt != nil {
|
if r := container.IntelRdt; r != nil {
|
||||||
path := m.GetPath()
|
|
||||||
l3CacheSchema := container.IntelRdt.L3CacheSchema
|
|
||||||
memBwSchema := container.IntelRdt.MemBwSchema
|
|
||||||
|
|
||||||
// TODO: verify that l3CacheSchema and/or memBwSchema match the
|
// TODO: verify that l3CacheSchema and/or memBwSchema match the
|
||||||
// existing schemata if ClosID has been specified. This is a more
|
// existing schemata if ClosID has been specified. This is a more
|
||||||
// involved than reading the file and doing plain string comparison as
|
// involved than reading the file and doing plain string comparison as
|
||||||
// the value written in does not necessarily match what gets read out
|
// the value written in does not necessarily match what gets read out
|
||||||
// (leading zeros, cache id ordering etc).
|
// (leading zeros, cache id ordering etc).
|
||||||
|
var schemata strings.Builder
|
||||||
// Write a single joint schema string to schemata file
|
for _, s := range append([]string{r.L3CacheSchema, r.MemBwSchema}, r.Schemata...) {
|
||||||
if l3CacheSchema != "" && memBwSchema != "" {
|
if s != "" {
|
||||||
if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil {
|
schemata.WriteString(s)
|
||||||
return err
|
schemata.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write only L3 cache schema string to schemata file
|
if schemata.Len() > 0 {
|
||||||
if l3CacheSchema != "" && memBwSchema == "" {
|
path := filepath.Join(m.GetPath(), "schemata")
|
||||||
if err := writeFile(path, "schemata", l3CacheSchema); err != nil {
|
if err := os.WriteFile(path, []byte(schemata.String()), 0o600); err != nil {
|
||||||
return err
|
return newLastCmdError(fmt.Errorf("intelrdt: unable to write %q: %w", schemata.String(), err))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write only memory bandwidth schema string to schemata file
|
|
||||||
if l3CacheSchema == "" && memBwSchema != "" {
|
|
||||||
if err := writeFile(path, "schemata", memBwSchema); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,97 +3,125 @@ package intelrdt
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIntelRdtSetL3CacheSchema(t *testing.T) {
|
func TestIntelRdtSet(t *testing.T) {
|
||||||
helper := NewIntelRdtTestUtil(t)
|
tcs := []struct {
|
||||||
|
name string
|
||||||
const (
|
config *configs.IntelRdt
|
||||||
l3CacheSchemaBefore = "L3:0=f;1=f0"
|
schemataAfter []string
|
||||||
l3CacheSchemeAfter = "L3:0=f0;1=f"
|
}{
|
||||||
)
|
{
|
||||||
|
name: "L3",
|
||||||
helper.writeFileContents(map[string]string{
|
config: &configs.IntelRdt{
|
||||||
"schemata": l3CacheSchemaBefore + "\n",
|
L3CacheSchema: "L3:0=f0;1=f",
|
||||||
})
|
},
|
||||||
|
schemataAfter: []string{"L3:0=f0;1=f"},
|
||||||
helper.config.IntelRdt.L3CacheSchema = l3CacheSchemeAfter
|
},
|
||||||
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
|
{
|
||||||
if err := intelrdt.Set(helper.config); err != nil {
|
name: "MemBw",
|
||||||
t.Fatal(err)
|
config: &configs.IntelRdt{
|
||||||
|
MemBwSchema: "MB:0=70;1=20",
|
||||||
|
},
|
||||||
|
schemataAfter: []string{"MB:0=70;1=20"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MemBwSc",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
MemBwSchema: "MB:0=9000;1=4000",
|
||||||
|
},
|
||||||
|
schemataAfter: []string{"MB:0=9000;1=4000"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "L3 and MemBw",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
L3CacheSchema: "L3:0=f0;1=f",
|
||||||
|
MemBwSchema: "MB:0=9000;1=4000",
|
||||||
|
},
|
||||||
|
schemataAfter: []string{
|
||||||
|
"L3:0=f0;1=f",
|
||||||
|
"MB:0=9000;1=4000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schemata",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
Schemata: []string{
|
||||||
|
"L3CODE:0=ff;1=ff",
|
||||||
|
"L3DATA:0=f;1=f0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
schemataAfter: []string{
|
||||||
|
"L3CODE:0=ff;1=ff",
|
||||||
|
"L3DATA:0=f;1=f0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schemata and L3",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
L3CacheSchema: "L3:0=f0;1=f",
|
||||||
|
Schemata: []string{"L2:0=ff00;1=ff"},
|
||||||
|
},
|
||||||
|
schemataAfter: []string{
|
||||||
|
"L3:0=f0;1=f",
|
||||||
|
"L2:0=ff00;1=ff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schemata and MemBw",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
MemBwSchema: "MB:0=2000;1=4000",
|
||||||
|
Schemata: []string{"L3:0=ff;1=ff"},
|
||||||
|
},
|
||||||
|
schemataAfter: []string{
|
||||||
|
"MB:0=2000;1=4000",
|
||||||
|
"L3:0=ff;1=ff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schemata, L3 and MemBw",
|
||||||
|
config: &configs.IntelRdt{
|
||||||
|
L3CacheSchema: "L3:0=80;1=7f",
|
||||||
|
MemBwSchema: "MB:0=2000;1=4000",
|
||||||
|
Schemata: []string{
|
||||||
|
"L2:0=ff00;1=ff",
|
||||||
|
"L3:0=c0;1=3f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
schemataAfter: []string{
|
||||||
|
"L3:0=80;1=7f",
|
||||||
|
"MB:0=2000;1=4000",
|
||||||
|
"L2:0=ff00;1=ff",
|
||||||
|
"L3:0=c0;1=3f",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
|
for _, tc := range tcs {
|
||||||
if err != nil {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Fatalf("Failed to parse file 'schemata' - %s", err)
|
helper := NewIntelRdtTestUtil(t)
|
||||||
}
|
helper.config.IntelRdt = tc.config
|
||||||
values := strings.Split(tmpStrings, "\n")
|
|
||||||
value := values[0]
|
|
||||||
|
|
||||||
if value != l3CacheSchemeAfter {
|
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
|
||||||
t.Fatal("Got the wrong value, set 'schemata' failed.")
|
if err := intelrdt.Set(helper.config); err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntelRdtSetMemBwSchema(t *testing.T) {
|
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
|
||||||
helper := NewIntelRdtTestUtil(t)
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to parse file 'schemata' - %s", err)
|
||||||
|
}
|
||||||
|
values := strings.Split(tmpStrings, "\n")
|
||||||
|
|
||||||
const (
|
if slices.Compare(values, tc.schemataAfter) != 0 {
|
||||||
memBwSchemaBefore = "MB:0=20;1=70"
|
t.Fatalf("Got the wrong value, expected %v, got %v", tc.schemataAfter, values)
|
||||||
memBwSchemeAfter = "MB:0=70;1=20"
|
}
|
||||||
)
|
})
|
||||||
|
|
||||||
helper.writeFileContents(map[string]string{
|
|
||||||
"schemata": memBwSchemaBefore + "\n",
|
|
||||||
})
|
|
||||||
|
|
||||||
helper.config.IntelRdt.MemBwSchema = memBwSchemeAfter
|
|
||||||
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
|
|
||||||
if err := intelrdt.Set(helper.config); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse file 'schemata' - %s", err)
|
|
||||||
}
|
|
||||||
values := strings.Split(tmpStrings, "\n")
|
|
||||||
value := values[0]
|
|
||||||
|
|
||||||
if value != memBwSchemeAfter {
|
|
||||||
t.Fatal("Got the wrong value, set 'schemata' failed.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIntelRdtSetMemBwScSchema(t *testing.T) {
|
|
||||||
helper := NewIntelRdtTestUtil(t)
|
|
||||||
|
|
||||||
const (
|
|
||||||
memBwScSchemaBefore = "MB:0=5000;1=7000"
|
|
||||||
memBwScSchemeAfter = "MB:0=9000;1=4000"
|
|
||||||
)
|
|
||||||
|
|
||||||
helper.writeFileContents(map[string]string{
|
|
||||||
"schemata": memBwScSchemaBefore + "\n",
|
|
||||||
})
|
|
||||||
|
|
||||||
helper.config.IntelRdt.MemBwSchema = memBwScSchemeAfter
|
|
||||||
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
|
|
||||||
if err := intelrdt.Set(helper.config); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse file 'schemata' - %s", err)
|
|
||||||
}
|
|
||||||
values := strings.Split(tmpStrings, "\n")
|
|
||||||
value := values[0]
|
|
||||||
|
|
||||||
if value != memBwScSchemeAfter {
|
|
||||||
t.Fatal("Got the wrong value, set 'schemata' failed.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ type Stats struct {
|
|||||||
// The memory bandwidth schema in 'container_id' group
|
// The memory bandwidth schema in 'container_id' group
|
||||||
MemBwSchema string `json:"mem_bw_schema,omitempty"`
|
MemBwSchema string `json:"mem_bw_schema,omitempty"`
|
||||||
|
|
||||||
|
// Schemata contains the full schemata of the ClosID (resctrl group) that the container is assigned to.
|
||||||
|
Schemata []string `json:"schemata,omitempty"`
|
||||||
|
|
||||||
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
|
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
|
||||||
MBMStats *[]MBMNumaNodeStats `json:"mbm_stats,omitempty"`
|
MBMStats *[]MBMNumaNodeStats `json:"mbm_stats,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -40,13 +40,3 @@ func NewIntelRdtTestUtil(t *testing.T) *intelRdtTestUtil {
|
|||||||
}
|
}
|
||||||
return &intelRdtTestUtil{config: config, IntelRdtPath: testIntelRdtPath, t: t}
|
return &intelRdtTestUtil{config: config, IntelRdtPath: testIntelRdtPath, t: t}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the specified contents on the mock of the specified Intel RDT "resource control" files
|
|
||||||
func (c *intelRdtTestUtil) writeFileContents(fileContents map[string]string) {
|
|
||||||
for file, contents := range fileContents {
|
|
||||||
err := writeFile(c.IntelRdtPath, file, contents)
|
|
||||||
if err != nil {
|
|
||||||
c.t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ func (l *linuxSetnsInit) Init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := setupMemoryPolicy(l.config.Config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Tell our parent that we're ready to exec. This must be done before the
|
// Tell our parent that we're ready to exec. This must be done before the
|
||||||
// Seccomp rules have been applied, because we need to be able to read and
|
// Seccomp rules have been applied, because we need to be able to read and
|
||||||
// write to a socket.
|
// write to a socket.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -41,6 +42,8 @@ var (
|
|||||||
flag int
|
flag int
|
||||||
}
|
}
|
||||||
complexFlags map[string]func(*configs.Mount)
|
complexFlags map[string]func(*configs.Mount)
|
||||||
|
mpolModeMap map[string]uint
|
||||||
|
mpolModeFMap map[string]uint
|
||||||
)
|
)
|
||||||
|
|
||||||
func initMaps() {
|
func initMaps() {
|
||||||
@@ -148,6 +151,22 @@ func initMaps() {
|
|||||||
m.IDMapping.Recursive = true
|
m.IDMapping.Recursive = true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mpolModeMap = map[string]uint{
|
||||||
|
string(specs.MpolDefault): configs.MPOL_DEFAULT,
|
||||||
|
string(specs.MpolPreferred): configs.MPOL_PREFERRED,
|
||||||
|
string(specs.MpolBind): configs.MPOL_BIND,
|
||||||
|
string(specs.MpolInterleave): configs.MPOL_INTERLEAVE,
|
||||||
|
string(specs.MpolLocal): configs.MPOL_LOCAL,
|
||||||
|
string(specs.MpolPreferredMany): configs.MPOL_PREFERRED_MANY,
|
||||||
|
string(specs.MpolWeightedInterleave): configs.MPOL_WEIGHTED_INTERLEAVE,
|
||||||
|
}
|
||||||
|
|
||||||
|
mpolModeFMap = map[string]uint{
|
||||||
|
string(specs.MpolFStaticNodes): configs.MPOL_F_STATIC_NODES,
|
||||||
|
string(specs.MpolFRelativeNodes): configs.MPOL_F_RELATIVE_NODES,
|
||||||
|
string(specs.MpolFNumaBalancing): configs.MPOL_F_NUMA_BALANCING,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +203,20 @@ func KnownMountOptions() []string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KnownMemoryPolicyModes returns the list of the known memory policy modes.
|
||||||
|
// Used by `runc features`.
|
||||||
|
func KnownMemoryPolicyModes() []string {
|
||||||
|
initMaps()
|
||||||
|
return slices.Sorted(maps.Keys(mpolModeMap))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KnownMemoryPolicyFlags returns the list of the known memory policy mode flags.
|
||||||
|
// Used by `runc features`.
|
||||||
|
func KnownMemoryPolicyFlags() []string {
|
||||||
|
initMaps()
|
||||||
|
return slices.Sorted(maps.Keys(mpolModeFMap))
|
||||||
|
}
|
||||||
|
|
||||||
// AllowedDevices is the set of devices which are automatically included for
|
// AllowedDevices is the set of devices which are automatically included for
|
||||||
// all containers.
|
// all containers.
|
||||||
//
|
//
|
||||||
@@ -463,10 +496,33 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||||||
if spec.Linux.IntelRdt != nil {
|
if spec.Linux.IntelRdt != nil {
|
||||||
config.IntelRdt = &configs.IntelRdt{
|
config.IntelRdt = &configs.IntelRdt{
|
||||||
ClosID: spec.Linux.IntelRdt.ClosID,
|
ClosID: spec.Linux.IntelRdt.ClosID,
|
||||||
|
Schemata: spec.Linux.IntelRdt.Schemata,
|
||||||
L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema,
|
L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema,
|
||||||
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
|
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if spec.Linux.MemoryPolicy != nil {
|
||||||
|
var ok bool
|
||||||
|
var err error
|
||||||
|
specMp := spec.Linux.MemoryPolicy
|
||||||
|
confMp := &configs.LinuxMemoryPolicy{}
|
||||||
|
confMp.Mode, ok = mpolModeMap[string(specMp.Mode)]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid memory policy mode %q", specMp.Mode)
|
||||||
|
}
|
||||||
|
confMp.Nodes, err = configs.ToCPUSet(specMp.Nodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid memory policy nodes %q: %w", specMp.Nodes, err)
|
||||||
|
}
|
||||||
|
for _, specFlag := range specMp.Flags {
|
||||||
|
confFlag, ok := mpolModeFMap[string(specFlag)]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid memory policy flag %q", specFlag)
|
||||||
|
}
|
||||||
|
confMp.Flags |= confFlag
|
||||||
|
}
|
||||||
|
config.MemoryPolicy = confMp
|
||||||
|
}
|
||||||
if spec.Linux.Personality != nil {
|
if spec.Linux.Personality != nil {
|
||||||
if len(spec.Linux.Personality.Flags) > 0 {
|
if len(spec.Linux.Personality.Flags) > 0 {
|
||||||
logrus.Warnf("ignoring unsupported personality flags: %+v because personality flag has not supported at this time", spec.Linux.Personality.Flags)
|
logrus.Warnf("ignoring unsupported personality flags: %+v because personality flag has not supported at this time", spec.Linux.Personality.Flags)
|
||||||
|
|||||||
@@ -171,6 +171,10 @@ func (l *linuxStandardInit) Init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := setupMemoryPolicy(l.config.Config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Tell our parent that we're ready to exec. This must be done before the
|
// Tell our parent that we're ready to exec. This must be done before the
|
||||||
// Seccomp rules have been applied, because we need to be able to read and
|
// Seccomp rules have been applied, because we need to be able to read and
|
||||||
// write to a socket.
|
// write to a socket.
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
setup_busybox
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
teardown_bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy interleave without flags" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_INTERLEAVE",
|
||||||
|
"nodes": "0"
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${lines[0]}" == "interleave:0" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy bind static" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_BIND",
|
||||||
|
"nodes": "0",
|
||||||
|
"flags": ["MPOL_F_STATIC_NODES"]
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${lines[0]}" == "bind"*"static"*"0" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run and exec memory policy prefer relative" {
|
||||||
|
update_config '
|
||||||
|
.linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_PREFERRED",
|
||||||
|
"nodes": "0",
|
||||||
|
"flags": ["MPOL_F_RELATIVE_NODES"]
|
||||||
|
}'
|
||||||
|
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
runc exec test_busybox /bin/sh -c "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${lines[0]}" == "prefer"*"relative"*"0" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run empty memory policy" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[0]}" == *"invalid memory policy"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy with non-existing mode" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "INTERLEAVE",
|
||||||
|
"nodes": "0"
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[0]}" == *"invalid memory policy"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy with invalid flag" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_PREFERRED",
|
||||||
|
"nodes": "0",
|
||||||
|
"flags": ["MPOL_F_RELATIVE_NODES", "badflag"]
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[0]}" == *"invalid memory policy flag"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy default with missing nodes" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_DEFAULT"
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${lines[0]}" == *"default"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy with missing mode" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"nodes": "0-7"
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[0]}" == *"invalid memory policy mode"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy calls syscall with invalid arguments" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_DEFAULT",
|
||||||
|
"nodes": "0-7",
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[*]}" == *"mode requires 0 nodes but got 8"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "runc run memory policy bind way too large a node number" {
|
||||||
|
update_config '
|
||||||
|
.process.args = ["/bin/sh", "-c", "head -n 1 /proc/self/numa_maps | cut -d \" \" -f 2"]
|
||||||
|
| .linux.memoryPolicy = {
|
||||||
|
"mode": "MPOL_BIND",
|
||||||
|
"nodes": "0-9876543210",
|
||||||
|
"flags": []
|
||||||
|
}'
|
||||||
|
runc run test_busybox
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[[ "${lines[0]}" == *"invalid memory policy node"* ]]
|
||||||
|
}
|
||||||
@@ -143,6 +143,9 @@ type IntelRdt struct {
|
|||||||
// The memory bandwidth schema in 'container_id' group
|
// The memory bandwidth schema in 'container_id' group
|
||||||
MemBwSchema string `json:"mem_bw_schema,omitempty"`
|
MemBwSchema string `json:"mem_bw_schema,omitempty"`
|
||||||
|
|
||||||
|
// Schemata contains the full schemata of the ClosID (resctrl group) that the container is assigned to.
|
||||||
|
Schemata []string `json:"schemata,omitempty"`
|
||||||
|
|
||||||
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
|
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
|
||||||
MBMStats *[]intelrdt.MBMNumaNodeStats `json:"mbm_stats,omitempty"`
|
MBMStats *[]intelrdt.MBMNumaNodeStats `json:"mbm_stats,omitempty"`
|
||||||
|
|
||||||
|
|||||||
+46
-6
@@ -251,6 +251,8 @@ type Linux struct {
|
|||||||
// IntelRdt contains Intel Resource Director Technology (RDT) information for
|
// IntelRdt contains Intel Resource Director Technology (RDT) information for
|
||||||
// handling resource constraints and monitoring metrics (e.g., L3 cache, memory bandwidth) for the container
|
// handling resource constraints and monitoring metrics (e.g., L3 cache, memory bandwidth) for the container
|
||||||
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
|
IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"`
|
||||||
|
// MemoryPolicy contains NUMA memory policy for the container.
|
||||||
|
MemoryPolicy *LinuxMemoryPolicy `json:"memoryPolicy,omitempty"`
|
||||||
// Personality contains configuration for the Linux personality syscall
|
// Personality contains configuration for the Linux personality syscall
|
||||||
Personality *LinuxPersonality `json:"personality,omitempty"`
|
Personality *LinuxPersonality `json:"personality,omitempty"`
|
||||||
// TimeOffsets specifies the offset for supporting time namespaces.
|
// TimeOffsets specifies the offset for supporting time namespaces.
|
||||||
@@ -836,23 +838,41 @@ type LinuxSyscall struct {
|
|||||||
type LinuxIntelRdt struct {
|
type LinuxIntelRdt struct {
|
||||||
// The identity for RDT Class of Service
|
// The identity for RDT Class of Service
|
||||||
ClosID string `json:"closID,omitempty"`
|
ClosID string `json:"closID,omitempty"`
|
||||||
|
|
||||||
|
// Schemata specifies the complete schemata to be written as is to the
|
||||||
|
// schemata file in resctrl fs. Each element represents a single line in the schemata file.
|
||||||
|
// NOTE: This will overwrite schemas specified in the L3CacheSchema and/or
|
||||||
|
// MemBwSchema fields.
|
||||||
|
Schemata []string `json:"schemata,omitempty"`
|
||||||
|
|
||||||
// The schema for L3 cache id and capacity bitmask (CBM)
|
// The schema for L3 cache id and capacity bitmask (CBM)
|
||||||
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
|
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
|
||||||
|
// NOTE: Should not be specified if Schemata is non-empty.
|
||||||
L3CacheSchema string `json:"l3CacheSchema,omitempty"`
|
L3CacheSchema string `json:"l3CacheSchema,omitempty"`
|
||||||
|
|
||||||
// The schema of memory bandwidth per L3 cache id
|
// The schema of memory bandwidth per L3 cache id
|
||||||
// Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..."
|
// Format: "MB:<cache_id0>=bandwidth0;<cache_id1>=bandwidth1;..."
|
||||||
// The unit of memory bandwidth is specified in "percentages" by
|
// The unit of memory bandwidth is specified in "percentages" by
|
||||||
// default, and in "MBps" if MBA Software Controller is enabled.
|
// default, and in "MBps" if MBA Software Controller is enabled.
|
||||||
|
// NOTE: Should not be specified if Schemata is non-empty.
|
||||||
MemBwSchema string `json:"memBwSchema,omitempty"`
|
MemBwSchema string `json:"memBwSchema,omitempty"`
|
||||||
|
|
||||||
// EnableCMT is the flag to indicate if the Intel RDT CMT is enabled. CMT (Cache Monitoring Technology) supports monitoring of
|
// EnableMonitoring enables resctrl monitoring for the container. This will
|
||||||
// the last-level cache (LLC) occupancy for the container.
|
// create a dedicated resctrl monitoring group for the container.
|
||||||
EnableCMT bool `json:"enableCMT,omitempty"`
|
EnableMonitoring bool `json:"enableMonitoring,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// EnableMBM is the flag to indicate if the Intel RDT MBM is enabled. MBM (Memory Bandwidth Monitoring) supports monitoring of
|
// LinuxMemoryPolicy represents input for the set_mempolicy syscall.
|
||||||
// total and local memory bandwidth for the container.
|
type LinuxMemoryPolicy struct {
|
||||||
EnableMBM bool `json:"enableMBM,omitempty"`
|
// Mode for the set_mempolicy syscall.
|
||||||
|
Mode MemoryPolicyModeType `json:"mode"`
|
||||||
|
|
||||||
|
// Nodes representing the nodemask for the set_mempolicy syscall in comma separated ranges format.
|
||||||
|
// Format: "<node0>-<node1>,<node2>,<node3>-<node4>,..."
|
||||||
|
Nodes string `json:"nodes"`
|
||||||
|
|
||||||
|
// Flags for the set_mempolicy syscall.
|
||||||
|
Flags []MemoryPolicyFlagType `json:"flags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZOS contains platform-specific configuration for z/OS based containers.
|
// ZOS contains platform-specific configuration for z/OS based containers.
|
||||||
@@ -884,6 +904,26 @@ const (
|
|||||||
ZOSUTSNamespace ZOSNamespaceType = "uts"
|
ZOSUTSNamespace ZOSNamespaceType = "uts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MemoryPolicyModeType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MpolDefault MemoryPolicyModeType = "MPOL_DEFAULT"
|
||||||
|
MpolBind MemoryPolicyModeType = "MPOL_BIND"
|
||||||
|
MpolInterleave MemoryPolicyModeType = "MPOL_INTERLEAVE"
|
||||||
|
MpolWeightedInterleave MemoryPolicyModeType = "MPOL_WEIGHTED_INTERLEAVE"
|
||||||
|
MpolPreferred MemoryPolicyModeType = "MPOL_PREFERRED"
|
||||||
|
MpolPreferredMany MemoryPolicyModeType = "MPOL_PREFERRED_MANY"
|
||||||
|
MpolLocal MemoryPolicyModeType = "MPOL_LOCAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemoryPolicyFlagType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MpolFNumaBalancing MemoryPolicyFlagType = "MPOL_F_NUMA_BALANCING"
|
||||||
|
MpolFRelativeNodes MemoryPolicyFlagType = "MPOL_F_RELATIVE_NODES"
|
||||||
|
MpolFStaticNodes MemoryPolicyFlagType = "MPOL_F_STATIC_NODES"
|
||||||
|
)
|
||||||
|
|
||||||
// LinuxSchedulerPolicy represents different scheduling policies used with the Linux Scheduler
|
// LinuxSchedulerPolicy represents different scheduling policies used with the Linux Scheduler
|
||||||
type LinuxSchedulerPolicy string
|
type LinuxSchedulerPolicy string
|
||||||
|
|
||||||
|
|||||||
+16
@@ -47,6 +47,7 @@ type Linux struct {
|
|||||||
Apparmor *Apparmor `json:"apparmor,omitempty"`
|
Apparmor *Apparmor `json:"apparmor,omitempty"`
|
||||||
Selinux *Selinux `json:"selinux,omitempty"`
|
Selinux *Selinux `json:"selinux,omitempty"`
|
||||||
IntelRdt *IntelRdt `json:"intelRdt,omitempty"`
|
IntelRdt *IntelRdt `json:"intelRdt,omitempty"`
|
||||||
|
MemoryPolicy *MemoryPolicy `json:"memoryPolicy,omitempty"`
|
||||||
MountExtensions *MountExtensions `json:"mountExtensions,omitempty"`
|
MountExtensions *MountExtensions `json:"mountExtensions,omitempty"`
|
||||||
NetDevices *NetDevices `json:"netDevices,omitempty"`
|
NetDevices *NetDevices `json:"netDevices,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -130,6 +131,21 @@ type IntelRdt struct {
|
|||||||
// Unrelated to whether the host supports Intel RDT or not.
|
// Unrelated to whether the host supports Intel RDT or not.
|
||||||
// Nil value means "unknown", not "false".
|
// Nil value means "unknown", not "false".
|
||||||
Enabled *bool `json:"enabled,omitempty"`
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
|
// Schemata is true if the "linux.intelRdt.enableMonitoring" field of the
|
||||||
|
// spec is implemented.
|
||||||
|
Schemata *bool `json:"schemata,omitempty"`
|
||||||
|
// Monitoring is true if the "linux.intelRdt.enableMonitoring" field of the
|
||||||
|
// spec is implemented.
|
||||||
|
// Nil value means "unknown", not "false".
|
||||||
|
Monitoring *bool `json:"monitoring,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryPolicy represents the "memoryPolicy" field.
|
||||||
|
type MemoryPolicy struct {
|
||||||
|
// modes is the list of known memory policy modes, e.g., "MPOL_INTERLEAVE".
|
||||||
|
Modes []string `json:"modes,omitempty"`
|
||||||
|
// flags is the list of known memory policy mode flags, e.g., "MPOL_F_STATIC_NODES".
|
||||||
|
Flags []string `json:"flags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MountExtensions represents the "mountExtensions" field.
|
// MountExtensions represents the "mountExtensions" field.
|
||||||
|
|||||||
Vendored
+1
-1
@@ -62,7 +62,7 @@ github.com/opencontainers/cgroups/fscommon
|
|||||||
github.com/opencontainers/cgroups/internal/path
|
github.com/opencontainers/cgroups/internal/path
|
||||||
github.com/opencontainers/cgroups/manager
|
github.com/opencontainers/cgroups/manager
|
||||||
github.com/opencontainers/cgroups/systemd
|
github.com/opencontainers/cgroups/systemd
|
||||||
# github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67
|
# github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/opencontainers/runtime-spec/specs-go
|
github.com/opencontainers/runtime-spec/specs-go
|
||||||
github.com/opencontainers/runtime-spec/specs-go/features
|
github.com/opencontainers/runtime-spec/specs-go/features
|
||||||
|
|||||||
Reference in New Issue
Block a user