Skip to content

Commit 7932aa2

Browse files
committed
Add memory policy support
Signed-off-by: Antti Kervinen <antti.kervinen@intel.com>
1 parent edf4cb3 commit 7932aa2

File tree

6 files changed

+217
-2
lines changed

6 files changed

+217
-2
lines changed

cmd/oci-runtime-tool/generate.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ var generateFlags = []cli.Flag{
6464
cli.StringFlag{Name: "linux-mems", Usage: "list of memory nodes in the cpuset (default is to use any available memory node)"},
6565
cli.Uint64Flag{Name: "linux-mem-swap", Usage: "total memory limit (memory + swap) (in bytes)"},
6666
cli.Uint64Flag{Name: "linux-mem-swappiness", Usage: "how aggressive the kernel will swap memory pages (Range from 0 to 100)"},
67+
cli.StringFlag{Name: "linux-memorypolicy-mode", Usage: "mode of the default NUMA memory policy for page allocation, e.g MPOL_INTERLEAVE"},
68+
cli.StringFlag{Name: "linux-memorypolicy-nodes", Usage: "nodes of the default NUMA memory policy, e.g 0-3,7"},
69+
cli.StringSliceFlag{Name: "linux-memorypolicy-flags", Usage: "flags of the default NUMA memory policy, e.g MPOL_F_STATIC_NODES"},
6770
cli.StringFlag{Name: "linux-mount-label", Usage: "selinux mount context label"},
6871
cli.StringSliceFlag{Name: "linux-namespace-add", Usage: "adds a namespace to the set of namespaces to create or join of the form 'ns[:path]'"},
6972
cli.StringSliceFlag{Name: "linux-namespace-remove", Usage: "removes a namespace from the set of namespaces to create or join of the form 'ns'"},
@@ -782,6 +785,27 @@ func setupSpec(g *generate.Generator, context *cli.Context) error {
782785
g.SetLinuxResourcesMemorySwappiness(context.Uint64("linux-mem-swappiness"))
783786
}
784787

788+
if context.IsSet("linux-memorypolicy-mode") {
789+
mpolMode := context.String("linux-memorypolicy-mode")
790+
if err := g.SetLinuxMemoryPolicyMode(mpolMode); err != nil {
791+
return err
792+
}
793+
}
794+
795+
if context.IsSet("linux-memorypolicy-nodes") {
796+
mpolNodes := context.String("linux-memorypolicy-nodes")
797+
if err := g.SetLinuxMemoryPolicyNodes(mpolNodes); err != nil {
798+
return err
799+
}
800+
}
801+
802+
if context.IsSet("linux-memorypolicy-flags") {
803+
mpolFlags := context.StringSlice("linux-memorypolicy-flags")
804+
if err := g.SetLinuxMemoryPolicyFlags(mpolFlags); err != nil {
805+
return err
806+
}
807+
}
808+
785809
if context.IsSet("linux-network-classid") {
786810
g.SetLinuxResourcesNetworkClassID(uint32(context.Int("linux-network-classid")))
787811
}

generate/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ func (g *Generator) initConfigLinuxResourcesMemory() {
109109
}
110110
}
111111

