@@ -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,77 @@ 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+ _ , err := openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY )
124+ t .Logf ("openFile attempt %d: %v\n " , i , err )
125+ if err != nil {
126+ readErrLock .Lock ()
127+ readErr = & err
128+ errCount ++
129+ readErrLock .Unlock ()
130+ }
131+ }(i )
132+ }
133+ wg .Wait ()
134+
135+ if errCount == 0 {
136+ t .Fatal ("At least one openFile should fail" )
137+ }
138+
139+ if ! strings .Contains ((* readErr ).Error (), "unexpectedly opened to" ) {
140+ t .Fatalf ("openFile should fail with 'cgroupRootHandle %d unexpectedly opened to <another file>'" , cgroupRootHandle .Fd ())
141+ }
142+
143+ // The openFile should work after prepOnce is reset because the cgroupRootHandle is updated.
144+ if _ , err := openFile (cgroupfsDir , filepath .Join ("memory" , cgroupName , memoryLimit ), os .O_RDONLY ); err != nil {
145+ t .Fatal (err )
146+ }
147+ }
148+
73149func BenchmarkWriteFile (b * testing.B ) {
74150 TestMode = true
75151 defer func () { TestMode = false }()
0 commit comments