github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/cgroupfs/cpuset.go (about) 1 // Copyright 2021 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cgroupfs 16 17 import ( 18 "bytes" 19 "fmt" 20 21 "github.com/nicocha30/gvisor-ligolo/pkg/bitmap" 22 "github.com/nicocha30/gvisor-ligolo/pkg/context" 23 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 24 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 25 "github.com/nicocha30/gvisor-ligolo/pkg/log" 26 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsimpl/kernfs" 27 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 28 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 29 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 30 "github.com/nicocha30/gvisor-ligolo/pkg/sync" 31 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 32 ) 33 34 // +stateify savable 35 type cpusetController struct { 36 controllerCommon 37 controllerStateless 38 controllerNoResource 39 40 maxCpus uint32 41 maxMems uint32 42 43 mu sync.Mutex `state:"nosave"` 44 45 cpus *bitmap.Bitmap 46 mems *bitmap.Bitmap 47 } 48 49 var _ controller = (*cpusetController)(nil) 50 51 func newCPUSetController(k *kernel.Kernel, fs *filesystem) *cpusetController { 52 cores := uint32(k.ApplicationCores()) 53 cpus := bitmap.New(cores) 54 cpus.FlipRange(0, cores) 55 mems := bitmap.New(1) 56 mems.FlipRange(0, 1) 57 c := &cpusetController{ 58 cpus: &cpus, 59 mems: &mems, 60 maxCpus: uint32(k.ApplicationCores()), 61 maxMems: 1, // We always report a single NUMA node. 62 } 63 c.controllerCommon.init(kernel.CgroupControllerCPUSet, fs) 64 return c 65 } 66 67 // Clone implements controller.Clone. 68 func (c *cpusetController) Clone() controller { 69 c.mu.Lock() 70 defer c.mu.Unlock() 71 cpus := c.cpus.Clone() 72 mems := c.mems.Clone() 73 new := &cpusetController{ 74 maxCpus: c.maxCpus, 75 maxMems: c.maxMems, 76 cpus: &cpus, 77 mems: &mems, 78 } 79 new.controllerCommon.cloneFromParent(c) 80 return new 81 } 82 83 // AddControlFiles implements controller.AddControlFiles. 84 func (c *cpusetController) AddControlFiles(ctx context.Context, creds *auth.Credentials, _ *cgroupInode, contents map[string]kernfs.Inode) { 85 contents["cpuset.cpus"] = c.fs.newControllerWritableFile(ctx, creds, &cpusData{c: c}, true) 86 contents["cpuset.mems"] = c.fs.newControllerWritableFile(ctx, creds, &memsData{c: c}, true) 87 } 88 89 // +stateify savable 90 type cpusData struct { 91 c *cpusetController 92 } 93 94 // Generate implements vfs.DynamicBytesSource.Generate. 95 func (d *cpusData) Generate(ctx context.Context, buf *bytes.Buffer) error { 96 d.c.mu.Lock() 97 defer d.c.mu.Unlock() 98 fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.cpus)) 99 return nil 100 } 101 102 // Write implements vfs.WritableDynamicBytesSource.Write. 103 func (d *cpusData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 104 return d.WriteBackground(ctx, src) 105 } 106 107 // WriteBackground implements writableControllerFileImpl.WriteBackground. 108 func (d *cpusData) WriteBackground(ctx context.Context, src usermem.IOSequence) (int64, error) { 109 if src.NumBytes() > hostarch.PageSize { 110 return 0, linuxerr.EINVAL 111 } 112 113 buf := copyScratchBufferFromContext(ctx, hostarch.PageSize) 114 n, err := src.CopyIn(ctx, buf) 115 if err != nil { 116 return 0, err 117 } 118 buf = buf[:n] 119 120 b, err := parseBitmap(string(buf), d.c.maxCpus) 121 if err != nil { 122 log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err) 123 return 0, linuxerr.EINVAL 124 } 125 126 if got, want := b.Maximum(), d.c.maxCpus; got > want { 127 log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.cpus beyond highest available cpu: got %d, want %d", got, want) 128 return 0, linuxerr.EINVAL 129 } 130 131 d.c.mu.Lock() 132 defer d.c.mu.Unlock() 133 d.c.cpus = b 134 return int64(n), nil 135 } 136 137 // +stateify savable 138 type memsData struct { 139 c *cpusetController 140 } 141 142 // Generate implements vfs.DynamicBytesSource.Generate. 143 func (d *memsData) Generate(ctx context.Context, buf *bytes.Buffer) error { 144 d.c.mu.Lock() 145 defer d.c.mu.Unlock() 146 fmt.Fprintf(buf, "%s\n", formatBitmap(d.c.mems)) 147 return nil 148 } 149 150 // Write implements vfs.WritableDynamicBytesSource.Write. 151 func (d *memsData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 152 return d.WriteBackground(ctx, src) 153 } 154 155 // WriteBackground implements writableControllerFileImpl.WriteBackground. 156 func (d *memsData) WriteBackground(ctx context.Context, src usermem.IOSequence) (int64, error) { 157 if src.NumBytes() > hostarch.PageSize { 158 return 0, linuxerr.EINVAL 159 } 160 161 buf := copyScratchBufferFromContext(ctx, hostarch.PageSize) 162 n, err := src.CopyIn(ctx, buf) 163 if err != nil { 164 return 0, err 165 } 166 buf = buf[:n] 167 168 b, err := parseBitmap(string(buf), d.c.maxMems) 169 if err != nil { 170 log.Warningf("cgroupfs cpuset controller: Failed to parse bitmap: %v", err) 171 return 0, linuxerr.EINVAL 172 } 173 174 if got, want := b.Maximum(), d.c.maxMems; got > want { 175 log.Warningf("cgroupfs cpuset controller: Attempted to specify cpuset.mems beyond highest available node: got %d, want %d", got, want) 176 return 0, linuxerr.EINVAL 177 } 178 179 d.c.mu.Lock() 180 defer d.c.mu.Unlock() 181 d.c.mems = b 182 return int64(n), nil 183 }