This is because of the cgroups v2 "no internal processes" rule:
The "no internal processes" rule is in fact more subtle than stated above. More precisely, the rule is that a (nonroot) cgroup can't both (1) have member processes, and (2) distribute resources into child cgroups—that is, have a nonempty cgroup.subtree_control file. Thus, it is possible for a cgroup to have both member processes and child cgroups, but before controllers can be enabled for that cgroup, the member processes must be moved out of the cgroup (e.g., perhaps into the child cgroups). With the Linux 4.14 addition of "thread mode" (described below), the "no internal processes" rule has been relaxed in some cases.
source: cgroups(7)
See also the kernel source code:
https://elixir.bootlin.com/linux/v5.19/source/kernel/cgroup/cgroup.c#L2586
** * cgroup_migrate_vet_dst - verify whether a cgroup can be migration destination * @dst_cgrp: destination cgroup to test * * On the default hierarchy, except for the mixable, (possible) thread root * and threaded cgroups, subtree_control must be zero for migration * destination cgroups with tasks so that child cgroups don't compete * against tasks. */ int cgroup_migrate_vet_dst(struct cgroup *dst_cgrp) { // [..] /* apply no-internal-process constraint */ if (dst_cgrp->subtree_control) return -EBUSY; return 0; }
So for example, this works:
# cd /sys/fs/cgroup /sys/fs/cgroup # rmdir deleteme /sys/fs/cgroup # mkdir deleteme /sys/fs/cgroup # cd deleteme /sys/fs/cgroup/deleteme # mkdir leaf /sys/fs/cgroup/deleteme # echo '+memory' > cgroup.subtree_control /sys/fs/cgroup/deleteme # echo $$ > leaf/cgroup.procs /sys/fs/cgroup/deleteme #