gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/cgroupfs/memory.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 "math" 21 22 "gvisor.dev/gvisor/pkg/abi/linux" 23 "gvisor.dev/gvisor/pkg/atomicbitops" 24 "gvisor.dev/gvisor/pkg/context" 25 "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" 26 "gvisor.dev/gvisor/pkg/sentry/kernel" 27 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 28 "gvisor.dev/gvisor/pkg/sentry/usage" 29 ) 30 31 // +stateify savable 32 type memoryController struct { 33 controllerCommon 34 controllerNoResource 35 36 limitBytes atomicbitops.Int64 37 softLimitBytes atomicbitops.Int64 38 moveChargeAtImmigrate atomicbitops.Int64 39 pressureLevel int64 40 41 // memCg is the memory cgroup for this controller. 42 memCg *memoryCgroup 43 } 44 45 var _ controller = (*memoryController)(nil) 46 47 func newMemoryController(fs *filesystem, defaults map[string]int64) *memoryController { 48 c := &memoryController{ 49 // Linux sets these limits to (PAGE_COUNTER_MAX * PAGE_SIZE) by default, 50 // which is ~ 2**63 on a 64-bit system. So essentially, infinity. The 51 // exact value isn't very important. 52 53 limitBytes: atomicbitops.FromInt64(math.MaxInt64), 54 softLimitBytes: atomicbitops.FromInt64(math.MaxInt64), 55 } 56 57 consumeDefault := func(name string, valPtr *atomicbitops.Int64) { 58 if val, ok := defaults[name]; ok { 59 valPtr.Store(val) 60 delete(defaults, name) 61 } 62 } 63 64 consumeDefault("memory.limit_in_bytes", &c.limitBytes) 65 consumeDefault("memory.soft_limit_in_bytes", &c.softLimitBytes) 66 consumeDefault("memory.move_charge_at_immigrate", &c.moveChargeAtImmigrate) 67 68 c.controllerCommon.init(kernel.CgroupControllerMemory, fs) 69 return c 70 } 71 72 // Clone implements controller.Clone. 73 func (c *memoryController) Clone() controller { 74 new := &memoryController{ 75 limitBytes: atomicbitops.FromInt64(c.limitBytes.Load()), 76 softLimitBytes: atomicbitops.FromInt64(c.softLimitBytes.Load()), 77 moveChargeAtImmigrate: atomicbitops.FromInt64(c.moveChargeAtImmigrate.Load()), 78 } 79 new.controllerCommon.cloneFromParent(c) 80 return new 81 } 82 83 // AddControlFiles implements controller.AddControlFiles. 84 func (c *memoryController) AddControlFiles(ctx context.Context, creds *auth.Credentials, cg *cgroupInode, contents map[string]kernfs.Inode) { 85 c.memCg = &memoryCgroup{cg} 86 contents["memory.usage_in_bytes"] = c.fs.newControllerFile(ctx, creds, &memoryUsageInBytesData{memCg: &memoryCgroup{cg}}, true) 87 contents["memory.limit_in_bytes"] = c.fs.newStubControllerFile(ctx, creds, &c.limitBytes, true) 88 contents["memory.soft_limit_in_bytes"] = c.fs.newStubControllerFile(ctx, creds, &c.softLimitBytes, true) 89 contents["memory.move_charge_at_immigrate"] = c.fs.newStubControllerFile(ctx, creds, &c.moveChargeAtImmigrate, true) 90 contents["memory.pressure_level"] = c.fs.newStaticControllerFile(ctx, creds, linux.FileMode(0644), fmt.Sprintf("%d\n", c.pressureLevel)) 91 } 92 93 // Enter implements controller.Enter. 94 func (c *memoryController) Enter(t *kernel.Task) { 95 // Update the new cgroup id for the task. 96 t.SetMemCgID(c.memCg.ID()) 97 } 98 99 // Leave implements controller.Leave. 100 func (c *memoryController) Leave(t *kernel.Task) { 101 // Update the cgroup id for the task to zero. 102 t.SetMemCgID(0) 103 } 104 105 // PrepareMigrate implements controller.PrepareMigrate. 106 func (c *memoryController) PrepareMigrate(t *kernel.Task, src controller) error { 107 return nil 108 } 109 110 // CommitMigrate implements controller.CommitMigrate. 111 func (c *memoryController) CommitMigrate(t *kernel.Task, src controller) { 112 // Start tracking t at dst by updating the memCgID. 113 t.SetMemCgID(c.memCg.ID()) 114 } 115 116 // AbortMigrate implements controller.AbortMigrate. 117 func (c *memoryController) AbortMigrate(t *kernel.Task, src controller) {} 118 119 // +stateify savable 120 type memoryCgroup struct { 121 *cgroupInode 122 } 123 124 // Collects all the memory cgroup ids for the cgroup. 125 func (memCg *memoryCgroup) collectMemCgIDs(memCgIDs map[uint32]struct{}) { 126 // Add ourselves. 127 memCgIDs[memCg.ID()] = struct{}{} 128 // Add our children. 129 memCg.forEachChildDir(func(d *dir) { 130 cg := memoryCgroup{d.cgi} 131 cg.collectMemCgIDs(memCgIDs) 132 }) 133 } 134 135 // Returns the memory usage for all cgroup ids in memCgIDs. 136 func getUsage(k *kernel.Kernel, memCgIDs map[uint32]struct{}) uint64 { 137 k.MemoryFile().UpdateUsage(memCgIDs) 138 var totalBytes uint64 139 for id := range memCgIDs { 140 _, bytes := usage.MemoryAccounting.CopyPerCg(id) 141 totalBytes += bytes 142 } 143 return totalBytes 144 } 145 146 // +stateify savable 147 type memoryUsageInBytesData struct { 148 memCg *memoryCgroup 149 } 150 151 // Generate implements vfs.DynamicBytesSource.Generate. 152 func (d *memoryUsageInBytesData) Generate(ctx context.Context, buf *bytes.Buffer) error { 153 k := kernel.KernelFromContext(ctx) 154 155 memCgIDs := make(map[uint32]struct{}) 156 d.memCg.collectMemCgIDs(memCgIDs) 157 totalBytes := getUsage(k, memCgIDs) 158 fmt.Fprintf(buf, "%d\n", totalBytes) 159 return nil 160 }