Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 75 additions & 8 deletions src/windows/service/exe/LxssUserSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,7 @@ HRESULT LxssUserSessionImpl::ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In
RETURN_HR_IF(E_INVALIDARG, (WI_IsAnyFlagSet(Flags, ~LXSS_EXPORT_DISTRO_FLAGS_ALL)));

LXSS_DISTRO_CONFIGURATION configuration;
std::vector<PidTermination> pidTerminations;
wil::unique_hkey distroKey;
try
{
Expand All @@ -1086,12 +1087,13 @@ HRESULT LxssUserSessionImpl::ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In
RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, WI_IsFlagClear(configuration.Flags, LXSS_DISTRO_FLAGS_VM_MODE) && !g_lxcoreInitialized);

// Add the distribution to the list of converting distributions.
_ConversionBegin(configuration.DistroId, LxssDistributionStateExporting);
pidTerminations = _ConversionBegin(configuration.DistroId, LxssDistributionStateExporting);
}
CATCH_RETURN()

// Set up a scope exit member to remove the distribution from the converting list.
auto exportComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });
RETURN_IF_FAILED(_WaitForPidTerminations(pidTerminations));

// Log telemetry to track how long exporting the distribution takes.
WSL_LOG_TELEMETRY(
Expand Down Expand Up @@ -1472,6 +1474,7 @@ HRESULT LxssUserSessionImpl::RegisterDistribution(

DistributionRegistration registration;
LXSS_DISTRO_CONFIGURATION configuration;
std::vector<PidTermination> pidTerminations;
std::filesystem::path distributionPath;
auto config = _GetResultantConfig(userToken.get());

Expand Down Expand Up @@ -1537,11 +1540,12 @@ HRESULT LxssUserSessionImpl::RegisterDistribution(
configuration = s_GetDistributionConfiguration(registration, DistributionName == nullptr);

// Add the distribution to the list of converting distributions.
_ConversionBegin(configuration.DistroId, LxssDistributionStateInstalling);
pidTerminations = _ConversionBegin(configuration.DistroId, LxssDistributionStateInstalling);
}

// Set up a scope exit member to remove the distribution from the converting list.
auto installComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });
THROW_IF_FAILED(_WaitForPidTerminations(pidTerminations));

// Declare a scope exit variable to clean up on failure.
ULONG deleteFlags = 0;
Expand Down Expand Up @@ -1872,10 +1876,11 @@ try
auto runAsUser = wil::CoImpersonateClient();
std::filesystem::path vhdPath;
LXSS_DISTRO_CONFIGURATION configuration{};
std::vector<PidTermination> pidTerminations;
const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);

{
std::lock_guard lock(m_instanceLock);
const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
const wil::unique_hkey lxssKey = s_OpenLxssUserKey(userToken.get());
const auto registration = DistributionRegistration::Open(lxssKey.get(), *DistroGuid);
configuration = s_GetDistributionConfiguration(registration);
Expand All @@ -1887,8 +1892,14 @@ try
THROW_HR_WITH_USER_ERROR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), wsl::shared::Localization::MessageCompactVhdNotSupported());
}

_ConversionBegin(configuration.DistroId, LxssDistributionStateCompacting);
pidTerminations = _ConversionBegin(configuration.DistroId, LxssDistributionStateCompacting);
}

auto compactionComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });
THROW_IF_FAILED(_WaitForPidTerminations(pidTerminations));

{
std::lock_guard lock(m_instanceLock);
// Trim the filesystem before compaction so the host can reclaim the freed blocks.
//
// WSL2 does not mount ext4 with 'discard' and does not run fsck at boot, so blocks freed
Expand All @@ -1908,8 +1919,6 @@ try
CATCH_LOG();
}

auto compactionComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });

THROW_IF_FAILED_MSG(
wil::ResultFromException([&] { wsl::core::filesystem::CompactVhd(vhdPath.c_str()); }),
"Failed to compact VHD: %ls",
Expand All @@ -1924,6 +1933,7 @@ HRESULT LxssUserSessionImpl::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Vers

DistributionRegistration registration;
LXSS_DISTRO_CONFIGURATION configuration;
std::vector<PidTermination> pidTerminations;
const auto userToken = wsl::windows::common::security::GetUserToken(TokenImpersonation);
wil::unique_hkey lxssKey = s_OpenLxssUserKey(userToken.get());
try
Expand Down Expand Up @@ -1953,7 +1963,7 @@ HRESULT LxssUserSessionImpl::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Vers
RETURN_HR_IF(WSL_E_WSL1_NOT_SUPPORTED, !g_lxcoreInitialized);

// Add the distribution to the list of converting distributions.
_ConversionBegin(configuration.DistroId, LxssDistributionStateConverting);
pidTerminations = _ConversionBegin(configuration.DistroId, LxssDistributionStateConverting);

// Remove the distribution ID from m_updatedInitDistros so init is updated on the next launch (in the case of a conversion to WSL1).
m_updatedInitDistros.erase(
Expand All @@ -1963,6 +1973,7 @@ HRESULT LxssUserSessionImpl::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Vers

// Set up a scope exit member to remove the distribution from the converting list.
auto conversionComplete = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { _ConversionComplete(configuration.DistroId); });
RETURN_IF_FAILED(_WaitForPidTerminations(pidTerminations));

// Log telemetry to track how long enabling VM mode takes.
WSL_LOG_TELEMETRY(
Expand Down Expand Up @@ -2513,11 +2524,22 @@ HRESULT LxssUserSessionImpl::UnregisterDistribution(_In_ LPCGUID DistroGuid)
}

_Requires_lock_held_(m_instanceLock)
void LxssUserSessionImpl::_ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State)
std::vector<LxssUserSessionImpl::PidTermination> LxssUserSessionImpl::_ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State)
{
_EnsureNotLocked(&DistroGuid);
_TerminateInstanceInternal(&DistroGuid);

std::vector<PidTermination> pidTerminations;
for (const auto& termination : m_pidTerminations)
{
if (IsEqualGUID(termination.second.DistroId, DistroGuid))
{
Comment on lines +2532 to +2536
pidTerminations.push_back(termination.second);
}
}

m_lockedDistributions.emplace_back(DistroGuid, State);
return pidTerminations;
}

