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>
Blue committed
Oct 2, 2025 at 20:36 UTC
65eea7b31cdce11b0f5001de56d7bc330bb1db18
5 files changed
+115
-35
src/linux/init/WslDistributionConfig.cpp
+1
@@ -28,6 +28,7 @@ WslDistributionConfig::WslDistributionConfig(const char* configFilePath)
28
ConfigKey("automount.options", DrvFsOptions),
29
ConfigKey(c_ConfigMountFsTabOption, MountFsTab),
30
ConfigKey(c_ConfigLinkOsLibsOption, LinkOsLibs),
31
+ ConfigKey("automount.cgroups", {{"v1", CGroupVersion::v1}, {"v2", CGroupVersion::v2}}, CGroup, nullptr),
32
33
ConfigKey("filesystem.umask", Umask),
34
src/linux/init/WslDistributionConfig.h
+7
@@ -48,6 +48,12 @@ struct WslDistributionConfig
48
WslDistributionConfig(WslDistributionConfig&&) = default;
49
WslDistributionConfig& operator=(WslDistributionConfig&&) = default;
50
51
+ enum class CGroupVersion
52
+ {
53
+ v1 = 0,
54
+ v2 = 1
55
+ };
56
+
57
bool AutoMount = true;
58
bool AutoUpdateTimezone = true;
59
std::optional<std::string> BootCommand;
@@ -71,6 +77,7 @@ struct WslDistributionConfig
77
bool AppendGpuLibPath = true;
78
bool GpuEnabled = true;
79
bool LinkOsLibs = true;
80
+ CGroupVersion CGroup = CGroupVersion::v2;
81
82
//
83
// Values not set by /etc/wsl.conf.
src/linux/init/config.cpp
+68
-34
@@ -73,6 +73,8 @@ Abstract:
73
#define MOUNTS_DEVICE_FIELD 0
74
#define MOUNTS_FSTYPE_FIELD 2
75
76
+using wsl::linux::WslDistributionConfig;
77
+
78
static void ConfigApplyWindowsLibPath(const wsl::linux::WslDistributionConfig& Config);
79
80
static bool CreateLoginSession(const wsl::linux::WslDistributionConfig& Config, const char* Username, uid_t Uid);
@@ -567,7 +569,7 @@ Return Value:
569
// Initialize cgroups based on what the kernel supports.
570
//
571
570
- ConfigInitializeCgroups();
572
+ ConfigInitializeCgroups(Config);
573
574
//
575
// Attempt to register the NT interop binfmt extension.
@@ -1783,7 +1785,7 @@ Return Value:
1785
{LX_WSL2_GUI_APP_SUPPORT_ENV, "1"}};
1786
}
1787
1786
-void ConfigInitializeCgroups(void)
1788
+void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config)
1789
1790
/*++
1791
@@ -1795,7 +1797,7 @@ Routine Description:
1797
1798
Arguments:
1799
1798
- None.
1800
+ Config - Supplies the distribution configuration.
1801
1802
Return Value:
1803
@@ -1805,50 +1807,82 @@ Return Value:
1807
1808
try
1809
{
1808
-
1809
- //
1810
- // For WSL2 mount cgroup v2.
1811
- //
1812
- // N.B. Cgroup v2 is not implemented for WSL1.
1813
- //
1810
+ std::vector<std::string> DisabledControllers;
1811
1812
if (UtilIsUtilityVm())
1813
{
1817
- const auto Target = CGROUP_MOUNTPOINT;
1814
+ if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1)
1815
+ {
1816
+ auto commandLine = UtilReadFileContent("/proc/cmdline");
1817
+ auto position = commandLine.find(CGROUPS_NO_V1);
1818
+ if (position != std::string::npos)
1819
+ {
1820
+ auto list = commandLine.substr(position + sizeof(CGROUPS_NO_V1) - 1);
1821
+ auto end = list.find_first_of(" \n");
1822
+ if (end != std::string::npos)
1823
+ {
1824
+ list = list.substr(0, end);
1825
+ }
1826
+
1827
+ if (list == "all")
1828
+ {
1829
+ LOG_WARNING("Distribution has cgroupv1 enabled, but kernel command line has {}all. Falling back to cgroupv2", CGROUPS_NO_V1);
1830
+ Config.CGroup = WslDistributionConfig::CGroupVersion::v2;
1831
+ }
1832
+ else
1833
+ {
1834
+ DisabledControllers = wsl::shared::string::Split(list, ',');
1835
+ }
1836
+ }
1837
+ }
1838
+
1839
+ if (Config.CGroup == WslDistributionConfig::CGroupVersion::v1)
1840
+ {
1841
+ THROW_LAST_ERROR_IF(mount("tmpfs", CGROUP_MOUNTPOINT, "tmpfs", (MS_NOSUID | MS_NODEV | MS_NOEXEC), "mode=755") < 0);
1842
+ }
1843
+
1844
+ const auto Target = Config.CGroup == WslDistributionConfig::CGroupVersion::v1 ? CGROUP_MOUNTPOINT "/unified" : CGROUP_MOUNTPOINT;
1845
THROW_LAST_ERROR_IF(
1846
UtilMount(CGROUP2_DEVICE, Target, CGROUP2_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), "nsdelegate") < 0);
1847
+
1848
+ if (Config.CGroup == WslDistributionConfig::CGroupVersion::v2)
1849
+ {
1850
+ return;
1851
+ }
1852
}
1853
else
1854
{
1823
- //
1824
- // Mount cgroup v1 when running in WSL1 mode.
1825
- //
1826
- // Open the /proc/cgroups file and parse each line, ignoring malformed
1827
- // lines and disabled controllers.
1828
- //
1829
-
1855
THROW_LAST_ERROR_IF(mount("tmpfs", CGROUP_MOUNTPOINT, "tmpfs", (MS_NOSUID | MS_NODEV | MS_NOEXEC), "mode=755") < 0);
1856
+ }
1857
1832
- wil::unique_file Cgroups{fopen(CGROUPS_FILE, "r")};
1833
- THROW_LAST_ERROR_IF(!Cgroups);
1858
+ //
1859
+ // Mount cgroup v1 when running in WSL1 mode or when a WSL2 distro has automount.cgroups=v1 specified.
1860
+ //
1861
+ // Open the /proc/cgroups file and parse each line, ignoring malformed
1862
+ // lines and disabled controllers.
1863
+ //
1864
1835
- ssize_t BytesRead;
1836
- char* Line = nullptr;
1837
- auto LineCleanup = wil::scope_exit([&]() { free(Line); });
1838
- size_t LineLength = 0;
1839
- while ((BytesRead = getline(&Line, &LineLength, Cgroups.get())) != -1)
1840
- {
1841
- char* Subsystem = nullptr;
1842
- bool Enabled = false;
1843
- if ((UtilParseCgroupsLine(Line, &Subsystem, &Enabled) < 0) || (Enabled == false))
1844
- {
1845
- continue;
1846
- }
1865
+ wil::unique_file Cgroups{fopen(CGROUPS_FILE, "r")};
1866
+ THROW_LAST_ERROR_IF(!Cgroups);
1867
1848
- auto Target = std::format("{}/{}", CGROUP_MOUNTPOINT, Subsystem);
1849
- THROW_LAST_ERROR_IF(
1850
- UtilMount(CGROUP_DEVICE, Target.c_str(), CGROUP_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), Subsystem) < 0);
1868
+ ssize_t BytesRead;
1869
+ char* Line = nullptr;
1870
+ auto LineCleanup = wil::scope_exit([&]() { free(Line); });
1871
+ size_t LineLength = 0;
1872
+ while ((BytesRead = getline(&Line, &LineLength, Cgroups.get())) != -1)
1873
+ {
1874
+ char* Subsystem = nullptr;
1875
+ bool Enabled = false;
1876
+ if ((UtilParseCgroupsLine(Line, &Subsystem, &Enabled) < 0) || (Enabled == false) ||
1877
+ std::find(DisabledControllers.begin(), DisabledControllers.end(), Subsystem) != DisabledControllers.end())
1878
+
1879
+ {
1880
+ continue;
1881
}
1882
+
1883
+ auto Target = std::format("{}/{}", CGROUP_MOUNTPOINT, Subsystem);
1884
+ THROW_LAST_ERROR_IF(
1885
+ UtilMount(CGROUP_DEVICE, Target.c_str(), CGROUP_DEVICE, (MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME), Subsystem) < 0);
1886
}
1887
}
1888
CATCH_LOG()
src/linux/init/config.h
+1
-1
@@ -406,7 +406,7 @@ void ConfigHandleInteropMessage(
406
const MESSAGE_HEADER* Header,
407
const wsl::linux::WslDistributionConfig& Config);
408
409
-void ConfigInitializeCgroups(void);
409
+void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config);
410
411
int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config);
412
test/windows/UnitTests.cpp
+38
@@ -6156,5 +6156,43 @@ Error code: Wsl/InstallDistro/WSL_E_INVALID_JSON\r\n",
6156
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"dmesg | grep -iF 'vmbus_send_tl_connect_request'"), 0L);
6157
}
6158
6159
+ TEST_METHOD(CGroupv1)
6160
+ {
6161
+ WSL2_TEST_ONLY();
6162
+
6163
+ auto expectedMount = [](const char* path, const wchar_t* expected) {
6164
+ auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"findmnt -ln '{}' || true", path));
6165
+
6166
+ VERIFY_ARE_EQUAL(out, expected);
6167
+ };
6168
+
6169
+ // Validate that cgroupv2 is mounted by default.
6170
+ expectedMount("/sys/fs/cgroup", L"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n");
6171
+
6172
+ // Validate that setting cgroup=v1 causes unified cgroups to be mounted.
6173
+ DistroFileChange wslConf(L"/etc/wsl.conf", false);
6174
+ wslConf.SetContent(L"[automount]\ncgroups=v1");
6175
+
6176
+ TerminateDistribution();
6177
+
6178
+ expectedMount(
6179
+ "/sys/fs/cgroup/unified", L"/sys/fs/cgroup/unified cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n");
6180
+
6181
+ // Validate that the cgroupv1 mounts are present.
6182
+ expectedMount("/sys/fs/cgroup/cpu", L"/sys/fs/cgroup/cpu cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu\n");
6183
+
6184
+ // Validate that having cgroup_no_v1=all causes the distribution to fall back to v2.
6185
+ WslConfigChange wslConfig(LxssGenerateTestConfig({.kernelCommandLine = L"cgroup_no_v1=all"}));
6186
+
6187
+ expectedMount("/sys/fs/cgroup/unified", L"");
6188
+ expectedMount("/sys/fs/cgroup", L"/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate\n");
6189
+
6190
+ auto [dmesg, __] = LxsstuLaunchWslAndCaptureOutput(L"dmesg");
6191
+ VERIFY_ARE_NOT_EQUAL(
6192
+ dmesg.find(
6193
+ L"Distribution has cgroupv1 enabled, but kernel command line has cgroup_no_v1=all. Falling back to cgroupv2"),
6194
+ std::wstring::npos);
6195
+ }
6196
+
6197
}; // namespace UnitTests
6198
} // namespace UnitTests