@@ -251,27 +251,39 @@ again:
251251// RemovePath aims to remove cgroup path. It does so recursively,
252252// by removing any subdirectories (sub-cgroups) first.
253253func RemovePath (path string ) error {
254- // Try the fast path first.
254+ // Try the fast path first; don't retry on EBUSY yet .
255255 if err := rmdir (path , false ); err == nil {
256256 return nil
257257 }
258258
259+ // There are many reasons why rmdir can fail, including:
260+ // 1. cgroup have existing sub-cgroups;
261+ // 2. cgroup (still) have some processes (that are about to vanish);
262+ // 3. lack of permission (one example is read-only /sys/fs/cgroup mount,
263+ // in which case rmdir returns EROFS even for for a non-existent path,
264+ // see issue 4518).
265+ //
266+ // Using os.ReadDir here kills two birds with one stone: check if
267+ // the directory exists (handling scenario 3 above), and use
268+ // directory contents to remove sub-cgroups (handling scenario 1).
259269 infos , err := os .ReadDir (path )
260- if err != nil && ! os .IsNotExist (err ) {
270+ if err != nil {
271+ if os .IsNotExist (err ) {
272+ return nil
273+ }
261274 return err
262275 }
276+ // Let's remove sub-cgroups, if any.
263277 for _ , info := range infos {
264278 if info .IsDir () {
265- // We should remove subcgroup first.
266279 if err = RemovePath (filepath .Join (path , info .Name ())); err != nil {
267- break
280+ return err
268281 }
269282 }
270283 }
271- if err == nil {
272- err = rmdir (path , true )
273- }
274- return err
284+ // Finally, try rmdir again, this time with retries on EBUSY,
285+ // which may help with scenario 2 above.
286+ return rmdir (path , true )
275287}
276288
277289// RemovePaths iterates over the provided paths removing them.
0 commit comments