These PR does a few things. It ensures that the freezer cgroup is
joined in the systemd driver. It also provides a public api for setting
the freezer state via the cgroups package.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
| ... | ... |
@@ -10,6 +10,14 @@ var ( |
| 10 | 10 |
ErrNotFound = errors.New("mountpoint not found")
|
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 |
+type FreezerState string |
|
| 14 |
+ |
|
| 15 |
+const ( |
|
| 16 |
+ Undefined FreezerState = "" |
|
| 17 |
+ Frozen FreezerState = "FROZEN" |
|
| 18 |
+ Thawed FreezerState = "THAWED" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 13 | 21 |
type Cgroup struct {
|
| 14 | 22 |
Name string `json:"name,omitempty"` |
| 15 | 23 |
Parent string `json:"parent,omitempty"` // name of parent cgroup or slice |
| ... | ... |
@@ -23,9 +31,8 @@ type Cgroup struct {
|
| 23 | 23 |
CpuQuota int64 `json:"cpu_quota,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. |
| 24 | 24 |
CpuPeriod int64 `json:"cpu_period,omitempty"` // CPU period to be used for hardcapping (in usecs). 0 to use system default. |
| 25 | 25 |
CpusetCpus string `json:"cpuset_cpus,omitempty"` // CPU to use |
| 26 |
- Freezer string `json:"freezer,omitempty"` // set the freeze value for the process |
|
| 27 |
- |
|
| 28 |
- Slice string `json:"slice,omitempty"` // Parent slice to use for systemd |
|
| 26 |
+ Freezer FreezerState `json:"freezer,omitempty"` // set the freeze value for the process |
|
| 27 |
+ Slice string `json:"slice,omitempty"` // Parent slice to use for systemd |
|
| 29 | 28 |
} |
| 30 | 29 |
|
| 31 | 30 |
type ActiveCgroup interface {
|
| ... | ... |
@@ -37,65 +37,28 @@ type data struct {
|
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 | 39 |
func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 40 |
- // We have two implementation of cgroups support, one is based on |
|
| 41 |
- // systemd and the dbus api, and one is based on raw cgroup fs operations |
|
| 42 |
- // following the pre-single-writer model docs at: |
|
| 43 |
- // http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/ |
|
| 44 |
- // |
|
| 45 |
- // we can pick any subsystem to find the root |
|
| 46 |
- |
|
| 47 |
- cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
|
| 40 |
+ d, err := getCgroupData(c, pid) |
|
| 48 | 41 |
if err != nil {
|
| 49 | 42 |
return nil, err |
| 50 | 43 |
} |
| 51 |
- cgroupRoot = filepath.Dir(cgroupRoot) |
|
| 52 | 44 |
|
| 53 |
- if _, err := os.Stat(cgroupRoot); err != nil {
|
|
| 54 |
- return nil, fmt.Errorf("cgroups fs not found")
|
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- cgroup := c.Name |
|
| 58 |
- if c.Parent != "" {
|
|
| 59 |
- cgroup = filepath.Join(c.Parent, cgroup) |
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- d := &data{
|
|
| 63 |
- root: cgroupRoot, |
|
| 64 |
- cgroup: cgroup, |
|
| 65 |
- c: c, |
|
| 66 |
- pid: pid, |
|
| 67 |
- } |
|
| 68 | 45 |
for _, sys := range subsystems {
|
| 69 | 46 |
if err := sys.Set(d); err != nil {
|
| 70 | 47 |
d.Cleanup() |
| 71 | 48 |
return nil, err |
| 72 | 49 |
} |
| 73 | 50 |
} |
| 51 |
+ |
|
| 74 | 52 |
return d, nil |
| 75 | 53 |
} |
| 76 | 54 |
|
| 77 | 55 |
func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
| 78 | 56 |
stats := cgroups.NewStats() |
| 79 |
- cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
|
| 57 |
+ |
|
| 58 |
+ d, err := getCgroupData(c, 0) |
|
| 80 | 59 |
if err != nil {
|
| 81 | 60 |
return nil, err |
| 82 | 61 |
} |
| 83 |
- cgroupRoot = filepath.Dir(cgroupRoot) |
|
| 84 |
- |
|
| 85 |
- if _, err := os.Stat(cgroupRoot); err != nil {
|
|
| 86 |
- return nil, fmt.Errorf("cgroups fs not found")
|
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- cgroup := c.Name |
|
| 90 |
- if c.Parent != "" {
|
|
| 91 |
- cgroup = filepath.Join(c.Parent, cgroup) |
|
| 92 |
- } |
|
| 93 |
- |
|
| 94 |
- d := &data{
|
|
| 95 |
- root: cgroupRoot, |
|
| 96 |
- cgroup: cgroup, |
|
| 97 |
- c: c, |
|
| 98 |
- } |
|
| 99 | 62 |
|
| 100 | 63 |
for _, sys := range subsystems {
|
| 101 | 64 |
if err := sys.GetStats(d, stats); err != nil {
|
| ... | ... |
@@ -106,7 +69,37 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
|
| 106 | 106 |
return stats, nil |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
+// Freeze toggles the container's freezer cgroup depending on the state |
|
| 110 |
+// provided |
|
| 111 |
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
|
| 112 |
+ d, err := getCgroupData(c, 0) |
|
| 113 |
+ if err != nil {
|
|
| 114 |
+ return err |
|
| 115 |
+ } |
|
| 116 |
+ |
|
| 117 |
+ c.Freezer = state |
|
| 118 |
+ |
|
| 119 |
+ freezer := subsystems["freezer"] |
|
| 120 |
+ |
|
| 121 |
+ return freezer.Set(d) |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 109 | 124 |
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| 125 |
+ d, err := getCgroupData(c, 0) |
|
| 126 |
+ if err != nil {
|
|
| 127 |
+ return nil, err |
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ dir, err := d.path("devices")
|
|
| 131 |
+ if err != nil {
|
|
| 132 |
+ return nil, err |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ return cgroups.ReadProcsFile(dir) |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) {
|
|
| 139 |
+ // we can pick any subsystem to find the root |
|
| 110 | 140 |
cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
|
| 111 | 141 |
if err != nil {
|
| 112 | 142 |
return nil, err |
| ... | ... |
@@ -114,7 +107,7 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| 114 | 114 |
cgroupRoot = filepath.Dir(cgroupRoot) |
| 115 | 115 |
|
| 116 | 116 |
if _, err := os.Stat(cgroupRoot); err != nil {
|
| 117 |
- return nil, fmt.Errorf("cgroup root %s not found", cgroupRoot)
|
|
| 117 |
+ return nil, fmt.Errorf("cgroups fs not found")
|
|
| 118 | 118 |
} |
| 119 | 119 |
|
| 120 | 120 |
cgroup := c.Name |
| ... | ... |
@@ -122,18 +115,12 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| 122 | 122 |
cgroup = filepath.Join(c.Parent, cgroup) |
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 |
- d := &data{
|
|
| 125 |
+ return &data{
|
|
| 126 | 126 |
root: cgroupRoot, |
| 127 | 127 |
cgroup: cgroup, |
| 128 | 128 |
c: c, |
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- dir, err := d.path("devices")
|
|
| 132 |
- if err != nil {
|
|
| 133 |
- return nil, err |
|
| 134 |
- } |
|
| 135 |
- |
|
| 136 |
- return cgroups.ReadProcsFile(dir) |
|
| 129 |
+ pid: pid, |
|
| 130 |
+ }, nil |
|
| 137 | 131 |
} |
| 138 | 132 |
|
| 139 | 133 |
func (raw *data) parent(subsystem string) (string, error) {
|
| ... | ... |
@@ -20,11 +20,12 @@ func (s *freezerGroup) Set(d *data) error {
|
| 20 | 20 |
return nil |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- if d.c.Freezer != "" {
|
|
| 24 |
- if err := writeFile(dir, "freezer.state", d.c.Freezer); err != nil {
|
|
| 23 |
+ if d.c.Freezer != cgroups.Undefined {
|
|
| 24 |
+ if err := writeFile(dir, "freezer.state", string(d.c.Freezer)); err != nil {
|
|
| 25 | 25 |
return err |
| 26 | 26 |
} |
| 27 | 27 |
} |
| 28 |
+ |
|
| 28 | 29 |
return nil |
| 29 | 30 |
} |
| 30 | 31 |
|
| ... | ... |
@@ -19,3 +19,7 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 19 | 19 |
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| 20 | 20 |
return nil, fmt.Errorf("Systemd not supported")
|
| 21 | 21 |
} |
| 22 |
+ |
|
| 23 |
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
|
| 24 |
+ return fmt.Errorf("Systemd not supported")
|
|
| 25 |
+} |
| ... | ... |
@@ -218,6 +218,14 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 218 | 218 |
} |
| 219 | 219 |
} |
| 220 | 220 |
|
| 221 |
+ // we need to manually join the freezer cgroup in systemd because it does not currently support it |
|
| 222 |
+ // via the dbus api |
|
| 223 |
+ freezerPath, err := joinFreezer(c, pid) |
|
| 224 |
+ if err != nil {
|
|
| 225 |
+ return nil, err |
|
| 226 |
+ } |
|
| 227 |
+ res.cleanupDirs = append(res.cleanupDirs, freezerPath) |
|
| 228 |
+ |
|
| 221 | 229 |
if len(cpusetArgs) != 0 {
|
| 222 | 230 |
// systemd does not atm set up the cpuset controller, so we must manually |
| 223 | 231 |
// join it. Additionally that is a very finicky controller where each |
| ... | ... |
@@ -227,14 +235,19 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 227 | 227 |
if err != nil {
|
| 228 | 228 |
return nil, err |
| 229 | 229 |
} |
| 230 |
+ |
|
| 230 | 231 |
initPath, err := cgroups.GetInitCgroupDir("cpuset")
|
| 231 | 232 |
if err != nil {
|
| 232 | 233 |
return nil, err |
| 233 | 234 |
} |
| 234 | 235 |
|
| 235 |
- rootPath := filepath.Join(mountpoint, initPath) |
|
| 236 |
+ var ( |
|
| 237 |
+ foundCpus bool |
|
| 238 |
+ foundMems bool |
|
| 236 | 239 |
|
| 237 |
- path := filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name) |
|
| 240 |
+ rootPath = filepath.Join(mountpoint, initPath) |
|
| 241 |
+ path = filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name) |
|
| 242 |
+ ) |
|
| 238 | 243 |
|
| 239 | 244 |
res.cleanupDirs = append(res.cleanupDirs, path) |
| 240 | 245 |
|
| ... | ... |
@@ -242,9 +255,6 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
| 242 | 242 |
return nil, err |
| 243 | 243 |
} |
| 244 | 244 |
|
| 245 |
- foundCpus := false |
|
| 246 |
- foundMems := false |
|
| 247 |
- |
|
| 248 | 245 |
for _, arg := range cpusetArgs {
|
| 249 | 246 |
if arg.File == "cpuset.cpus" {
|
| 250 | 247 |
foundCpus = true |
| ... | ... |
@@ -303,6 +313,47 @@ func (c *systemdCgroup) Cleanup() error {
|
| 303 | 303 |
return nil |
| 304 | 304 |
} |
| 305 | 305 |
|
| 306 |
+func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
|
|
| 307 |
+ path, err := getFreezerPath(c) |
|
| 308 |
+ if err != nil {
|
|
| 309 |
+ return "", err |
|
| 310 |
+ } |
|
| 311 |
+ |
|
| 312 |
+ if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
|
|
| 313 |
+ return "", err |
|
| 314 |
+ } |
|
| 315 |
+ |
|
| 316 |
+ if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
|
|
| 317 |
+ return "", err |
|
| 318 |
+ } |
|
| 319 |
+ |
|
| 320 |
+ return path, nil |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+func getFreezerPath(c *cgroups.Cgroup) (string, error) {
|
|
| 324 |
+ mountpoint, err := cgroups.FindCgroupMountpoint("freezer")
|
|
| 325 |
+ if err != nil {
|
|
| 326 |
+ return "", err |
|
| 327 |
+ } |
|
| 328 |
+ |
|
| 329 |
+ initPath, err := cgroups.GetInitCgroupDir("freezer")
|
|
| 330 |
+ if err != nil {
|
|
| 331 |
+ return "", err |
|
| 332 |
+ } |
|
| 333 |
+ |
|
| 334 |
+ return filepath.Join(mountpoint, initPath, fmt.Sprintf("%s-%s", c.Parent, c.Name)), nil
|
|
| 335 |
+ |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
|
|
| 339 |
+ path, err := getFreezerPath(c) |
|
| 340 |
+ if err != nil {
|
|
| 341 |
+ return err |
|
| 342 |
+ } |
|
| 343 |
+ |
|
| 344 |
+ return ioutil.WriteFile(filepath.Join(path, "freezer.state"), []byte(state), 0) |
|
| 345 |
+} |
|
| 346 |
+ |
|
| 306 | 347 |
func GetPids(c *cgroups.Cgroup) ([]int, error) {
|
| 307 | 348 |
unitName := getUnitName(c) |
| 308 | 349 |
|