_Requires_lock_not_held_(m_instanceLock)
Expand All @@ -2529,6 +2551,31 @@ void LxssUserSessionImpl::_ConversionComplete(_In_ GUID DistroGuid)
_VmCheckIdle();
}

_Requires_lock_not_held_(m_instanceLock)
HRESULT LxssUserSessionImpl::_WaitForPidTerminations(_In_ const std::vector<PidTermination>& PidTerminations)
try
{
SlowOperationWatcher slowOperation{"WaitForPidTermination"};
const auto startTime = GetTickCount64();
for (const auto& termination : PidTerminations)
{
const auto elapsed = GetTickCount64() - startTime;
const auto timeout = termination.Timeout == INFINITE ? INFINITE
: elapsed >= termination.Timeout ? 0
: termination.Timeout - gsl::narrow_cast<DWORD>(elapsed);
if (!termination.Event.wait(timeout))
{
WSL_LOG(
"PidTerminationTimeout",
TraceLoggingValue(termination.ClientId, "pid"),
TraceLoggingValue(termination.Timeout, "timeout"));
}
}

return S_OK;
}
CATCH_RETURN()

_Requires_exclusive_lock_held_(m_instanceLock)
void LxssUserSessionImpl::_CreateLegacyRegistration(_In_ HKEY LxssKey, _In_ HANDLE UserToken)
{
Expand Down Expand Up @@ -3038,6 +3085,12 @@ void LxssUserSessionImpl::_CreateVm()

auto unlock = wil::scope_exit([&]() { m_instanceLock.unlock(); });
TerminateByClientIdLockHeld(Pid);

if (const auto termination = m_pidTerminations.find(Pid); termination != m_pidTerminations.end())
{
termination->second.Event.SetEvent();
m_pidTerminations.erase(termination);
}
};

// N.B. The callbacks must be registered outside of the above try/catch.
Expand Down Expand Up @@ -3736,6 +3789,13 @@ bool LxssUserSessionImpl::_TerminateInstanceInternal(_In_ LPCGUID DistroGuid, _I
{
if (const auto* wslcoreInstance = dynamic_cast<WslCoreInstance*>(instance->second.get()); wslcoreInstance != nullptr)
{
// Stop releases the system distro, so capture its PID while it is still available.
PidTermination termination;
termination.DistroId = *DistroGuid;
termination.ClientId = wslcoreInstance->GetClientId();
termination.Timeout = gsl::narrow_cast<DWORD>(m_utilityVm->GetConfig().DistributionStartTimeout);
m_pidTerminations.try_emplace(termination.ClientId, std::move(termination));

m_pluginManager.OnDistributionStopping(&m_session, wslcoreInstance->DistributionInformation());
}

Expand Down Expand Up @@ -4038,6 +4098,13 @@ void LxssUserSessionImpl::_VmTerminate()
m_utilityVm.reset();
m_vmId.store(GUID_NULL);

// VM teardown cancels exit callbacks, so complete any remaining waiters after the VM has released its resources.
for (const auto& termination : m_pidTerminations)
{
termination.second.Event.SetEvent();
}
m_pidTerminations.clear();

// Reset the user's token since its lifetime is tied to the VM.
m_userToken.reset();
m_session.UserToken = nullptr;
Expand Down
18 changes: 17 additions & 1 deletion src/windows/service/exe/LxssUserSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,11 +544,19 @@ class LxssUserSessionImpl : public std::enable_shared_from_this<LxssUserSessionI
static CreateLxProcessContext s_GetCreateProcessContext(_In_ const GUID& DistroGuid, _In_ bool SystemDistro);

private:
struct PidTermination
{
GUID DistroId{};
ULONG ClientId{};
DWORD Timeout{};
wil::shared_event Event{wil::EventOptions::ManualReset};
};

/// <summary>
/// Adds a distro to the list of converting distros.
/// </summary>
_Requires_lock_held_(m_instanceLock)
void _ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State);
std::vector<PidTermination> _ConversionBegin(_In_ GUID DistroGuid, _In_ LxssDistributionState State);

/// <summary>
/// Removes a distro from the list of converting distros and checks if the
Expand All @@ -557,6 +565,9 @@ class LxssUserSessionImpl : public std::enable_shared_from_this<LxssUserSessionI
_Requires_lock_not_held_(m_instanceLock)
void _ConversionComplete(_In_ GUID DistroGuid);

_Requires_lock_not_held_(m_instanceLock)
HRESULT _WaitForPidTerminations(_In_ const std::vector<PidTermination>& PidTerminations);

/// <summary>
/// Creates a distribution registration for legacy installs.
/// </summary>
Expand Down Expand Up @@ -822,6 +833,11 @@ class LxssUserSessionImpl : public std::enable_shared_from_this<LxssUserSessionI
/// </summary>
_Guarded_by_(m_instanceLock) std::list<std::pair<GUID, LxssDistributionState>> m_lockedDistributions;

/// <summary>
/// Contains pending WSL2 exits until their exit notification or VM teardown, even if a conversion wait times out.
/// </summary>
_Guarded_by_(m_instanceLock) std::map<ULONG, PidTermination> m_pidTerminations;

/// <summary>
/// The running utility vm for WSL2 distributions.
///
Expand Down
Loading