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 <benhillis@gmail.com>
This commit is contained in:
Blue 2025-10-02 20:36:12 -07:00 committed by GitHub
parent 98f4960118
commit 65eea7b31c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 115 additions and 35 deletions

View File

@ -28,6 +28,7 @@ WslDistributionConfig::WslDistributionConfig(const char* configFilePath)
ConfigKey("automount.options", DrvFsOptions), ConfigKey("automount.options", DrvFsOptions),
ConfigKey(c_ConfigMountFsTabOption, MountFsTab), ConfigKey(c_ConfigMountFsTabOption, MountFsTab),
ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs), ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs),
ConfigKey("automount.cgroups", {{"v1", CGroupVersion::v1}, {"v2", CGroupVersion::v2}}, CGroup, nullptr),
ConfigKey("filesystem.umask", Umask), ConfigKey("filesystem.umask", Umask),

View File

@ -48,6 +48,12 @@ struct WslDistributionConfig
WslDistributionConfig(WslDistributionConfig&&) = default; WslDistributionConfig(WslDistributionConfig&&) = default;
WslDistributionConfig& operator=(WslDistributionConfig&&) = default; WslDistributionConfig& operator=(WslDistributionConfig&&) = default;
enum class CGroupVersion
{
v1 = 0,
v2 = 1
};
bool AutoMount = true; bool AutoMount = true;
bool AutoUpdateTimezone = true; bool AutoUpdateTimezone = true;
std::optional<std::string> BootCommand; std::optional<std::string> BootCommand;
@ -71,6 +77,7 @@ struct WslDistributionConfig
bool AppendGpuLibPath = true; bool AppendGpuLibPath = true;
bool GpuEnabled = true; bool GpuEnabled = true;
bool LinkOsLibs = true; bool LinkOsLibs = true;
CGroupVersion CGroup = CGroupVersion::v2;
// //
// Values not set by /etc/wsl.conf. // Values not set by /etc/wsl.conf.

View File

@ -73,6 +73,8 @@ Abstract:
#define MOUNTS_DEVICE_FIELD 0 #define MOUNTS_DEVICE_FIELD 0
#define MOUNTS_FSTYPE_FIELD 2 #define MOUNTS_FSTYPE_FIELD 2
using wsl::linux::WslDistributionConfig;
static void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config); static void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config);
static bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid); 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. // Initialize cgroups based on what the kernel supports.
// //
ConfigInitializeCgroups(); ConfigInitializeCgroups(Config);
// //
// Attempt to register the NT interop binfmt extension. // Attempt to register the NT interop binfmt extension.
@ -1783,7 +1785,7 @@ Return Value:
{LX_WSL2_GUI_APP_SUPPORT_ENV, "1"}}; {LX_WSL2_GUI_APP_SUPPORT_ENV, "1"}};
} }
void ConfigInitializeCgroups(void) void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config)
/*++ /*++
@ -1795,7 +1797,7 @@ Routine Description:
Arguments: Arguments:
None. Config - Supplies the distribution configuration.
Return Value: Return Value:
@ -1805,50 +1807,82 @@ Return Value:
try try
{ {
std::vector<std::string> DisabledControllers;
//
// For WSL2 mount cgroup v2.
//
// N.B. Cgroup v2 is not implemented for WSL1.
//
if (UtilIsUtilityVm()) 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( THROW_LAST_ERROR_IF(
UtilMount(CGROUP2_DEVICE, Target, CGROUP2_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), "nsdelegate") < 0); UtilMount(CGROUP2_DEVICE, Target, CGROUP2_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), "nsdelegate") < 0);
if (Config.CGroup == WslDistributionConfig::CGroupVersion::v2)
{
return;
}
} }
else 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); 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; continue;
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);
} }
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() CATCH_LOG()

View File

@ -406,7 +406,7 @@ void ConfigHandleInteropMessage(
const MESSAGE_HEADER* Header, const MESSAGE_HEADER* Header,
const wsl::linux::WslDistributionConfig& Config); const wsl::linux::WslDistributionConfig& Config);
void ConfigInitializeCgroups(void); void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config);
int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config); int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config);

View File

@ -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); 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
} // namespace UnitTests } // namespace UnitTests