Skip to content

Commit 1f3b218

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 <krisztian.litkey@intel.com>
1 parent 2764304 commit 1f3b218

2 files changed

Lines changed: 139 additions & 0 deletions

File tree

plugins/device-injector/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,43 @@ instead of a single container. Also many of the settings are not namespaced at
158158
all. Trying to adjust such a setting will result in an error and a failure to
159159
create the container.
160160

161+
### Linux Memory Policy Annotations
162+
163+
Memory policies are annotated in a similar manner to devices, but using the
164+
`memory-policy.noderesource.dev` annotation key prefix. As with devices, the
165+
`memory-policy.nri.io` annotation key prefix is also supported.
166+
167+
The annotation value syntax for memory policy adjustment is
168+
169+
```
170+
mode: <policy mode, for instance MPOL_INTERLEAVE>
171+
nodes: <list of NUMA nodes as a string, for instance "2,3">
172+
flags: <list of policy flags as strings, for instance [ MPOL_F_STATIC_NODES ] >
173+
```
174+
175+
The supported modes are:
176+
177+
- MPOL_DEFAULT
178+
- MPOL_PREFERRED
179+
- MPOL_BIND
180+
- MPOL_INTERLEAVE
181+
- MPOL_LOCAL
182+
- MPOL_PREFERRED_MANY
183+
- MPOL_WEIGHTED_INTERLEAVE
184+
185+
You can omit the MPOL_ prefix and can use lowercase at will.
186+
187+
The supported flags are:
188+
189+
- MPOL_F_STATIC_NODES
190+
- MPOL_F_RELATIVE_NODES
191+
- MPOL_F_NUMA_BALANCING
192+
193+
As with modes, you can omit the MPOL_F_ prefix and use lowercase at will.
194+
195+
See man set_mempolicy(2) for a description of what the effects are of these
196+
settings.
197+
161198
## Deployment
162199

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

plugins/device-injector/device-injector.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ const (
5757
oldSchedulerKey = "scheduling-policy.nri.io"
5858
// Prefix of the key used for sysctl adjustment.
5959
sysctlKey = "sysctl.noderesource.dev"
60+
// Prefix of the key used for memory policy adjustment.
61+
memoryPolicyKey = "memory-policy.noderesource.dev"
62+
// Deprecated: Prefix of the key used for memory policy adjustment.
63+
oldMemoryPolicyKey = "memory-policy.nri.io"
6064
)
6165

6266
var (
@@ -106,6 +110,13 @@ type scheduler struct {
106110
Period uint64 `json:"period"`
107111
}
108112

113+
// memoryPolicy adjustment
114+
type memoryPolicy struct {
115+
Mode string `json:"mode"`
116+
Nodes string `json:"nodes"`
117+
Flags []string `json:"flags"`
118+
}
119+
109120
// our injector plugin
110121
type plugin struct {
111122
stub stub.Stub
@@ -146,6 +157,10 @@ func (p *plugin) CreateContainer(_ context.Context, pod *api.PodSandbox, ctr *ap
146157
return nil, nil, err
147158
}
148159

160+
if err := adjustMemoryPolicy(pod, ctr, adjust); err != nil {
161+
return nil, nil, err
162+
}
163+
149164
if verbose {
150165
dump(containerName(pod, ctr), "ContainerAdjustment", adjust)
151166
}
@@ -443,6 +458,53 @@ func parseSysctl(ctr string, annotations map[string]string) (map[string]string,
443458
return sysctl, nil
444459
}
445460

461+
func adjustMemoryPolicy(pod *api.PodSandbox, ctr *api.Container, a *api.ContainerAdjustment) error {
462+
pol, err := parseMemoryPolicy(ctr.Name, pod.Annotations)
463+
if err != nil {
464+
log.Errorf("%s: invalid memory policy annotation: %v",
465+
containerName(pod, ctr), err)
466+
return err
467+
}
468+
469+
if pol == nil {
470+
log.Debugf("%s: no memory policy annotated...", containerName(pod, ctr))
471+
return nil
472+
}
473+
474+
if verbose {
475+
dump(containerName(pod, ctr), "annotated memory policy", pol)
476+
}
477+
478+
amp, err := pol.ToNRI()
479+
if err != nil {
480+
log.Errorf("%s: invalid memory policy annotation: %v",
481+
containerName(pod, ctr), err)
482+
return fmt.Errorf("invalid memory policy: %w", err)
483+
}
484+
485+
a.SetLinuxMemoryPolicy(amp.Mode, amp.Nodes, amp.Flags...)
486+
log.Infof("%s: adjusted memory policy to %s...", containerName(pod, ctr), amp)
487+
488+
return nil
489+
}
490+
491+
func parseMemoryPolicy(ctr string, annotations map[string]string) (*memoryPolicy, error) {
492+
var (
493+
policy = &memoryPolicy{}
494+
)
495+
496+
annotation := getAnnotation(annotations, memoryPolicyKey, oldMemoryPolicyKey, ctr)
497+
if annotation == nil {
498+
return nil, nil
499+
}
500+
501+
if err := yaml.Unmarshal(annotation, policy); err != nil {
502+
return nil, fmt.Errorf("invalid memory policy annotation %q: %w", string(annotation), err)
503+
}
504+
505+
return policy, nil
506+
}
507+
446508
func getAnnotation(annotations map[string]string, mainKey, oldKey, ctr string) []byte {
447509
for _, key := range []string{
448510
mainKey + "/container." + ctr,
@@ -570,6 +632,46 @@ func (sc *scheduler) String() string {
570632
return s
571633
}
572634

635+
// Convert memoryPolicy to the NRI API representation.
636+
func (mp *memoryPolicy) ToNRI() (*api.LinuxMemoryPolicy, error) {
637+
apiPol := &api.LinuxMemoryPolicy{
638+
Nodes: mp.Nodes,
639+
}
640+
641+
mode, ok := api.MpolMode_value[mp.Mode]
642+
if !ok {
643+
return nil, fmt.Errorf("invalid memory policy mode %q", mp.Mode)
644+
}
645+
apiPol.Mode = api.MpolMode(mode)
646+
647+
for _, f := range mp.Flags {
648+
flag, ok := api.MpolFlag_value[f]
649+
if !ok {
650+
return nil, fmt.Errorf("invalid memory policy flag %q", f)
651+
}
652+
apiPol.Flags = append(apiPol.Flags, api.MpolFlag(flag))
653+
}
654+
655+
return apiPol, nil
656+
}
657+
658+
func (mp *memoryPolicy) String() string {
659+
if mp == nil {
660+
return "<no memory policy>"
661+
}
662+
663+
s := fmt.Sprintf("<memory policy mode=%s", mp.Mode)
664+
if mp.Nodes != "" {
665+
s += fmt.Sprintf(", nodes=%s", mp.Nodes)
666+
}
667+
if len(mp.Flags) > 0 {
668+
s += fmt.Sprintf(", flags=%v", mp.Flags)
669+
}
670+
s += ">"
671+
672+
return s
673+
}
674+
573675
// Construct a container name for log messages.
574676
func containerName(pod *api.PodSandbox, container *api.Container) string {
575677
if pod != nil {

0 commit comments

Comments
 (0)