Skip to content

Commit 700c9ff

Browse files
committed
device-injector: implement memory policy adjustment.
Add support for adjusting container linux memory policy based on annotations. Signed-off-by: Krisztian Litkey <[email protected]>
1 parent 7674034 commit 700c9ff

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

plugins/device-injector/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,43 @@ requests the injection of the host network interface `ens2.100` into container `
128128
as the network interface `netdev0`, and the host network interface `ens2.101` into
129129
container `c1` as the network interface `netdev1`.
130130

131+
### Linux Memory Policy Annotations
132+
133+
Memory policies are annotated in a similar manner to devices, but using the
134+
`memory-policy.noderesource.dev` annotation key prefix. As with devices, the
135+
`memory-policy.nri.io` annotation key prefix is also supported.
136+
137+
The annotation value syntax for memory policy adjustment is
138+
139+
```
140+
mode: <policy mode, for instance MPOL_INTERLEAVE>
141+
nodes: <list of NUMA nodes as a string, for instance "2,3">
142+
flags: <list of policy flags as strings, for instance [ MPOL_F_STATIC_NODES ] >
143+
```
144+
145+
The supported modes are:
146+
147+
- MPOL_DEFAULT
148+
- MPOL_PREFERRED
149+
- MPOL_BIND
150+
- MPOL_INTERLEAVE
151+
- MPOL_LOCAL
152+
- MPOL_PREFERRED_MANY
153+
- MPOL_WEIGHTED_INTERLEAVE
154+
155+
You can omit the MPOL_ prefix and can use lowercase at will.
156+
157+
The supported flags are:
158+
159+
- MPOL_F_STATIC_NODES
160+
- MPOL_F_RELATIVE_NODES
161+
- MPOL_F_NUMA_BALANCING
162+
163+
As with modes, you can omit the MPOL_F_ prefix and use lowercase at will.
164+
165+
See man set_mempolicy(2) for a description of what the effects are of these
166+
settings.
167+
131168
## Deployment
132169

133170
The NRI repository contains minimal kustomize overlays for this plugin at

plugins/device-injector/device-injector.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ const (
5555
schedulerKey = "scheduling-policy.noderesource.dev"
5656
// Deprecated: Prefix of the key used for scheduler attribute adjustment.
5757
oldSchedulerKey = "scheduling-policy.nri.io"
58+
// Prefix of the key used for memory policy adjustment.
59+
memoryPolicyKey = "memory-policy.noderesource.dev"
60+
// Deprecated: Prefix of the key used for memory policy adjustment.
61+
oldMemoryPolicyKey = "memory-policy.nri.io"
5862
)
5963

6064
var (
@@ -104,6 +108,13 @@ type scheduler struct {
104108
Period uint64 `json:"period"`
105109
}
106110

111+
// memoryPolicy adjustment
112+
type memoryPolicy struct {
113+
Mode string `json:"mode"`
114+
Nodes string `json:"nodes"`
115+
Flags []string `json:"flags"`
116+
}
117+
107118
// our injector plugin
108119
type plugin struct {
109120
stub stub.Stub
@@ -135,6 +146,9 @@ func (p *plugin) CreateContainer(_ context.Context, pod *api.PodSandbox, ctr *ap
135146
if err := adjustScheduler(pod, ctr, adjust); err != nil {
136147
return nil, nil, err
137148
}
149+
if err := adjustMemoryPolicy(pod, ctr, adjust); err != nil {
150+
return nil, nil, err
151+
}
138152

139153
if err := injectNetDevices(pod, ctr, adjust); err != nil {
140154
return nil, nil, err
@@ -395,6 +409,70 @@ func parseScheduler(ctr string, annotations map[string]string) (*scheduler, erro
395409
return sch, nil
396410
}
397411

412+
func adjustMemoryPolicy(pod *api.PodSandbox, ctr *api.Container, a *api.ContainerAdjustment) error {
413+
pol, err := parseMemoryPolicy(ctr.Name, pod.Annotations)
414+
if err != nil {
415+
log.Errorf("%s: invalid memory policy annotation: %v",
416+
containerName(pod, ctr), err)
417+
return err
418+
}
419+
420+
if pol == nil {
421+
log.Debugf("%s: no memory policy annotated...", containerName(pod, ctr))
422+
return nil
423+
}
424+
425+
if verbose {
426+
dump(containerName(pod, ctr), "annotated memory policy", pol)
427+
}
428+
429+
amp, err := pol.ToNRI()
430+
if err != nil {
431+
log.Errorf("%s: invalid memory policy annotation: %v",
432+
containerName(pod, ctr), err)
433+
return fmt.Errorf("invalid memory policy: %w", err)
434+
}
435+
436+
a.SetLinuxMemoryPolicy(amp.Mode, amp.Nodes, amp.Flags...)
437+
if !verbose {
438+
log.Infof("%s: adjusted memory policy to %s...", containerName(pod, ctr), amp)
439+
}
440+
441+
return nil
442+
}
443+
444+
func parseMemoryPolicy(ctr string, annotations map[string]string) (*memoryPolicy, error) {
445+
var (
446+
policy = &memoryPolicy{}
447+
)
448+
449+
annotation := getAnnotation(annotations, memoryPolicyKey, oldMemoryPolicyKey, ctr)
450+
if annotation == nil {
451+
return nil, nil
452+
}
453+
454+
if err := yaml.Unmarshal(annotation, policy); err != nil {
455+
return nil, fmt.Errorf("invalid memory policy annotation %q: %w", string(annotation), err)
456+
}
457+
458+
if policy.Mode != "" {
459+
policy.Mode = strings.ToUpper(policy.Mode)
460+
if !strings.HasPrefix(policy.Mode, "MPOL_") {
461+
policy.Mode = "MPOL_" + policy.Mode
462+
}
463+
}
464+
465+
for i, f := range policy.Flags {
466+
f = strings.ToUpper(f)
467+
if !strings.HasPrefix(f, "MPOL_F_") {
468+
f = "MPOL_F_" + f
469+
}
470+
policy.Flags[i] = f
471+
}
472+
473+
return policy, nil
474+
}
475+
398476
func getAnnotation(annotations map[string]string, mainKey, oldKey, ctr string) []byte {
399477
for _, key := range []string{
400478
mainKey + "/container." + ctr,
@@ -519,6 +597,46 @@ func (sc *scheduler) String() string {
519597
return s
520598
}
521599

600+
// Convert memoryPolicy to the NRI API representation.
601+
func (mp *memoryPolicy) ToNRI() (*api.LinuxMemoryPolicy, error) {
602+
apiPol := &api.LinuxMemoryPolicy{
603+
Nodes: mp.Nodes,
604+
}
605+
606+
mode, ok := api.MpolMode_value[strings.ToUpper(mp.Mode)]
607+
if !ok {
608+
return nil, fmt.Errorf("invalid memory policy mode %q", mp.Mode)
609+
}
610+
apiPol.Mode = api.MpolMode(mode)
611+
612+
for _, f := range mp.Flags {
613+
flag, ok := api.MpolFlag_value[strings.ToUpper(f)]
614+
if !ok {
615+
return nil, fmt.Errorf("invalid memory policy flag %q", f)
616+
}
617+
apiPol.Flags = append(apiPol.Flags, api.MpolFlag(flag))
618+
}
619+
620+
return apiPol, nil
621+
}
622+
623+
func (mp *memoryPolicy) String() string {
624+
if mp == nil {
625+
return "<no memory policy>"
626+
}
627+
628+
s := fmt.Sprintf("<memory policy mode=%s", mp.Mode)
629+
if mp.Nodes != "" {
630+
s += fmt.Sprintf(", nodes=%s", mp.Nodes)
631+
}
632+
if len(mp.Flags) > 0 {
633+
s += fmt.Sprintf(", flags=%v", mp.Flags)
634+
}
635+
s += ">"
636+
637+
return s
638+
}
639+
522640
// Construct a container name for log messages.
523641
func containerName(pod *api.PodSandbox, container *api.Container) string {
524642
if pod != nil {

0 commit comments

Comments
 (0)