libprocessgroup: Rework SetupCgroup()

Improve the readability of this function by splitting it.

This CL includes the following behavior changes:
- If changing the directory owner and/or mode fails for /sys/fs/cgroup,
  this is considered as a fatal error instead of something that should
  only fail if "Optional" has not been set.
- If mounting the v2 cgroup controller fails, this is considered as an
  error.
- Activating/mounting a cgroup controller only fails if the controller
  has not been marked as optional.

Bug: 213617178
Change-Id: If6908dfdbcb2e1c9637ab4ac8a7625f0a17dc9e0
Signed-off-by: Bart Van Assche <bvanassche@google.com>
This commit is contained in:
Bart Van Assche 2023-02-07 13:14:56 -08:00
parent 6f9a9092b7
commit 1ec6ec4f57

View file

@ -254,86 +254,64 @@ static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors
// To avoid issues in sdk_mac build
#if defined(__ANDROID__)
static bool SetupCgroup(const CgroupDescriptor& descriptor) {
static bool IsOptionalController(const format::CgroupController* controller) {
return controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
}
static bool MountV2CgroupController(const CgroupDescriptor& descriptor) {
const format::CgroupController* controller = descriptor.controller();
int result;
if (controller->version() == 2) {
result = 0;
if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
// /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,
// try to create again in case the mount point is changed
if (!Mkdir(controller->path(), 0, "", "")) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
// /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,
// try to create again in case the mount point is changed
if (!Mkdir(controller->path(), 0, "", "")) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
// The memory_recursiveprot mount option has been introduced by kernel commit
// 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
// mount with that option enabled. If mounting fails because the kernel is too old,
// retry without that mount option.
if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
"memory_recursiveprot") < 0) {
LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
nullptr) < 0) {
PLOG(ERROR) << "Failed to mount cgroup v2";
}
}
// selinux permissions change after mounting, so it's ok to change mode and owner now
if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
descriptor.gid())) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
result = -1;
}
} else {
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
std::string str = std::string("+") + controller->name();
std::string path = std::string(controller->path()) + "/cgroup.subtree_control";
if (!base::WriteStringToFile(str, path)) {
LOG(ERROR) << "Failed to activate controller " << controller->name();
return false;
}
}
}
} else {
// mkdir <path> [mode] [owner] [group]
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
// Unfortunately historically cpuset controller was mounted using a mount command
// different from all other controllers. This results in controller attributes not
// to be prepended with controller name. For example this way instead of
// /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
// the system currently expects.
if (!strcmp(controller->name(), "cpuset")) {
// mount cpuset none /dev/cpuset nodev noexec nosuid
result = mount("none", controller->path(), controller->name(),
MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
} else {
// mount cgroup none <path> nodev noexec nosuid <controller>
result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
controller->name());
// The memory_recursiveprot mount option has been introduced by kernel commit
// 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
// mount with that option enabled. If mounting fails because the kernel is too old,
// retry without that mount option.
if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
"memory_recursiveprot") < 0) {
LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
nullptr) < 0) {
PLOG(ERROR) << "Failed to mount cgroup v2";
return IsOptionalController(controller);
}
}
if (result < 0) {
bool optional = controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
// selinux permissions change after mounting, so it's ok to change mode and owner now
if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
descriptor.gid())) {
PLOG(ERROR) << "Change of ownership or mode failed for controller " << controller->name();
return IsOptionalController(controller);
}
if (optional && errno == EINVAL) {
// Optional controllers are allowed to fail to mount if kernel does not support them
LOG(INFO) << "Optional " << controller->name() << " cgroup controller is not mounted";
} else {
PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
return true;
}
static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) {
const format::CgroupController* controller = descriptor.controller();
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
std::string str = "+";
str += controller->name();
std::string path = controller->path();
path += "/cgroup.subtree_control";
if (!base::WriteStringToFile(str, path)) {
if (IsOptionalController(controller)) {
PLOG(INFO) << "Failed to activate optional controller " << controller->name();
return true;
}
PLOG(ERROR) << "Failed to activate controller " << controller->name();
return false;
}
}
@ -341,6 +319,55 @@ static bool SetupCgroup(const CgroupDescriptor& descriptor) {
return true;
}
static bool MountV1CgroupController(const CgroupDescriptor& descriptor) {
const format::CgroupController* controller = descriptor.controller();
// mkdir <path> [mode] [owner] [group]
if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
return false;
}
// Unfortunately historically cpuset controller was mounted using a mount command
// different from all other controllers. This results in controller attributes not
// to be prepended with controller name. For example this way instead of
// /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
// the system currently expects.
int res;
if (!strcmp(controller->name(), "cpuset")) {
// mount cpuset none /dev/cpuset nodev noexec nosuid
res = mount("none", controller->path(), controller->name(),
MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
} else {
// mount cgroup none <path> nodev noexec nosuid <controller>
res = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
controller->name());
}
if (res != 0) {
if (IsOptionalController(controller)) {
PLOG(INFO) << "Failed to mount optional controller " << controller->name();
return true;
}
PLOG(ERROR) << "Failed to mount controller " << controller->name();
return false;
}
return true;
}
static bool SetupCgroup(const CgroupDescriptor& descriptor) {
const format::CgroupController* controller = descriptor.controller();
if (controller->version() == 2) {
if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
return MountV2CgroupController(descriptor);
} else {
return ActivateV2CgroupController(descriptor);
}
} else {
return MountV1CgroupController(descriptor);
}
}
#else
// Stubs for non-Android targets.