cpuset.go (3901B)
1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cgroup1 18 19 import ( 20 "bytes" 21 "fmt" 22 "os" 23 "path/filepath" 24 25 specs "github.com/opencontainers/runtime-spec/specs-go" 26 ) 27 28 func NewCpuset(root string) *cpusetController { 29 return &cpusetController{ 30 root: filepath.Join(root, string(Cpuset)), 31 } 32 } 33 34 type cpusetController struct { 35 root string 36 } 37 38 func (c *cpusetController) Name() Name { 39 return Cpuset 40 } 41 42 func (c *cpusetController) Path(path string) string { 43 return filepath.Join(c.root, path) 44 } 45 46 func (c *cpusetController) Create(path string, resources *specs.LinuxResources) error { 47 if err := c.ensureParent(c.Path(path), c.root); err != nil { 48 return err 49 } 50 if err := os.MkdirAll(c.Path(path), defaultDirPerm); err != nil { 51 return err 52 } 53 if err := c.copyIfNeeded(c.Path(path), filepath.Dir(c.Path(path))); err != nil { 54 return err 55 } 56 if resources.CPU != nil { 57 for _, t := range []struct { 58 name string 59 value string 60 }{ 61 { 62 name: "cpus", 63 value: resources.CPU.Cpus, 64 }, 65 { 66 name: "mems", 67 value: resources.CPU.Mems, 68 }, 69 } { 70 if t.value != "" { 71 if err := os.WriteFile( 72 filepath.Join(c.Path(path), "cpuset."+t.name), 73 []byte(t.value), 74 defaultFilePerm, 75 ); err != nil { 76 return err 77 } 78 } 79 } 80 } 81 return nil 82 } 83 84 func (c *cpusetController) Update(path string, resources *specs.LinuxResources) error { 85 return c.Create(path, resources) 86 } 87 88 func (c *cpusetController) getValues(path string) (cpus []byte, mems []byte, err error) { 89 if cpus, err = os.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) { 90 return 91 } 92 if mems, err = os.ReadFile(filepath.Join(path, "cpuset.mems")); err != nil && !os.IsNotExist(err) { 93 return 94 } 95 return cpus, mems, nil 96 } 97 98 // ensureParent makes sure that the parent directory of current is created 99 // and populated with the proper cpus and mems files copied from 100 // it's parent. 101 func (c *cpusetController) ensureParent(current, root string) error { 102 parent := filepath.Dir(current) 103 if _, err := filepath.Rel(root, parent); err != nil { 104 return nil 105 } 106 // Avoid infinite recursion. 107 if parent == current { 108 return fmt.Errorf("cpuset: cgroup parent path outside cgroup root") 109 } 110 if cleanPath(parent) != root { 111 if err := c.ensureParent(parent, root); err != nil { 112 return err 113 } 114 } 115 if err := os.MkdirAll(current, defaultDirPerm); err != nil { 116 return err 117 } 118 return c.copyIfNeeded(current, parent) 119 } 120 121 // copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent 122 // directory to the current directory if the file's contents are 0 123 func (c *cpusetController) copyIfNeeded(current, parent string) error { 124 var ( 125 err error 126 currentCpus, currentMems []byte 127 parentCpus, parentMems []byte 128 ) 129 if currentCpus, currentMems, err = c.getValues(current); err != nil { 130 return err 131 } 132 if parentCpus, parentMems, err = c.getValues(parent); err != nil { 133 return err 134 } 135 if isEmpty(currentCpus) { 136 if err := os.WriteFile( 137 filepath.Join(current, "cpuset.cpus"), 138 parentCpus, 139 defaultFilePerm, 140 ); err != nil { 141 return err 142 } 143 } 144 if isEmpty(currentMems) { 145 if err := os.WriteFile( 146 filepath.Join(current, "cpuset.mems"), 147 parentMems, 148 defaultFilePerm, 149 ); err != nil { 150 return err 151 } 152 } 153 return nil 154 } 155 156 func isEmpty(b []byte) bool { 157 return len(bytes.Trim(b, "\n")) == 0 158 }