github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/cgroups.go (about)

     1  // Copyright (c) 2018 Huawei Corporation
     2  // Copyright (c) 2019 Intel Corporation
     3  //
     4  // SPDX-License-Identifier: Apache-2.0
     5  //
     6  
     7  package virtcontainers
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	"github.com/containerd/cgroups"
    17  	specs "github.com/opencontainers/runtime-spec/specs-go"
    18  )
    19  
    20  type cgroupPather interface {
    21  	cgroups.Subsystem
    22  	Path(path string) string
    23  }
    24  
    25  // unconstrained cgroups are placed here.
    26  // for example /sys/fs/cgroup/memory/kata/$CGPATH
    27  // where path is defined by the containers manager
    28  const cgroupKataPath = "/kata/"
    29  
    30  var cgroupsLoadFunc = cgroups.Load
    31  var cgroupsNewFunc = cgroups.New
    32  
    33  // V1Constraints returns the cgroups that are compatible with the VC architecture
    34  // and hypervisor, constraints can be applied to these cgroups.
    35  func V1Constraints() ([]cgroups.Subsystem, error) {
    36  	root, err := cgroupV1MountPoint()
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	subsystems := []cgroups.Subsystem{
    41  		cgroups.NewCputset(root),
    42  		cgroups.NewCpu(root),
    43  		cgroups.NewCpuacct(root),
    44  	}
    45  	return cgroupsSubsystems(subsystems)
    46  }
    47  
    48  // V1NoConstraints returns the cgroups that are *not* compatible with the VC
    49  // architecture and hypervisor, constraints MUST NOT be applied to these cgroups.
    50  func V1NoConstraints() ([]cgroups.Subsystem, error) {
    51  	root, err := cgroupV1MountPoint()
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	subsystems := []cgroups.Subsystem{
    56  		// Some constainers managers, like k8s, take the control of cgroups.
    57  		// k8s: the memory cgroup for the dns containers is small to place
    58  		// a hypervisor there.
    59  		cgroups.NewMemory(root),
    60  	}
    61  	return cgroupsSubsystems(subsystems)
    62  }
    63  
    64  func cgroupsSubsystems(subsystems []cgroups.Subsystem) ([]cgroups.Subsystem, error) {
    65  	var enabled []cgroups.Subsystem
    66  	for _, s := range cgroupPathers(subsystems) {
    67  		// check and remove the default groups that do not exist
    68  		if _, err := os.Lstat(s.Path("/")); err == nil {
    69  			enabled = append(enabled, s)
    70  		}
    71  	}
    72  	return enabled, nil
    73  }
    74  
    75  func cgroupPathers(subystems []cgroups.Subsystem) []cgroupPather {
    76  	var out []cgroupPather
    77  	for _, s := range subystems {
    78  		if p, ok := s.(cgroupPather); ok {
    79  			out = append(out, p)
    80  		}
    81  	}
    82  	return out
    83  }
    84  
    85  // v1MountPoint returns the mount point where the cgroup
    86  // mountpoints are mounted in a single hiearchy
    87  func cgroupV1MountPoint() (string, error) {
    88  	f, err := os.Open("/proc/self/mountinfo")
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  	defer f.Close()
    93  	scanner := bufio.NewScanner(f)
    94  	for scanner.Scan() {
    95  		if err := scanner.Err(); err != nil {
    96  			return "", err
    97  		}
    98  		var (
    99  			text   = scanner.Text()
   100  			fields = strings.Split(text, " ")
   101  			// safe as mountinfo encodes mountpoints with spaces as \040.
   102  			index               = strings.Index(text, " - ")
   103  			postSeparatorFields = strings.Fields(text[index+3:])
   104  			numPostFields       = len(postSeparatorFields)
   105  		)
   106  		// this is an error as we can't detect if the mount is for "cgroup"
   107  		if numPostFields == 0 {
   108  			return "", fmt.Errorf("Found no fields post '-' in %q", text)
   109  		}
   110  		if postSeparatorFields[0] == "cgroup" {
   111  			// check that the mount is properly formated.
   112  			if numPostFields < 3 {
   113  				return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
   114  			}
   115  			return filepath.Dir(fields[4]), nil
   116  		}
   117  	}
   118  	return "", cgroups.ErrMountPointNotExist
   119  }
   120  
   121  func cgroupNoConstraintsPath(path string) string {
   122  	return filepath.Join(cgroupKataPath, path)
   123  }
   124  
   125  // return the parent cgroup for the given path
   126  func parentCgroup(hierarchy cgroups.Hierarchy, path string) (cgroups.Cgroup, error) {
   127  	// append '/' just in case CgroupsPath doesn't start with it
   128  	parent := filepath.Dir("/" + path)
   129  
   130  	parentCgroup, err := cgroupsLoadFunc(hierarchy,
   131  		cgroups.StaticPath(parent))
   132  	if err != nil {
   133  		return nil, fmt.Errorf("Could not load parent cgroup %v: %v", parent, err)
   134  	}
   135  
   136  	return parentCgroup, nil
   137  }
   138  
   139  // validCPUResources checks CPU resources coherency
   140  func validCPUResources(cpuSpec *specs.LinuxCPU) *specs.LinuxCPU {
   141  	if cpuSpec == nil {
   142  		return nil
   143  	}
   144  
   145  	cpu := *cpuSpec
   146  	if cpu.Period != nil && *cpu.Period < 1 {
   147  		cpu.Period = nil
   148  	}
   149  
   150  	if cpu.Quota != nil && *cpu.Quota < 1 {
   151  		cpu.Quota = nil
   152  	}
   153  
   154  	if cpu.Shares != nil && *cpu.Shares < 1 {
   155  		cpu.Shares = nil
   156  	}
   157  
   158  	if cpu.RealtimePeriod != nil && *cpu.RealtimePeriod < 1 {
   159  		cpu.RealtimePeriod = nil
   160  	}
   161  
   162  	if cpu.RealtimeRuntime != nil && *cpu.RealtimeRuntime < 1 {
   163  		cpu.RealtimeRuntime = nil
   164  	}
   165  
   166  	return &cpu
   167  }