@@ -6,8 +6,13 @@ import (
66 "os"
77 "path/filepath"
88 "strconv"
9+ "strings"
10+ "sync"
11+ "syscall"
912 "testing"
1013 "time"
14+
15+ "golang.org/x/sys/unix"
1116)
1217
1318func TestWriteCgroupFileHandlesInterrupt (t * testing.T ) {
@@ -70,6 +75,75 @@ func TestOpenat2(t *testing.T) {
7075 }
7176}
7277
78+ func TestCgroupRootHandleOpenedToAnotherFile (t * testing.T ) {
79+ const (
80+ memoryCgroupMount = "/sys/fs/cgroup/memory"
81+ memoryLimit = "memory.limit_in_bytes"
82+ )
83+ if _ , err := os .Stat (memoryCgroupMount ); err != nil {
84+ // most probably cgroupv2
85+ t .Skip (err )
86+ }
87+
88+ cgroupName := fmt .Sprintf ("test-eano-%d" , time .Now ().Nanosecond ())
89+ cgroupPath := filepath .Join (memoryCgroupMount , cgroupName )
90+ if err := os .MkdirAll (cgroupPath , 0o755 ); err != nil {
91+ t .Fatal (err )
92+ }
93+ defer os .RemoveAll (cgroupPath )
94+
95+ if _ , err := os .Stat (filepath .Join (cgroupPath , memoryLimit )); err != nil {
96+ // either cgroupv2, or memory controller is not available
97+ t .Skip (err )
98+ }
99+
100+ // The cgroupRootHandle is opened when the openFile is called.
101+ if _ , err := openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err != nil {
102+ t .Fatal (err )
103+ }
104+
105+ // Make sure the cgroupRootHandle is opened to another file.
106+ if err := syscall .Close (int (cgroupRootHandle .Fd ())); err != nil {
107+ t .Fatal (err )
108+ }
109+ if _ , err := unix .Openat2 (- 1 , "/tmp" , & unix.OpenHow {Flags : unix .O_DIRECTORY | unix .O_PATH | unix .O_CLOEXEC }); err != nil {
110+ t .Fatal (err )
111+ }
112+
113+ var readErr * error
114+ readErrLock := sync.Mutex {}
115+ errCount := 0
116+
117+ // The openFile returns error (may be multiple times) and the prepOnce is reset only once.
118+ var wg sync.WaitGroup
119+ for i := 0 ; i < 100 ; i ++ {
120+ wg .Add (1 )
121+ go func (i int ) {
122+ defer wg .Done ()
123+ if _ , err := openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err != nil {
124+ readErrLock .Lock ()
125+ readErr = & err
126+ errCount ++
127+ readErrLock .Unlock ()
128+ }
129+ }(i )
130+ }
131+ wg .Wait ()
132+
133+ if errCount == 0 {
134+ t .Fatal ("At least one openFile should fail" )
135+ }
136+
137+ if ! strings .Contains ((* readErr ).Error (), "unexpectedly opened to" ) {
138+ t .Fatalf ("openFile should fail with 'cgroupRootHandle %d unexpectedly opened to <another file>'" , cgroupRootHandle .Fd ())
139+ }
140+
141+ // The openFile should work after prepOnce is reset because the cgroupRootHandle is updated.
142+ if _ , err := openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err != nil {
143+ t .Fatal (err )
144+ }
145+ }
146+
73147func BenchmarkWriteFile (b * testing.B ) {
74148 TestMode = true
75149 defer func () { TestMode = false }()
0 commit comments