diff options
Diffstat (limited to 'pkg/sentry/fsimpl/cgroupfs')
-rw-r--r-- | pkg/sentry/fsimpl/cgroupfs/BUILD | 60 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/cgroupfs/bitmap_test.go | 99 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/cgroupfs/cgroupfs_state_autogen.go | 766 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/cgroupfs/dir_refs.go | 140 |
4 files changed, 906 insertions, 159 deletions
diff --git a/pkg/sentry/fsimpl/cgroupfs/BUILD b/pkg/sentry/fsimpl/cgroupfs/BUILD deleted file mode 100644 index 60ee5ede2..000000000 --- a/pkg/sentry/fsimpl/cgroupfs/BUILD +++ /dev/null @@ -1,60 +0,0 @@ -load("//tools:defs.bzl", "go_library", "go_test") -load("//tools/go_generics:defs.bzl", "go_template_instance") - -licenses(["notice"]) - -go_template_instance( - name = "dir_refs", - out = "dir_refs.go", - package = "cgroupfs", - prefix = "dir", - template = "//pkg/refsvfs2:refs_template", - types = { - "T": "dir", - }, -) - -go_library( - name = "cgroupfs", - srcs = [ - "base.go", - "bitmap.go", - "cgroupfs.go", - "cpu.go", - "cpuacct.go", - "cpuset.go", - "dir_refs.go", - "job.go", - "memory.go", - ], - visibility = ["//pkg/sentry:internal"], - deps = [ - "//pkg/abi/linux", - "//pkg/bitmap", - "//pkg/context", - "//pkg/coverage", - "//pkg/errors/linuxerr", - "//pkg/fspath", - "//pkg/hostarch", - "//pkg/log", - "//pkg/refs", - "//pkg/refsvfs2", - "//pkg/sentry/arch", - "//pkg/sentry/fsimpl/kernfs", - "//pkg/sentry/kernel", - "//pkg/sentry/kernel/auth", - "//pkg/sentry/memmap", - "//pkg/sentry/usage", - "//pkg/sentry/vfs", - "//pkg/sync", - "//pkg/usermem", - ], -) - -go_test( - name = "cgroupfs_test", - size = "small", - srcs = ["bitmap_test.go"], - library = ":cgroupfs", - deps = ["//pkg/bitmap"], -) diff --git a/pkg/sentry/fsimpl/cgroupfs/bitmap_test.go b/pkg/sentry/fsimpl/cgroupfs/bitmap_test.go deleted file mode 100644 index 5cc56de3b..000000000 --- a/pkg/sentry/fsimpl/cgroupfs/bitmap_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2021 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cgroupfs - -import ( - "fmt" - "reflect" - "testing" - - "gvisor.dev/gvisor/pkg/bitmap" -) - -func TestFormat(t *testing.T) { - tests := []struct { - input []uint32 - output string - }{ - {[]uint32{1, 2, 3, 4, 7}, "1-4,7"}, - {[]uint32{2}, "2"}, - {[]uint32{0, 1, 2}, "0-2"}, - {[]uint32{}, ""}, - {[]uint32{1, 3, 4, 5, 6, 9, 11, 13, 14, 15, 16, 17}, "1,3-6,9,11,13-17"}, - {[]uint32{2, 3, 10, 12, 13, 14, 15, 16, 20, 21, 33, 34, 47}, "2-3,10,12-16,20-21,33-34,47"}, - } - for i, tt := range tests { - t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { - b := bitmap.New(64) - for _, v := range tt.input { - b.Add(v) - } - s := formatBitmap(&b) - if s != tt.output { - t.Errorf("Expected %q, got %q", tt.output, s) - } - b1, err := parseBitmap(s, 64) - if err != nil { - t.Fatalf("Failed to parse formatted bitmap: %v", err) - } - if got, want := b1.ToSlice(), b.ToSlice(); !reflect.DeepEqual(got, want) { - t.Errorf("Parsing formatted output doesn't result in the original bitmap. Got %v, want %v", got, want) - } - }) - } -} - -func TestParse(t *testing.T) { - tests := []struct { - input string - output []uint32 - shouldFail bool - }{ - {"1", []uint32{1}, false}, - {"", []uint32{}, false}, - {"1,2,3,4", []uint32{1, 2, 3, 4}, false}, - {"1-4", []uint32{1, 2, 3, 4}, false}, - {"1,2-4", []uint32{1, 2, 3, 4}, false}, - {"1,2-3,4", []uint32{1, 2, 3, 4}, false}, - {"1-2,3,4,10,11", []uint32{1, 2, 3, 4, 10, 11}, false}, - {"1,2-4,5,16", []uint32{1, 2, 3, 4, 5, 16}, false}, - {"abc", []uint32{}, true}, - {"1,3-2,4", []uint32{}, true}, - {"1,3-3,4", []uint32{}, true}, - {"1,2,3\000,4", []uint32{1, 2, 3}, false}, - {"1,2,3\n,4", []uint32{1, 2, 3}, false}, - } - for i, tt := range tests { - t.Run(fmt.Sprintf("case-%d", i), func(t *testing.T) { - b, err := parseBitmap(tt.input, 64) - if tt.shouldFail { - if err == nil { - t.Fatalf("Expected parsing of %q to fail, but it didn't", tt.input) - } - return - } - if err != nil { - t.Fatalf("Failed to parse bitmap: %v", err) - return - } - - got := b.ToSlice() - if !reflect.DeepEqual(got, tt.output) { - t.Errorf("Parsed bitmap doesn't match what we expected. Got %v, want %v", got, tt.output) - } - - }) - } -} diff --git a/pkg/sentry/fsimpl/cgroupfs/cgroupfs_state_autogen.go b/pkg/sentry/fsimpl/cgroupfs/cgroupfs_state_autogen.go new file mode 100644 index 000000000..47ddb54c5 --- /dev/null +++ b/pkg/sentry/fsimpl/cgroupfs/cgroupfs_state_autogen.go @@ -0,0 +1,766 @@ +// automatically generated by stateify. + +package cgroupfs + +import ( + "gvisor.dev/gvisor/pkg/state" +) + +func (c *controllerCommon) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.controllerCommon" +} + +func (c *controllerCommon) StateFields() []string { + return []string{ + "ty", + "fs", + } +} + +func (c *controllerCommon) beforeSave() {} + +// +checklocksignore +func (c *controllerCommon) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.ty) + stateSinkObject.Save(1, &c.fs) +} + +func (c *controllerCommon) afterLoad() {} + +// +checklocksignore +func (c *controllerCommon) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.ty) + stateSourceObject.Load(1, &c.fs) +} + +func (c *cgroupInode) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cgroupInode" +} + +func (c *cgroupInode) StateFields() []string { + return []string{ + "dir", + "ts", + } +} + +func (c *cgroupInode) beforeSave() {} + +// +checklocksignore +func (c *cgroupInode) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.dir) + stateSinkObject.Save(1, &c.ts) +} + +func (c *cgroupInode) afterLoad() {} + +// +checklocksignore +func (c *cgroupInode) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.dir) + stateSourceObject.Load(1, &c.ts) +} + +func (d *cgroupProcsData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cgroupProcsData" +} + +func (d *cgroupProcsData) StateFields() []string { + return []string{ + "cgroupInode", + } +} + +func (d *cgroupProcsData) beforeSave() {} + +// +checklocksignore +func (d *cgroupProcsData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cgroupInode) +} + +func (d *cgroupProcsData) afterLoad() {} + +// +checklocksignore +func (d *cgroupProcsData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cgroupInode) +} + +func (d *tasksData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.tasksData" +} + +func (d *tasksData) StateFields() []string { + return []string{ + "cgroupInode", + } +} + +func (d *tasksData) beforeSave() {} + +// +checklocksignore +func (d *tasksData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cgroupInode) +} + +func (d *tasksData) afterLoad() {} + +// +checklocksignore +func (d *tasksData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cgroupInode) +} + +func (fsType *FilesystemType) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.FilesystemType" +} + +func (fsType *FilesystemType) StateFields() []string { + return []string{} +} + +func (fsType *FilesystemType) beforeSave() {} + +// +checklocksignore +func (fsType *FilesystemType) StateSave(stateSinkObject state.Sink) { + fsType.beforeSave() +} + +func (fsType *FilesystemType) afterLoad() {} + +// +checklocksignore +func (fsType *FilesystemType) StateLoad(stateSourceObject state.Source) { +} + +func (i *InternalData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.InternalData" +} + +func (i *InternalData) StateFields() []string { + return []string{ + "DefaultControlValues", + "InitialCgroupPath", + } +} + +func (i *InternalData) beforeSave() {} + +// +checklocksignore +func (i *InternalData) StateSave(stateSinkObject state.Sink) { + i.beforeSave() + stateSinkObject.Save(0, &i.DefaultControlValues) + stateSinkObject.Save(1, &i.InitialCgroupPath) +} + +func (i *InternalData) afterLoad() {} + +// +checklocksignore +func (i *InternalData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &i.DefaultControlValues) + stateSourceObject.Load(1, &i.InitialCgroupPath) +} + +func (fs *filesystem) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.filesystem" +} + +func (fs *filesystem) StateFields() []string { + return []string{ + "Filesystem", + "devMinor", + "hierarchyID", + "controllers", + "kcontrollers", + "numCgroups", + "root", + "effectiveRoot", + } +} + +func (fs *filesystem) beforeSave() {} + +// +checklocksignore +func (fs *filesystem) StateSave(stateSinkObject state.Sink) { + fs.beforeSave() + stateSinkObject.Save(0, &fs.Filesystem) + stateSinkObject.Save(1, &fs.devMinor) + stateSinkObject.Save(2, &fs.hierarchyID) + stateSinkObject.Save(3, &fs.controllers) + stateSinkObject.Save(4, &fs.kcontrollers) + stateSinkObject.Save(5, &fs.numCgroups) + stateSinkObject.Save(6, &fs.root) + stateSinkObject.Save(7, &fs.effectiveRoot) +} + +func (fs *filesystem) afterLoad() {} + +// +checklocksignore +func (fs *filesystem) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &fs.Filesystem) + stateSourceObject.Load(1, &fs.devMinor) + stateSourceObject.Load(2, &fs.hierarchyID) + stateSourceObject.Load(3, &fs.controllers) + stateSourceObject.Load(4, &fs.kcontrollers) + stateSourceObject.Load(5, &fs.numCgroups) + stateSourceObject.Load(6, &fs.root) + stateSourceObject.Load(7, &fs.effectiveRoot) +} + +func (i *implStatFS) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.implStatFS" +} + +func (i *implStatFS) StateFields() []string { + return []string{} +} + +func (i *implStatFS) beforeSave() {} + +// +checklocksignore +func (i *implStatFS) StateSave(stateSinkObject state.Sink) { + i.beforeSave() +} + +func (i *implStatFS) afterLoad() {} + +// +checklocksignore +func (i *implStatFS) StateLoad(stateSourceObject state.Source) { +} + +func (d *dir) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.dir" +} + +func (d *dir) StateFields() []string { + return []string{ + "InodeNoopRefCount", + "InodeAlwaysValid", + "InodeAttrs", + "InodeNotSymlink", + "InodeDirectoryNoNewChildren", + "OrderedChildren", + "implStatFS", + "locks", + "fs", + "cgi", + } +} + +func (d *dir) beforeSave() {} + +// +checklocksignore +func (d *dir) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.InodeNoopRefCount) + stateSinkObject.Save(1, &d.InodeAlwaysValid) + stateSinkObject.Save(2, &d.InodeAttrs) + stateSinkObject.Save(3, &d.InodeNotSymlink) + stateSinkObject.Save(4, &d.InodeDirectoryNoNewChildren) + stateSinkObject.Save(5, &d.OrderedChildren) + stateSinkObject.Save(6, &d.implStatFS) + stateSinkObject.Save(7, &d.locks) + stateSinkObject.Save(8, &d.fs) + stateSinkObject.Save(9, &d.cgi) +} + +func (d *dir) afterLoad() {} + +// +checklocksignore +func (d *dir) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.InodeNoopRefCount) + stateSourceObject.Load(1, &d.InodeAlwaysValid) + stateSourceObject.Load(2, &d.InodeAttrs) + stateSourceObject.Load(3, &d.InodeNotSymlink) + stateSourceObject.Load(4, &d.InodeDirectoryNoNewChildren) + stateSourceObject.Load(5, &d.OrderedChildren) + stateSourceObject.Load(6, &d.implStatFS) + stateSourceObject.Load(7, &d.locks) + stateSourceObject.Load(8, &d.fs) + stateSourceObject.Load(9, &d.cgi) +} + +func (c *controllerFile) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.controllerFile" +} + +func (c *controllerFile) StateFields() []string { + return []string{ + "DynamicBytesFile", + } +} + +func (c *controllerFile) beforeSave() {} + +// +checklocksignore +func (c *controllerFile) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.DynamicBytesFile) +} + +func (c *controllerFile) afterLoad() {} + +// +checklocksignore +func (c *controllerFile) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.DynamicBytesFile) +} + +func (s *staticControllerFile) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.staticControllerFile" +} + +func (s *staticControllerFile) StateFields() []string { + return []string{ + "DynamicBytesFile", + "StaticData", + } +} + +func (s *staticControllerFile) beforeSave() {} + +// +checklocksignore +func (s *staticControllerFile) StateSave(stateSinkObject state.Sink) { + s.beforeSave() + stateSinkObject.Save(0, &s.DynamicBytesFile) + stateSinkObject.Save(1, &s.StaticData) +} + +func (s *staticControllerFile) afterLoad() {} + +// +checklocksignore +func (s *staticControllerFile) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &s.DynamicBytesFile) + stateSourceObject.Load(1, &s.StaticData) +} + +func (c *cpuController) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuController" +} + +func (c *cpuController) StateFields() []string { + return []string{ + "controllerCommon", + "cfsPeriod", + "cfsQuota", + "shares", + } +} + +func (c *cpuController) beforeSave() {} + +// +checklocksignore +func (c *cpuController) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.controllerCommon) + stateSinkObject.Save(1, &c.cfsPeriod) + stateSinkObject.Save(2, &c.cfsQuota) + stateSinkObject.Save(3, &c.shares) +} + +func (c *cpuController) afterLoad() {} + +// +checklocksignore +func (c *cpuController) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.controllerCommon) + stateSourceObject.Load(1, &c.cfsPeriod) + stateSourceObject.Load(2, &c.cfsQuota) + stateSourceObject.Load(3, &c.shares) +} + +func (c *cpuacctController) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctController" +} + +func (c *cpuacctController) StateFields() []string { + return []string{ + "controllerCommon", + } +} + +func (c *cpuacctController) beforeSave() {} + +// +checklocksignore +func (c *cpuacctController) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.controllerCommon) +} + +func (c *cpuacctController) afterLoad() {} + +// +checklocksignore +func (c *cpuacctController) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.controllerCommon) +} + +func (c *cpuacctCgroup) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctCgroup" +} + +func (c *cpuacctCgroup) StateFields() []string { + return []string{ + "cgroupInode", + } +} + +func (c *cpuacctCgroup) beforeSave() {} + +// +checklocksignore +func (c *cpuacctCgroup) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.cgroupInode) +} + +func (c *cpuacctCgroup) afterLoad() {} + +// +checklocksignore +func (c *cpuacctCgroup) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.cgroupInode) +} + +func (d *cpuacctStatData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctStatData" +} + +func (d *cpuacctStatData) StateFields() []string { + return []string{ + "cpuacctCgroup", + } +} + +func (d *cpuacctStatData) beforeSave() {} + +// +checklocksignore +func (d *cpuacctStatData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cpuacctCgroup) +} + +func (d *cpuacctStatData) afterLoad() {} + +// +checklocksignore +func (d *cpuacctStatData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctUsageData" +} + +func (d *cpuacctUsageData) StateFields() []string { + return []string{ + "cpuacctCgroup", + } +} + +func (d *cpuacctUsageData) beforeSave() {} + +// +checklocksignore +func (d *cpuacctUsageData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageData) afterLoad() {} + +// +checklocksignore +func (d *cpuacctUsageData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageUserData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctUsageUserData" +} + +func (d *cpuacctUsageUserData) StateFields() []string { + return []string{ + "cpuacctCgroup", + } +} + +func (d *cpuacctUsageUserData) beforeSave() {} + +// +checklocksignore +func (d *cpuacctUsageUserData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageUserData) afterLoad() {} + +// +checklocksignore +func (d *cpuacctUsageUserData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageSysData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpuacctUsageSysData" +} + +func (d *cpuacctUsageSysData) StateFields() []string { + return []string{ + "cpuacctCgroup", + } +} + +func (d *cpuacctUsageSysData) beforeSave() {} + +// +checklocksignore +func (d *cpuacctUsageSysData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.cpuacctCgroup) +} + +func (d *cpuacctUsageSysData) afterLoad() {} + +// +checklocksignore +func (d *cpuacctUsageSysData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.cpuacctCgroup) +} + +func (c *cpusetController) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpusetController" +} + +func (c *cpusetController) StateFields() []string { + return []string{ + "controllerCommon", + "maxCpus", + "maxMems", + "cpus", + "mems", + } +} + +func (c *cpusetController) beforeSave() {} + +// +checklocksignore +func (c *cpusetController) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.controllerCommon) + stateSinkObject.Save(1, &c.maxCpus) + stateSinkObject.Save(2, &c.maxMems) + stateSinkObject.Save(3, &c.cpus) + stateSinkObject.Save(4, &c.mems) +} + +func (c *cpusetController) afterLoad() {} + +// +checklocksignore +func (c *cpusetController) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.controllerCommon) + stateSourceObject.Load(1, &c.maxCpus) + stateSourceObject.Load(2, &c.maxMems) + stateSourceObject.Load(3, &c.cpus) + stateSourceObject.Load(4, &c.mems) +} + +func (d *cpusData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.cpusData" +} + +func (d *cpusData) StateFields() []string { + return []string{ + "c", + } +} + +func (d *cpusData) beforeSave() {} + +// +checklocksignore +func (d *cpusData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.c) +} + +func (d *cpusData) afterLoad() {} + +// +checklocksignore +func (d *cpusData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.c) +} + +func (d *memsData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.memsData" +} + +func (d *memsData) StateFields() []string { + return []string{ + "c", + } +} + +func (d *memsData) beforeSave() {} + +// +checklocksignore +func (d *memsData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.c) +} + +func (d *memsData) afterLoad() {} + +// +checklocksignore +func (d *memsData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.c) +} + +func (r *dirRefs) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.dirRefs" +} + +func (r *dirRefs) StateFields() []string { + return []string{ + "refCount", + } +} + +func (r *dirRefs) beforeSave() {} + +// +checklocksignore +func (r *dirRefs) StateSave(stateSinkObject state.Sink) { + r.beforeSave() + stateSinkObject.Save(0, &r.refCount) +} + +// +checklocksignore +func (r *dirRefs) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &r.refCount) + stateSourceObject.AfterLoad(r.afterLoad) +} + +func (c *jobController) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.jobController" +} + +func (c *jobController) StateFields() []string { + return []string{ + "controllerCommon", + "id", + } +} + +func (c *jobController) beforeSave() {} + +// +checklocksignore +func (c *jobController) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.controllerCommon) + stateSinkObject.Save(1, &c.id) +} + +func (c *jobController) afterLoad() {} + +// +checklocksignore +func (c *jobController) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.controllerCommon) + stateSourceObject.Load(1, &c.id) +} + +func (d *jobIDData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.jobIDData" +} + +func (d *jobIDData) StateFields() []string { + return []string{ + "c", + } +} + +func (d *jobIDData) beforeSave() {} + +// +checklocksignore +func (d *jobIDData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() + stateSinkObject.Save(0, &d.c) +} + +func (d *jobIDData) afterLoad() {} + +// +checklocksignore +func (d *jobIDData) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &d.c) +} + +func (c *memoryController) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.memoryController" +} + +func (c *memoryController) StateFields() []string { + return []string{ + "controllerCommon", + "limitBytes", + "softLimitBytes", + "moveChargeAtImmigrate", + } +} + +func (c *memoryController) beforeSave() {} + +// +checklocksignore +func (c *memoryController) StateSave(stateSinkObject state.Sink) { + c.beforeSave() + stateSinkObject.Save(0, &c.controllerCommon) + stateSinkObject.Save(1, &c.limitBytes) + stateSinkObject.Save(2, &c.softLimitBytes) + stateSinkObject.Save(3, &c.moveChargeAtImmigrate) +} + +func (c *memoryController) afterLoad() {} + +// +checklocksignore +func (c *memoryController) StateLoad(stateSourceObject state.Source) { + stateSourceObject.Load(0, &c.controllerCommon) + stateSourceObject.Load(1, &c.limitBytes) + stateSourceObject.Load(2, &c.softLimitBytes) + stateSourceObject.Load(3, &c.moveChargeAtImmigrate) +} + +func (d *memoryUsageInBytesData) StateTypeName() string { + return "pkg/sentry/fsimpl/cgroupfs.memoryUsageInBytesData" +} + +func (d *memoryUsageInBytesData) StateFields() []string { + return []string{} +} + +func (d *memoryUsageInBytesData) beforeSave() {} + +// +checklocksignore +func (d *memoryUsageInBytesData) StateSave(stateSinkObject state.Sink) { + d.beforeSave() +} + +func (d *memoryUsageInBytesData) afterLoad() {} + +// +checklocksignore +func (d *memoryUsageInBytesData) StateLoad(stateSourceObject state.Source) { +} + +func init() { + state.Register((*controllerCommon)(nil)) + state.Register((*cgroupInode)(nil)) + state.Register((*cgroupProcsData)(nil)) + state.Register((*tasksData)(nil)) + state.Register((*FilesystemType)(nil)) + state.Register((*InternalData)(nil)) + state.Register((*filesystem)(nil)) + state.Register((*implStatFS)(nil)) + state.Register((*dir)(nil)) + state.Register((*controllerFile)(nil)) + state.Register((*staticControllerFile)(nil)) + state.Register((*cpuController)(nil)) + state.Register((*cpuacctController)(nil)) + state.Register((*cpuacctCgroup)(nil)) + state.Register((*cpuacctStatData)(nil)) + state.Register((*cpuacctUsageData)(nil)) + state.Register((*cpuacctUsageUserData)(nil)) + state.Register((*cpuacctUsageSysData)(nil)) + state.Register((*cpusetController)(nil)) + state.Register((*cpusData)(nil)) + state.Register((*memsData)(nil)) + state.Register((*dirRefs)(nil)) + state.Register((*jobController)(nil)) + state.Register((*jobIDData)(nil)) + state.Register((*memoryController)(nil)) + state.Register((*memoryUsageInBytesData)(nil)) +} diff --git a/pkg/sentry/fsimpl/cgroupfs/dir_refs.go b/pkg/sentry/fsimpl/cgroupfs/dir_refs.go new file mode 100644 index 000000000..c29f0c9ae --- /dev/null +++ b/pkg/sentry/fsimpl/cgroupfs/dir_refs.go @@ -0,0 +1,140 @@ +package cgroupfs + +import ( + "fmt" + "sync/atomic" + + "gvisor.dev/gvisor/pkg/refsvfs2" +) + +// enableLogging indicates whether reference-related events should be logged (with +// stack traces). This is false by default and should only be set to true for +// debugging purposes, as it can generate an extremely large amount of output +// and drastically degrade performance. +const direnableLogging = false + +// obj is used to customize logging. Note that we use a pointer to T so that +// we do not copy the entire object when passed as a format parameter. +var dirobj *dir + +// Refs implements refs.RefCounter. It keeps a reference count using atomic +// operations and calls the destructor when the count reaches zero. +// +// NOTE: Do not introduce additional fields to the Refs struct. It is used by +// many filesystem objects, and we want to keep it as small as possible (i.e., +// the same size as using an int64 directly) to avoid taking up extra cache +// space. In general, this template should not be extended at the cost of +// performance. If it does not offer enough flexibility for a particular object +// (example: b/187877947), we should implement the RefCounter/CheckedObject +// interfaces manually. +// +// +stateify savable +type dirRefs struct { + // refCount is composed of two fields: + // + // [32-bit speculative references]:[32-bit real references] + // + // Speculative references are used for TryIncRef, to avoid a CompareAndSwap + // loop. See IncRef, DecRef and TryIncRef for details of how these fields are + // used. + refCount int64 +} + +// InitRefs initializes r with one reference and, if enabled, activates leak +// checking. +func (r *dirRefs) InitRefs() { + atomic.StoreInt64(&r.refCount, 1) + refsvfs2.Register(r) +} + +// RefType implements refsvfs2.CheckedObject.RefType. +func (r *dirRefs) RefType() string { + return fmt.Sprintf("%T", dirobj)[1:] +} + +// LeakMessage implements refsvfs2.CheckedObject.LeakMessage. +func (r *dirRefs) LeakMessage() string { + return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs()) +} + +// LogRefs implements refsvfs2.CheckedObject.LogRefs. +func (r *dirRefs) LogRefs() bool { + return direnableLogging +} + +// ReadRefs returns the current number of references. The returned count is +// inherently racy and is unsafe to use without external synchronization. +func (r *dirRefs) ReadRefs() int64 { + return atomic.LoadInt64(&r.refCount) +} + +// IncRef implements refs.RefCounter.IncRef. +// +//go:nosplit +func (r *dirRefs) IncRef() { + v := atomic.AddInt64(&r.refCount, 1) + if direnableLogging { + refsvfs2.LogIncRef(r, v) + } + if v <= 1 { + panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType())) + } +} + +// TryIncRef implements refs.TryRefCounter.TryIncRef. +// +// To do this safely without a loop, a speculative reference is first acquired +// on the object. This allows multiple concurrent TryIncRef calls to distinguish +// other TryIncRef calls from genuine references held. +// +//go:nosplit +func (r *dirRefs) TryIncRef() bool { + const speculativeRef = 1 << 32 + if v := atomic.AddInt64(&r.refCount, speculativeRef); int32(v) == 0 { + + atomic.AddInt64(&r.refCount, -speculativeRef) + return false + } + + v := atomic.AddInt64(&r.refCount, -speculativeRef+1) + if direnableLogging { + refsvfs2.LogTryIncRef(r, v) + } + return true +} + +// DecRef implements refs.RefCounter.DecRef. +// +// Note that speculative references are counted here. Since they were added +// prior to real references reaching zero, they will successfully convert to +// real references. In other words, we see speculative references only in the +// following case: +// +// A: TryIncRef [speculative increase => sees non-negative references] +// B: DecRef [real decrease] +// A: TryIncRef [transform speculative to real] +// +//go:nosplit +func (r *dirRefs) DecRef(destroy func()) { + v := atomic.AddInt64(&r.refCount, -1) + if direnableLogging { + refsvfs2.LogDecRef(r, v) + } + switch { + case v < 0: + panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType())) + + case v == 0: + refsvfs2.Unregister(r) + + if destroy != nil { + destroy() + } + } +} + +func (r *dirRefs) afterLoad() { + if r.ReadRefs() > 0 { + refsvfs2.Register(r) + } +} |