From 65eea7b31cdce11b0f5001de56d7bc330bb1db18 Mon Sep 17 00:00:00 2001 From: Blue Date: Thu, 2 Oct 2025 20:36:12 -0700 Subject: [PATCH] Introduce a new wsl.conf config value to allow distributions to opt-in to cgroupv1 mounts (#13546) * Introduce a new wsl.conf config value to allow distributions to opt-in to cgroupv1 mounts * Add test coverage * Fix tmpfs on wsl1 --------- Co-authored-by: Ben Hillis --- src/linux/init/WslDistributionConfig.cpp | 1 + src/linux/init/WslDistributionConfig.h | 7 ++ src/linux/init/config.cpp | 102 +++++++++++++++-------- src/linux/init/config.h | 2 +- test/windows/UnitTests.cpp | 38 +++++++++ 5 files changed, 115 insertions(+), 35 deletions(-) diff --git a/src/linux/init/WslDistributionConfig.cpp b/src/linux/init/WslDistributionConfig.cpp index 4bb4f8c..56f5710 100644 --- a/src/linux/init/WslDistributionConfig.cpp +++ b/src/linux/init/WslDistributionConfig.cpp @@ -28,6 +28,7 @@ WslDistributionConfig::WslDistributionConfig(const char* configFilePath) ConfigKey("automount.options", DrvFsOptions), ConfigKey(c_ConfigMountFsTabOption, MountFsTab), ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs), + ConfigKey("automount.cgroups", {{"v1", CGroupVersion::v1}, {"v2", CGroupVersion::v2}}, CGroup, nullptr), ConfigKey("filesystem.umask", Umask), diff --git a/src/linux/init/WslDistributionConfig.h b/src/linux/init/WslDistributionConfig.h index 892240e..772ffd2 100644 --- a/src/linux/init/WslDistributionConfig.h +++ b/src/linux/init/WslDistributionConfig.h @@ -48,6 +48,12 @@ struct WslDistributionConfig WslDistributionConfig(WslDistributionConfig&&) = default; WslDistributionConfig& operator=(WslDistributionConfig&&) = default; + enum class CGroupVersion + { + v1 = 0, + v2 = 1 + }; + bool AutoMount = true; bool AutoUpdateTimezone = true; std::optional BootCommand; @@ -71,6 +77,7 @@ struct WslDistributionConfig bool AppendGpuLibPath = true; bool GpuEnabled = true; bool LinkOsLibs = true; + CGroupVersion CGroup = CGroupVersion::v2; // // Values not set by /etc/wsl.conf. diff --git a/src/linux/init/config.cpp b/src/linux/init/config.cpp index e205127..f36d319 100644 --- a/src/linux/init/config.cpp +++ b/src/linux/init/config.cpp @@ -73,6 +73,8 @@ Abstract: #define MOUNTS_DEVICE_FIELD 0 #define MOUNTS_FSTYPE_FIELD 2 +using wsl::linux::WslDistributionConfig; + static void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config); static bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid); @@ -567,7 +569,7 @@ Return Value: // Initialize cgroups based on what the kernel supports. // - ConfigInitializeCgroups(); + ConfigInitializeCgroups(Config); // // Attempt to register the NT interop binfmt extension. @@ -1783,7 +1785,7 @@ Return Value: {LX_WSL2_GUI_APP_SUPPORT_ENV, "1"}}; } -void ConfigInitializeCgroups(void) +void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config) /*++ @@ -1795,7 +1797,7 @@ Routine Description: Arguments: - None. + Config - Supplies the distribution configuration. Return Value: @@ -1805,50 +1807,82 @@ Return Value: try { - - // - // For WSL2 mount cgroup v2. - // - // N.B. Cgroup v2 is not implemented for WSL1. - // + std::vector DisabledControllers; if (UtilIsUtilityVm()) { - const auto Target = CGROUP_MOUNTPOINT; + if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1) + { + auto commandLine = UtilReadFileContent("/proc/cmdline"); + auto position = commandLine.find(CGROUPS_NO_V1); + if (position != std::string::npos) + { + auto list = commandLine.substr(position + sizeof(CGROUPS_NO_V1) - 1); + auto end = list.find_first_of(" \n"); + if (end != std::string::npos) + { + list = list.substr(0, end); + } + + if (list == "all") + { + LOG_WARNING("Distribution has cgroupv1 enabled, but kernel command line has {}all. Falling back to cgroupv2", CGROUPS_NO_V1); + Config.CGroup = WslDistributionConfig::CGroupVersion::v2; + } + else + { + DisabledControllers = wsl::shared::string::Split(list, ','); + } + } + } + + if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1) + { + THROW_LAST_ERROR_IF(mount("tmpfs", CGROUP_MOUNTPOINT, "tmpfs", (MS_NOSUID | MS_NODEV | MS_NOEXEC), "mode=755") < 0); + } + + const auto Target = Config.CGroup == WslDistributionConfig::CGroupVersion::v1 ? CGROUP_MOUNTPOINT "/unified" : CGROUP_MOUNTPOINT; THROW_LAST_ERROR_IF( UtilMount(CGROUP2_DEVICE, Target, CGROUP2_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), "nsdelegate") < 0); + + if (Config.CGroup == WslDistributionConfig::CGroupVersion::v2) + { + return; + } } else { - // - // Mount cgroup v1 when running in WSL1 mode. - // - // Open the /proc/cgroups file and parse each line, ignoring malformed - // lines and disabled controllers. - // - THROW_LAST_ERROR_IF(mount("tmpfs", CGROUP_MOUNTPOINT, "tmpfs", (MS_NOSUID | MS_NODEV | MS_NOEXEC), "mode=755") < 0); + } - wil::unique_file Cgroups{fopen(CGROUPS_FILE, "r")}; - THROW_LAST_ERROR_IF(!Cgroups); + // + // Mount cgroup v1 when running in WSL1 mode or when a WSL2 distro has automount.cgroups=v1 specified. + // + // Open the /proc/cgroups file and parse each line, ignoring malformed + // lines and disabled controllers. + // + + wil::unique_file Cgroups{fopen(CGROUPS_FILE, "r")}; + THROW_LAST_ERROR_IF(!Cgroups); + + ssize_t BytesRead; + char* Line = nullptr; + auto LineCleanup = wil::scope_exit([&]() { free(Line); }); + size_t LineLength = 0; + while ((BytesRead = getline(&Line, &LineLength, Cgroups.get())) != -1) + { + char* Subsystem = nullptr; + bool Enabled = false; + if ((UtilParseCgroupsLine(Line, &Subsystem, &Enabled) < 0) || (Enabled == false) || + std::find(DisabledControllers.begin(), DisabledControllers.end(), Subsystem) != DisabledControllers.end()) - ssize_t BytesRead; - char* Line = nullptr; - auto LineCleanup = wil::scope_exit([&]() { free(Line); }); - size_t LineLength = 0; - while ((BytesRead = getline(&Line, &LineLength, Cgroups.get())) != -1) { - char* Subsystem = nullptr; - bool Enabled = false; - if ((UtilParseCgroupsLine(Line, &Subsystem, &Enabled) < 0) || (Enabled == false)) - { - continue; - } - - auto Target = std::format("{}/{}", CGROUP_MOUNTPOINT, Subsystem); - THROW_LAST_ERROR_IF( - UtilMount(CGROUP_DEVICE, Target.c_str(), CGROUP_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), Subsystem) < 0); + continue; } + + auto Target = std::format("{}/{}", CGROUP_MOUNTPOINT, Subsystem); + THROW_LAST_ERROR_IF( + UtilMount(CGROUP_DEVICE, Target.c_str(), CGROUP_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), Subsystem) < 0); } } CATCH_LOG() diff --git a/src/linux/init/config.h b/src/linux/init/config.h index a08d1f7..d8c63f1 100644 --- a/src/linux/init/config.h +++ b/src/linux/init/config.h @@ -406,7 +406,7 @@ void ConfigHandleInteropMessage( const MESSAGE_HEADER* Header, const wsl::linux::WslDistributionConfig& Config); -void ConfigInitializeCgroups(void); +void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config); int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span Buffer, wsl::linux::WslDistributionConfig& Config); diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 2aa46b3..610ab57 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -6156,5 +6156,43 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n", VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"dmesg | grep -iF 'vmbus_send_tl_connect_request'"), 0L); } + TEST_METHOD(CGroupv1) + { + WSL2_TEST_ONLY(); + + auto expectedMount = [](const char* path, const wchar_t* expected) { + auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"findmnt -ln '{}' || true", path)); + + VERIFY_ARE_EQUAL(out, expected); + }; + + // Validate that cgroupv2 is mounted by default. + expectedMount("/sys/fs/cgroup", L"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n"); + + // Validate that setting cgroup=v1 causes unified cgroups to be mounted. + DistroFileChange wslConf(L"/etc/wsl.conf", false); + wslConf.SetContent(L"[automount]\ncgroups=v1"); + + TerminateDistribution(); + + expectedMount( + "/sys/fs/cgroup/unified", L"/sys/fs/cgroup/unified cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n"); + + // Validate that the cgroupv1 mounts are present. + expectedMount("/sys/fs/cgroup/cpu", L"/sys/fs/cgroup/cpu cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu\n"); + + // Validate that having cgroup_no_v1=all causes the distribution to fall back to v2. + WslConfigChange wslConfig(LxssGenerateTestConfig({.kernelCommandLine = L"cgroup_no_v1=all"})); + + expectedMount("/sys/fs/cgroup/unified", L""); + expectedMount("/sys/fs/cgroup", L"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n"); + + auto [dmesg, __] = LxsstuLaunchWslAndCaptureOutput(L"dmesg"); + VERIFY_ARE_NOT_EQUAL( + dmesg.find( + L"Distribution has cgroupv1 enabled, but kernel command line has cgroup_no_v1=all. Falling back to cgroupv2"), + std::wstring::npos); + } + }; // namespace UnitTests } // namespace UnitTests