1// +build linux
2
3package fs
4
5import (
6	"bufio"
7	"os"
8	"path/filepath"
9	"strconv"
10
11	"github.com/opencontainers/runc/libcontainer/cgroups"
12	"github.com/opencontainers/runc/libcontainer/configs"
13)
14
15type CpuGroup struct {
16}
17
18func (s *CpuGroup) Name() string {
19	return "cpu"
20}
21
22func (s *CpuGroup) Apply(d *cgroupData) error {
23	// We always want to join the cpu group, to allow fair cpu scheduling
24	// on a container basis
25	path, err := d.path("cpu")
26	if err != nil && !cgroups.IsNotFound(err) {
27		return err
28	}
29	return s.ApplyDir(path, d.config, d.pid)
30}
31
32func (s *CpuGroup) ApplyDir(path string, cgroup *configs.Cgroup, pid int) error {
33	// This might happen if we have no cpu cgroup mounted.
34	// Just do nothing and don't fail.
35	if path == "" {
36		return nil
37	}
38	if err := os.MkdirAll(path, 0755); err != nil {
39		return err
40	}
41	// We should set the real-Time group scheduling settings before moving
42	// in the process because if the process is already in SCHED_RR mode
43	// and no RT bandwidth is set, adding it will fail.
44	if err := s.SetRtSched(path, cgroup); err != nil {
45		return err
46	}
47	// because we are not using d.join we need to place the pid into the procs file
48	// unlike the other subsystems
49	return cgroups.WriteCgroupProc(path, pid)
50}
51
52func (s *CpuGroup) SetRtSched(path string, cgroup *configs.Cgroup) error {
53	if cgroup.Resources.CpuRtPeriod != 0 {
54		if err := writeFile(path, "cpu.rt_period_us", strconv.FormatUint(cgroup.Resources.CpuRtPeriod, 10)); err != nil {
55			return err
56		}
57	}
58	if cgroup.Resources.CpuRtRuntime != 0 {
59		if err := writeFile(path, "cpu.rt_runtime_us", strconv.FormatInt(cgroup.Resources.CpuRtRuntime, 10)); err != nil {
60			return err
61		}
62	}
63	return nil
64}
65
66func (s *CpuGroup) Set(path string, cgroup *configs.Cgroup) error {
67	if cgroup.Resources.CpuShares != 0 {
68		if err := writeFile(path, "cpu.shares", strconv.FormatUint(cgroup.Resources.CpuShares, 10)); err != nil {
69			return err
70		}
71	}
72	if cgroup.Resources.CpuPeriod != 0 {
73		if err := writeFile(path, "cpu.cfs_period_us", strconv.FormatUint(cgroup.Resources.CpuPeriod, 10)); err != nil {
74			return err
75		}
76	}
77	if cgroup.Resources.CpuQuota != 0 {
78		if err := writeFile(path, "cpu.cfs_quota_us", strconv.FormatInt(cgroup.Resources.CpuQuota, 10)); err != nil {
79			return err
80		}
81	}
82	return s.SetRtSched(path, cgroup)
83}
84
85func (s *CpuGroup) Remove(d *cgroupData) error {
86	return removePath(d.path("cpu"))
87}
88
89func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
90	f, err := os.Open(filepath.Join(path, "cpu.stat"))
91	if err != nil {
92		if os.IsNotExist(err) {
93			return nil
94		}
95		return err
96	}
97	defer f.Close()
98
99	sc := bufio.NewScanner(f)
100	for sc.Scan() {
101		t, v, err := getCgroupParamKeyValue(sc.Text())
102		if err != nil {
103			return err
104		}
105		switch t {
106		case "nr_periods":
107			stats.CpuStats.ThrottlingData.Periods = v
108
109		case "nr_throttled":
110			stats.CpuStats.ThrottlingData.ThrottledPeriods = v
111
112		case "throttled_time":
113			stats.CpuStats.ThrottlingData.ThrottledTime = v
114		}
115	}
116	return nil
117}
118