...

Source file src/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go

Documentation: github.com/opencontainers/runc/libcontainer/cgroups/fs

     1  package fs
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strconv"
     9  
    10  	"github.com/opencontainers/runc/libcontainer/cgroups"
    11  	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
    12  	"github.com/opencontainers/runc/libcontainer/configs"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  type CpuGroup struct{}
    17  
    18  func (s *CpuGroup) Name() string {
    19  	return "cpu"
    20  }
    21  
    22  func (s *CpuGroup) Apply(path string, r *configs.Resources, pid int) error {
    23  	if err := os.MkdirAll(path, 0o755); err != nil {
    24  		return err
    25  	}
    26  	// We should set the real-Time group scheduling settings before moving
    27  	// in the process because if the process is already in SCHED_RR mode
    28  	// and no RT bandwidth is set, adding it will fail.
    29  	if err := s.SetRtSched(path, r); err != nil {
    30  		return err
    31  	}
    32  	// Since we are not using apply(), we need to place the pid
    33  	// into the procs file.
    34  	return cgroups.WriteCgroupProc(path, pid)
    35  }
    36  
    37  func (s *CpuGroup) SetRtSched(path string, r *configs.Resources) error {
    38  	if r.CpuRtPeriod != 0 {
    39  		if err := cgroups.WriteFile(path, "cpu.rt_period_us", strconv.FormatUint(r.CpuRtPeriod, 10)); err != nil {
    40  			return err
    41  		}
    42  	}
    43  	if r.CpuRtRuntime != 0 {
    44  		if err := cgroups.WriteFile(path, "cpu.rt_runtime_us", strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil {
    45  			return err
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  func (s *CpuGroup) Set(path string, r *configs.Resources) error {
    52  	if r.CpuShares != 0 {
    53  		shares := r.CpuShares
    54  		if err := cgroups.WriteFile(path, "cpu.shares", strconv.FormatUint(shares, 10)); err != nil {
    55  			return err
    56  		}
    57  		// read it back
    58  		sharesRead, err := fscommon.GetCgroupParamUint(path, "cpu.shares")
    59  		if err != nil {
    60  			return err
    61  		}
    62  		// ... and check
    63  		if shares > sharesRead {
    64  			return fmt.Errorf("the maximum allowed cpu-shares is %d", sharesRead)
    65  		} else if shares < sharesRead {
    66  			return fmt.Errorf("the minimum allowed cpu-shares is %d", sharesRead)
    67  		}
    68  	}
    69  
    70  	var period string
    71  	if r.CpuPeriod != 0 {
    72  		period = strconv.FormatUint(r.CpuPeriod, 10)
    73  		if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
    74  			// Sometimes when the period to be set is smaller
    75  			// than the current one, it is rejected by the kernel
    76  			// (EINVAL) as old_quota/new_period exceeds the parent
    77  			// cgroup quota limit. If this happens and the quota is
    78  			// going to be set, ignore the error for now and retry
    79  			// after setting the quota.
    80  			if !errors.Is(err, unix.EINVAL) || r.CpuQuota == 0 {
    81  				return err
    82  			}
    83  		} else {
    84  			period = ""
    85  		}
    86  	}
    87  	if r.CpuQuota != 0 {
    88  		if err := cgroups.WriteFile(path, "cpu.cfs_quota_us", strconv.FormatInt(r.CpuQuota, 10)); err != nil {
    89  			return err
    90  		}
    91  		if period != "" {
    92  			if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
    93  				return err
    94  			}
    95  		}
    96  	}
    97  	return s.SetRtSched(path, r)
    98  }
    99  
   100  func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
   101  	const file = "cpu.stat"
   102  	f, err := cgroups.OpenFile(path, file, os.O_RDONLY)
   103  	if err != nil {
   104  		if os.IsNotExist(err) {
   105  			return nil
   106  		}
   107  		return err
   108  	}
   109  	defer f.Close()
   110  
   111  	sc := bufio.NewScanner(f)
   112  	for sc.Scan() {
   113  		t, v, err := fscommon.ParseKeyValue(sc.Text())
   114  		if err != nil {
   115  			return &parseError{Path: path, File: file, Err: err}
   116  		}
   117  		switch t {
   118  		case "nr_periods":
   119  			stats.CpuStats.ThrottlingData.Periods = v
   120  
   121  		case "nr_throttled":
   122  			stats.CpuStats.ThrottlingData.ThrottledPeriods = v
   123  
   124  		case "throttled_time":
   125  			stats.CpuStats.ThrottlingData.ThrottledTime = v
   126  		}
   127  	}
   128  	return nil
   129  }
   130  

View as plain text