112+
func (g *Generator) initConfigLinuxMemoryPolicy() {
113+
g.initConfigLinux()
114+
if g.Config.Linux.MemoryPolicy == nil {
115+
g.Config.Linux.MemoryPolicy = &rspec.LinuxMemoryPolicy{}
116+
}
117+
}
118+
112119
func (g *Generator) initConfigLinuxResourcesNetwork() {
113120
g.initConfigLinuxResources()
114121
if g.Config.Linux.Resources.Network == nil {

generate/generate.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
rspec "github.com/opencontainers/runtime-spec/specs-go"
1414
"github.com/opencontainers/runtime-tools/generate/seccomp"
1515
capsCheck "github.com/opencontainers/runtime-tools/validate/capabilities"
16+
mpolCheck "github.com/opencontainers/runtime-tools/validate/memorypolicy"
1617
)
1718

1819
var (
@@ -932,6 +933,40 @@ func (g *Generator) SetLinuxResourcesMemorySwappiness(swappiness uint64) {
932933
g.Config.Linux.Resources.Memory.Swappiness = &swappiness
933934
}
934935

936+
// SetLinuxMemoryPolicyMode sets g.Config.Linux.MemoryPolicy.Mode
937+
func (g *Generator) SetLinuxMemoryPolicyMode(mode string) error {
938+
modecp := strings.ToUpper(mode)
939+
if err := mpolCheck.MpolModeValid(modecp); err != nil {
940+
return err
941+
}
942+
g.initConfigLinuxMemoryPolicy()
943+
g.Config.Linux.MemoryPolicy.Mode = rspec.MemoryPolicyModeType(modecp)
944+
return nil
945+
}
946+
947+
// SetLinuxMemoryPolicyNodes sets g.Config.Linux.MemoryPolicy.Nodes
948+
func (g *Generator) SetLinuxMemoryPolicyNodes(nodes string) error {
949+
if err := mpolCheck.MpolNodesValid(nodes); err != nil {
950+
return err
951+
}
952+
g.initConfigLinuxMemoryPolicy()
953+
g.Config.Linux.MemoryPolicy.Nodes = nodes
954+
return nil
955+
}
956+
957+
// SetLinuxMemoryPolicyFlags sets g.Config.Linux.MemoryPolicy.Flags
958+
func (g *Generator) SetLinuxMemoryPolicyFlags(flags []string) error {
959+
g.initConfigLinuxMemoryPolicy()
960+
for _, flag := range flags {
961+
flagcp := strings.ToUpper(flag)
962+
if err := mpolCheck.MpolFlagValid(flagcp); err != nil {
963+
return err
964+
}
965+
g.Config.Linux.MemoryPolicy.Flags = append(g.Config.Linux.MemoryPolicy.Flags, rspec.MemoryPolicyFlagType(flagcp))
966+
}
967+
return nil
968+
}
969+
935970
// SetLinuxResourcesMemoryDisableOOMKiller sets g.Config.Linux.Resources.Memory.DisableOOMKiller.
936971
func (g *Generator) SetLinuxResourcesMemoryDisableOOMKiller(disable bool) {
937972
g.initConfigLinuxResourcesMemory()

validate/memorypolicy/validate.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package memorypolicy
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/hashicorp/go-multierror"
9+
rspec "github.com/opencontainers/runtime-spec/specs-go"
10+
)
11+
12+
var (
13+
knownModes map[rspec.MemoryPolicyModeType]struct{} = map[rspec.MemoryPolicyModeType]struct{}{
14+
rspec.MpolDefault: {},
15+
rspec.MpolBind: {},
16+
rspec.MpolInterleave: {},
17+
rspec.MpolWeightedInterleave: {},
18+
rspec.MpolPreferred: {},
19+
rspec.MpolPreferredMany: {},
20+
rspec.MpolLocal: {},
21+
}
22+
23+
knownModeFlags map[rspec.MemoryPolicyFlagType]struct{} = map[rspec.MemoryPolicyFlagType]struct{}{
24+
rspec.MpolFNumaBalancing: {},
25+
rspec.MpolFRelativeNodes: {},
26+
rspec.MpolFStaticNodes: {},
27+
}
28+
)
29+
30+
// MpolModeValid checks if the provided memory policy mode is valid.
31+
func MpolModeValid(mode string) error {
32+
if !strings.HasPrefix(mode, "MPOL_") {
33+
return fmt.Errorf("memory policy mode %q must start with 'MPOL_'", mode)
34+
}
35+
if _, ok := knownModes[rspec.MemoryPolicyModeType(mode)]; !ok {
36+
return fmt.Errorf("invalid memory policy mode %q", mode)
37+
}
38+
return nil
39+
}
40+
41+
// MpolNodesValid checks if the provided nodes specification is valid.
42+
func MpolNodesValid(nodes string) error {
43+
// nodes is a comma-separated list of node IDs or ranges thereof.
44+
nodeRanges := strings.Split(nodes, ",")
45+
for _, nodeRange := range nodeRanges {
46+
nodeRange = strings.TrimSpace(nodeRange)
47+
if nodeRange == "" {
48+
continue
49+
}
50+
bounds := strings.Split(nodeRange, "-")
51+
switch len(bounds) {
52+
case 1:
53+
// Single node
54+
number := strings.TrimSpace(bounds[0])
55+
if _, err := parseNodeID(number); err != nil {
56+
return err
57+
}
58+
case 2:
59+
// Range of nodes
60+
startNumber := strings.TrimSpace(bounds[0])
61+
startID, err := parseNodeID(startNumber)
62+
if err != nil {
63+
return err
64+
}
65+
endNumber := strings.TrimSpace(bounds[1])
66+
endID, err := parseNodeID(endNumber)
67+
if err != nil {
68+
return err
69+
}
70+
if startID > endID {
71+
return fmt.Errorf("invalid memory policy node range %q: start ID greater than end ID", nodeRange)
72+
}
73+
default:
74+
return fmt.Errorf("invalid memory policy node range %q", nodeRange)
75+
}
76+
}
77+
return nil
78+
}
79+
80+
func parseNodeID(nodeStr string) (int, error) {
81+
nodeID, err := strconv.Atoi(nodeStr)
82+
if err != nil {
83+
return 0, fmt.Errorf("invalid memory policy node %q", nodeStr)
84+
}
85+
if nodeID < 0 {
86+
return 0, fmt.Errorf("memory policy node %d must be non-negative", nodeID)
87+
}
88+
return nodeID, nil
89+
}
90+
91+
// MpolFlagValid checks if the provided memory policy flag is valid.
92+
func MpolFlagValid(flag string) error {
93+
if !strings.HasPrefix(flag, "MPOL_F_") {
94+
return fmt.Errorf("memory policy flag %q must start with 'MPOL_F_'", flag)
95+
}
96+
if _, ok := knownModeFlags[rspec.MemoryPolicyFlagType(flag)]; !ok {
97+
return fmt.Errorf("invalid memory policy flag %q", flag)
98+
}
99+
return nil
100+
}
101+
102+
// MpolModeNodesValid checks if the nodes specification is valid for the given memory policy mode.
103+
func MpolModeNodesValid(mode rspec.MemoryPolicyModeType, nodes string) error {
104+
switch mode {
105+
case rspec.MpolDefault, rspec.MpolLocal:
106+
if nodes != "" {
107+
return fmt.Errorf("memory policy mode %q must not have nodes specified", mode)
108+
}
109+
case rspec.MpolBind, rspec.MpolInterleave, rspec.MpolWeightedInterleave, rspec.MpolPreferred, rspec.MpolPreferredMany:
110+
if nodes == "" {
111+
return fmt.Errorf("memory policy mode %q must have nodes specified", mode)
112+
}
113+
case "":
114+
return fmt.Errorf("memory policy mode must be specified")
115+
default:
116+
return fmt.Errorf("unknown memory policy mode %q ", mode)
117+
}
118+
return nil
119+
}
120+
121+
// MpolValid checks if the provided memory policy configuration is valid.
122+
func MpolValid(mode rspec.MemoryPolicyModeType, nodes string, flags []rspec.MemoryPolicyFlagType) (errs error) {
123+
if err := MpolModeValid(string(mode)); err != nil {
124+
errs = multierror.Append(errs, err)
125+
}
126+
if err := MpolNodesValid(nodes); err != nil {
127+
errs = multierror.Append(errs, err)
128+
}
129+
for _, flag := range flags {
130+
if err := MpolFlagValid(string(flag)); err != nil {
131+
multierror.Append(errs, err)
132+
}
133+
}
134+
if errs == nil {
135+
err := MpolModeNodesValid(mode, nodes)
136+
if err != nil {
137+
errs = multierror.Append(errs, err)
138+
}
139+
}
140+
return errs
141+
}

validate/validate_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
rspec "github.com/opencontainers/runtime-spec/specs-go"
1515
osFilepath "github.com/opencontainers/runtime-tools/filepath"
1616
"github.com/opencontainers/runtime-tools/specerror"
17+
mpolCheck "github.com/opencontainers/runtime-tools/validate/memorypolicy"
1718
"github.com/opencontainers/selinux/go-selinux/label"
1819
"github.com/sirupsen/logrus"
1920
)
@@ -220,5 +221,12 @@ func (v *Validator) CheckLinux() (errs error) {
220221
}
221222
}
222223

224+
if v.spec.Linux.MemoryPolicy != nil {
225+
mp := v.spec.Linux.MemoryPolicy
226+
if err := mpolCheck.MpolValid(mp.Mode, mp.Nodes, mp.Flags); err != nil {
227+
errs = multierror.Append(errs, err)
228+
}
229+
}
230+
223231
return
224232
}

vendor/github.com/opencontainers/runtime-spec/specs-go/version.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)