Skip to content

Rework the memory reduction thread around explicit reclaim helpers#40777

Open
benhillis wants to merge 1 commit into
microsoft:masterfrom
benhillis:benhill/mem-reclaim-1-helpers
Open

Rework the memory reduction thread around explicit reclaim helpers#40777
benhillis wants to merge 1 commit into
microsoft:masterfrom
benhillis:benhill/mem-reclaim-1-helpers

Conversation

@benhillis

@benhillis benhillis commented Jun 11, 2026

Copy link
Copy Markdown
Member

First in a series of incremental changes reworking the WSL2 guest memory reduction thread. Each change is submitted one PR at a time; the next will follow once this merges. This PR is behavior-preserving relative to the existing idle-gated reclaim and introduces no default change.

What this does

Replaces the ring-buffer idle detector and user-CPU-only sampling in the mini-init memory reduction thread with a clearer, helper-based design:

  • Samples aggregate non-idle CPU time (user, system, irq, softirq, steal) so kernel-bound work keeps the VM out of the idle state, instead of looking at user time alone.
  • ReadProcFile reads a full procfs snapshot into a caller buffer (close-on-exec, partial-read safe); GetReclaimableCacheBytes / GetFreeMemoryBytes read the relevant counters through it.
  • Gradual reclaims cold page cache (cgroup memory.reclaim) above a fixed floor while CPU-idle, with a hysteresis margin so it does not churn near the floor.
  • DropCache stays gated on sustained CPU idle, drops once, and re-drops only after the reclaimable cache grows meaningfully.
  • Compaction is gated on free-memory growth so it runs only when there are newly-freed pages worth coalescing.

RequestCgroupReclaim performs the memory.reclaim write best-effort: it treats the kernel's expected EAGAIN (some, but not all, pages evicted) as success without logging, and never throws, so a transient write error cannot tear down the long-lived reduction thread.

Series (submitted sequentially)

  1. this PR — rework around explicit reclaim helpers
  2. drive gradual reclaim by memory pressure (PSI)
  3. adaptive working-set floor via refaults
  4. make gradual the default

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the WSL2 mini-init memory reduction thread to use explicit helper functions for procfs reads and to base “idle” detection on aggregate non-idle CPU time, enabling gradual reclaim/drop-cache/compaction policies to be expressed more clearly while aiming to preserve existing idle-gated behavior.

Changes:

  • Introduces ReadProcFile and ReadAggregateCpuTimes to read procfs reliably and compute busy-vs-idle CPU deltas across intervals.
  • Adds helpers to compute reclaimable cache bytes (/proc/meminfo) and free memory bytes (sysinfo) and uses them to drive gradual reclaim, drop_caches, and compaction policies.
  • Replaces the prior ring-buffer/user-time-only logic with a stateful, tick-based policy loop using explicit thresholds and hysteresis.

Comment thread src/linux/init/util.cpp
Comment thread src/linux/init/util.cpp
Comment thread src/linux/init/util.cpp Outdated
@benhillis benhillis force-pushed the benhill/mem-reclaim-1-helpers branch from 12dd190 to 51f1c8e Compare June 11, 2026 18:10
Copilot AI review requested due to automatic review settings June 11, 2026 19:23
@benhillis benhillis force-pushed the benhill/mem-reclaim-1-helpers branch from 51f1c8e to 4730acd Compare June 11, 2026 19:23
@benhillis benhillis force-pushed the benhill/mem-reclaim-1-helpers branch from 4730acd to 8e6f845 Compare June 11, 2026 19:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Replace the ring-buffer idle detector and user-CPU-only sampling in the
mini-init memory reduction thread with a clearer, helper-based design:

- Sample aggregate non-idle CPU time (user, system, irq, softirq, steal)
  so kernel-bound work keeps the VM out of the idle state, instead of
  looking at user time alone.
- ReadProcFile reads a full procfs snapshot into a caller buffer
  (close-on-exec, partial-read safe); GetReclaimableCacheBytes /
  GetFreeMemoryBytes read the relevant counters through it.
- Gradual mode reclaims cold page cache (cgroup memory.reclaim) above a
  fixed floor while CPU-idle, with a hysteresis margin so it does not
  churn near the floor.
- DropCache mode stays gated on sustained CPU idle, drops once, and
  re-drops only after the reclaimable cache grows meaningfully.
- Compaction is gated on free-memory growth so it runs only when there
  are newly-freed pages worth coalescing.

RequestCgroupReclaim performs the memory.reclaim write best-effort: it
treats the kernel's expected EAGAIN (some, but not all, pages evicted)
as success without logging, and never throws so a transient write error
cannot tear down the long-lived reduction thread.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benhillis benhillis force-pushed the benhill/mem-reclaim-1-helpers branch from 8e6f845 to 3ed82ea Compare June 11, 2026 20:06
@benhillis benhillis marked this pull request as ready for review June 12, 2026 02:55
@benhillis benhillis requested a review from a team as a code owner June 12, 2026 02:55
Copilot AI review requested due to automatic review settings June 12, 2026 02:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.

Comment thread src/linux/init/util.cpp
Comment on lines +4026 to +4030
if (!State.HavePreviousSample)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
Stop = GetUserCpuTime();
THROW_LAST_ERROR_IF(Stop == -1);
if ((Stop - Start) < IdleThreshold)
{
THROW_LAST_ERROR_IF(WriteToFile("/proc/sys/vm/compact_memory", "1\n") < 0);
}
State.PreviousBusy = Busy;
State.PreviousIdle = Idle;
State.HavePreviousSample = true;
Comment thread src/linux/init/util.cpp
Comment on lines 3722 to +3726
struct sysinfo Info = {};
THROW_LAST_ERROR_IF(sysinfo(&Info) < 0);
return (Info.totalram - Info.freeram) * Info.mem_unit;
if (sysinfo(&Info) < 0)
{
LOG_ERROR("sysinfo failed {}", errno);
return -1;
Comment thread src/linux/init/util.cpp
Comment on lines +3745 to +3748
// Gradual: reclaimable cache below this floor is always retained; only the excess above it (beyond a
// hysteresis margin) is reclaimed.
constexpr long long c_floorBaseBytes = 128ll * 1024 * 1024;
constexpr long long c_gradualHysteresisBytes = 128ll * 1024 * 1024;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants