|
| 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 | +} |
0 commit comments