Skip to content

Commit 73e3285

Browse files
committed
Testing reverting to zram by default.
1 parent b20616a commit 73e3285

23 files changed

Lines changed: 3079 additions & 2966 deletions

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repository = "https://github.com/biglinux/biglinux-systemd-swap"
99

1010
[dependencies]
1111
clap = { version = "4", features = ["derive"] }
12-
nix = { version = "0.29", features = ["fs", "process", "signal", "user", "feature"] }
12+
nix = { version = "0.30", features = ["fs", "process", "signal", "user"] }
1313
libsystemd = "0.7"
1414
glob = "0.3"
1515
libc = "0.2"

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ DFL_T := $(DESTDIR)$(datadir)/systemd-swap/swap-default.conf
3535
CNF_T := $(DESTDIR)$(sysconfdir)/systemd/swap.conf
3636
MAN5_T := $(DESTDIR)$(mandir)/man5/swap.conf.5
3737
MAN8_T := $(DESTDIR)$(mandir)/man8/systemd-swap.8
38+
SYSCTL_T := $(DESTDIR)$(sysconfdir)/sysctl.d/91-memory.conf
3839

3940
.PHONY: build files dirs install uninstall clean help
4041

@@ -72,6 +73,9 @@ $(MAN5_T): man/swap.conf.5
7273
$(MAN8_T): man/systemd-swap.8
7374
install -p -Dm644 $< $@
7475

76+
$(SYSCTL_T): include/91-memory.conf
77+
install -p -Dm644 $< $@
78+
7579
define banner
7680
# This file is part of systemd-swap.\n#\n# Entries in this file show the systemd-swap defaults as\n# specified in $(datarootdir)/systemd-swap/swap-default.conf\n# You can change settings by editing this file.\n# Defaults can be restored by simply deleting this file.\n#\n# See swap.conf(5) and $(datarootdir)/systemd-swap/swap-default.conf for details.\n\n
7781
endef
@@ -83,15 +87,15 @@ swap.conf: include/swap-default.conf ## Generate swap.conf
8387

8488
target/release/systemd-swap: build
8589

86-
files: $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(MAN5_T) $(MAN8_T)
90+
files: $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(MAN5_T) $(MAN8_T) $(SYSCTL_T)
8791

8892
install: ## Install systemd-swap
8993
install: build dirs files
9094

9195
uninstall: ## Delete systemd-swap (stop systemd-swap first)
9296
uninstall:
9397
test ! -f /run/systemd/swap/swap.conf
94-
rm -v $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(MAN5_T) $(MAN8_T)
98+
rm -v $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(MAN5_T) $(MAN8_T) $(SYSCTL_T)
9599
rm -rv $(LIB_T) $(DESTDIR)$(datadir)/systemd-swap
96100

97101
clean: ## Remove generated files

README.md

Lines changed: 204 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,126 @@
22

33
Smart dynamic swap management for Linux, written in Rust.
44

5-
## Overview
5+
Automatically detects hardware, selects the best swap strategy, and tunes the
6+
kernel for optimal memory management — no manual configuration required.
67

7-
`systemd-swap` automatically configures the best swap strategy for your system:
8+
## How It Works
89

9-
- **Auto-detection**: Smartly chooses between Zswap and Zram based on your filesystem.
10-
- **Zswap + SwapFC**: (Default for Btrfs/Ext4/XFS) Uses compressed RAM cache + dynamic swap files. Most efficient for desktops.
11-
- **Zram**: (Default for others) Uses compressed RAM block device. Good for systems without disk swap support.
12-
- **Dynamic Scaling**: Creates swap files on-demand, starting small and growing as needed.
10+
### Swap Modes
11+
12+
| Mode | Primary | Secondary | Selection |
13+
|------|---------|-----------|-----------|
14+
| `auto` | Auto-detected | Auto-detected | **Default** — recommended |
15+
| `zram+swapfile` | Zram (RAM) | Swap files (disk) | btrfs / ext4 / xfs with free space |
16+
| `zswap+swapfile` | Zswap (kernel) | Swap files (disk) | Large disk, SSD/NVMe |
17+
| `zram` | Zram (RAM) | None | LiveCD, low disk, tmpfs |
18+
| `manual` | Explicit flags | Explicit flags | Advanced users |
19+
| `disabled` ||| Service exits cleanly |
20+
21+
### Auto-Detection Logic
22+
23+
In `auto` mode, the daemon checks:
24+
25+
1. **LiveCD?** (tmpfs/squashfs/overlay root) → `zram` only
26+
2. **Filesystem supports swap files?** (btrfs/ext4/xfs) → if no, `zram` only
27+
3. **Free disk ≥ RAM?** → if no, `zram` only
28+
4. **Otherwise**`zram+swapfile` (zram primary + disk overflow)
29+
30+
### Zram Pool Architecture
31+
32+
The daemon manages a **dynamic pool of zram devices** that expands and
33+
contracts based on demand:
34+
35+
- **Initial pool**: one device per CPU core (max 8 devices)
36+
- **Expansion**: adds a device when pool utilization exceeds 85%
37+
- **Contraction**: removes idle devices when utilization drops below 20% for 120s
38+
- **Monitoring interval**: 5 seconds
39+
40+
Each zram device uses:
41+
- **Algorithm**: zstd (level 3) — best ratio-to-speed balance
42+
- **Disksize**: 150% of RAM (virtual/uncompressed size)
43+
- **No mem_limit**: prevents write errors that block kernel fallback to disk swap
44+
- **Priority**: 32767 (maximum — kernel uses zram before disk swap)
45+
46+
Physical RAM usage is naturally limited by the kernel's memory watermarks
47+
and the daemon's free-RAM guard (adaptive check before each expansion).
48+
49+
**Compression ratios** (typical):
50+
- Desktop workloads: 3–4x
51+
- Server / text-heavy: 5–10x
52+
- Incompressible data (media, encrypted): ~1x
53+
54+
### Swap Files (Overflow)
55+
56+
In `zram+swapfile` mode, swap files provide emergency overflow:
57+
58+
- **Size**: 512MB each, created on demand
59+
- **Maximum**: 28 files (14GB total capacity)
60+
- **Priority**: -1 (kernel only uses when zram is full)
61+
- **NOCOW**: enabled on btrfs (prevents deadlock under pressure)
62+
- **Created when**: free RAM < 20% or free swap < 40%
63+
- **Removed when**: free swap > 70%
64+
65+
### Zswap Mode
66+
67+
In `zswap+swapfile` mode, the kernel's zswap handles compression:
68+
69+
- Compresses pages before writing to disk swap
70+
- Shrinker moves cold compressed pages to disk automatically
71+
- Pool limited to 45% of RAM
72+
- Requires disk-backed swap files as backing storage
73+
74+
## Recommended Kernel Tuning
75+
76+
The following kernel parameters are **not applied by the daemon** — they are
77+
recommendations for optimal performance with zram/zswap. Configure them
78+
via `/etc/sysctl.d/99-swap.conf` or your distribution's tuning service.
79+
80+
### Memory Management
81+
82+
| Parameter | Value | Purpose |
83+
|-----------|-------|---------|
84+
| `vm.swappiness` | 120 (zram+swapfile) / 180 (zram only) | Prefer swap over file cache (zram is in-memory, so swapping is fast) |
85+
| `vm.min_free_kbytes` | 3% of RAM (max 512MB) | Emergency reserve — gives kswapd headroom before OOM |
86+
| `vm.watermark_scale_factor` | 150 (1.5%) | Gap between min/low/high watermarks for kswapd headroom |
87+
| `vm.vfs_cache_pressure` | 75 | Balance between VFS cache retention and anonymous page reclaim |
88+
| `vm.dirty_ratio` | 10 | Max dirty pages before blocking writes (reduces memory pressure) |
89+
| `vm.dirty_background_ratio` | 3 | Start background writeback early |
90+
| `vm.page-cluster` | 0 (zram) / 2 (zswap) | Pages read per swap-in. 0 = page-at-a-time (optimal for zram) |
91+
92+
### Memory Compaction
93+
94+
| Parameter | Value | Purpose |
95+
|-----------|-------|---------|
96+
| `vm.compaction_proactiveness` | 20 | Background defragmentation level. Lower avoids CPU waste with zram-heavy workloads |
97+
| `vm.watermark_boost_factor` | 15000 | Boosts watermarks after fragmentation events for compaction recovery |
98+
| `vm.extfrag_threshold` | 300 | Eagerness to compact vs reclaim. Lower = more willing to compact |
99+
100+
### Transparent Huge Pages
101+
102+
| Parameter | Value | Purpose |
103+
|-----------|-------|---------|
104+
| THP enabled | `madvise` | Only apps requesting huge pages get them — avoids compaction stalls |
105+
| mTHP 64kB | `madvise` | 64kB folios via madvise — reduces swap I/O overhead |
106+
107+
### MGLRU (Multi-Gen LRU)
108+
109+
| Parameter | Value | Purpose |
110+
|-----------|-------|---------|
111+
| `min_ttl_ms` | 1000 | Pages younger than 1s are never reclaimed — protects working set from thrashing |
13112

14113
## Installation
15114

16-
### Arch Linux / BigLinux / Manjaro...
115+
### Arch Linux / BigLinux / Manjaro
116+
17117
```bash
18118
cd pkgbuild
19119
makepkg -si
20120
```
21121

22122
### Manual Build
23-
Requirements: Rust 1.70+, `btrfs-progs`, `util-linux`
123+
124+
Requirements: Rust 1.70+, `util-linux`
24125

25126
```bash
26127
cargo build --release
@@ -30,46 +131,126 @@ sudo systemctl enable --now systemd-swap
30131

31132
## Usage
32133

33-
Check status:
134+
### Check Status
135+
34136
```bash
35137
systemd-swap status
36138
```
37139

38-
Reload configuration:
140+
Shows zram pool stats (compression ratio, utilization, device count),
141+
swap file details, and memory breakdown.
142+
143+
### Show Recommended Config
144+
145+
```bash
146+
sudo systemd-swap autoconfig
147+
```
148+
149+
Displays the auto-detected configuration for the current hardware.
150+
151+
### Restart
152+
39153
```bash
40154
sudo systemctl restart systemd-swap
41155
```
42156

157+
### View Logs
158+
159+
```bash
160+
journalctl -u systemd-swap -f
161+
```
162+
43163
## Configuration
44164

45-
Configuration is located at `/etc/systemd/swap.conf`.
165+
Configuration files (in order of priority):
166+
167+
1. `/usr/share/systemd-swap/swap-default.conf` — defaults (do not edit)
168+
2. `/etc/systemd/swap.conf` — user overrides
169+
3. `/etc/systemd/swap.conf.d/*.conf` — drop-in fragments
170+
171+
All options support `${NCPU}` and `${RAM_SIZE}` variables, plus simple
172+
arithmetic with `$(( expr ))`.
46173

47174
### Common Options
48175

49-
**Change Swap Mode:**
176+
**Change swap mode:**
177+
```ini
178+
swap_mode=zram+swapfile # or: auto, zram, zswap+swapfile, manual, disabled
179+
```
180+
181+
**Customize zram size:**
50182
```ini
51-
# Modes: auto, zswap+swapfc, zram, manuall
52-
swap_mode=auto
183+
zram_size=200% # Virtual disksize (% of RAM)
53184
```
54185

55-
**Customize Zram Size:**
186+
**Customize swap file location:**
56187
```ini
57-
zram_size=50%
188+
swapfile_path=/mnt/data/swapfile
58189
```
59190

60-
**Customize Swap File Location:**
191+
**Adjust anti-thrashing protection:**
61192
```ini
62-
swapfc_path=/mnt/data/swapfile
193+
mglru_min_ttl_ms=3000 # Higher = more protection, less reclaim
63194
```
64195

65-
See `/usr/share/systemd-swap/swap-default.conf` for all available options and defaults.
196+
**Customize zram pool behavior:**
197+
```ini
198+
zram_expand_threshold=90 # Expand pool above this utilization %
199+
zram_contract_threshold=15 # Contract pool below this utilization %
200+
```
201+
202+
### Full Option Reference
203+
204+
See `/usr/share/systemd-swap/swap-default.conf` for all available options
205+
with descriptions.
206+
207+
## Architecture
208+
209+
```
210+
systemd-swap (Rust daemon)
211+
├── main.rs — CLI (clap), mode dispatch, kernel tuning, THP/MGLRU
212+
├── lib.rs — Module declarations, global SHUTDOWN flag
213+
├── config.rs — Config parser (key=value, ${VAR} expansion, arithmetic)
214+
├── autoconfig.rs — Hardware detection, recommended config generation
215+
├── zram.rs — Dynamic zram pool (expansion, contraction, monitoring)
216+
├── swapfile.rs — Dynamic swap file management (NOCOW, loop-backed)
217+
├── zswap.rs — Zswap kernel module configuration
218+
├── meminfo.rs — /proc/meminfo parser, effective swap calculation
219+
├── systemd.rs — Systemd unit generation, sd-notify
220+
└── helpers.rs — Shared utilities (parse_size, fs detection, logging)
221+
```
222+
223+
### Data Flow
224+
225+
```
226+
Memory pressure (free RAM < threshold)
227+
→ MGLRU protects working set (pages < 1s old)
228+
→ Kernel swaps cold anonymous pages:
229+
├─ zram: compress with zstd level 3 → store in RAM
230+
│ ├─ Pool utilization > 85% → daemon adds zram device
231+
│ └─ All disksize consumed → kernel falls back to swapfiles
232+
└─ zswap: compress in kernel pool → shrinker writes back to disk
233+
234+
SwapFile monitor (1s interval):
235+
├─ free_ram < 20% → create 512MB swap file
236+
├─ free_ram < 5% → emergency: create immediately
237+
└─ free_swap > 70% → remove unused swap file
238+
239+
ZramPool monitor (5s interval):
240+
├─ utilization > 85% → add zram device (up to 8)
241+
└─ utilization < 20% (120s stable) → remove idle device
242+
```
66243

67244
## Features
68245

69-
- **Zero Configuration**: Works out of the box for most systems.
70-
- **Sparse Files**: Swap files use zero disk space until data is actually written (when using Zswap).
71-
- **MGLRU Support**: Integrates with Multi-Gen LRU (Kernel 6.1+) to prevent thrashing.
72-
- **Zram Writeback**: Can offload cold pages from Zram to disk.
246+
- **Zero configuration**: works out of the box for any system
247+
- **Dynamic scaling**: creates/removes swap resources on demand
248+
- **MGLRU integration**: protects working set from premature eviction (kernel 6.1+)
249+
- **mTHP support**: 64kB folios for efficient zram swap I/O
250+
- **Zswap disabled for zram**: prevents double compression per kernel docs
251+
- **NOCOW swap files**: safe on btrfs under memory pressure
252+
- **Adopt on restart**: reuses existing zram devices and swap files without swapoff
253+
- **Graceful shutdown**: restores all kernel parameters on stop
73254

74255
## License
75256

include/91-memory.conf

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Recommended kernel tuning for zram/zswap swap management.
2+
# Install to /etc/sysctl.d/91-memory.conf
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
#
5+
# These parameters optimize memory management for systems using
6+
# zram (compressed RAM swap) or zswap (compressed cache + disk swap).
7+
# Values are uniform — no RAM-size differentiation needed.
8+
#
9+
# NOTE: vm.min_free_kbytes is calculated dynamically by the
10+
# pre-systemd-swap service (3% of RAM, max 512MB).
11+
12+
# ── Swap readahead ──
13+
# Pages read per swap-in operation (2^N pages).
14+
# 0 = page-at-a-time (optimal for zram: each page compressed individually).
15+
# For zswap+disk: use 2 (4 pages = 16KB) to amortize disk I/O.
16+
vm.page-cluster = 0
17+
18+
# ── Swappiness ──
19+
vm.swappiness = 30
20+
21+
# ── Cache reclaim ──
22+
# Balance VFS cache retention vs anonymous page reclaim.
23+
# Default 100 aggressively reclaims directory/inode caches.
24+
# 75 retains more VFS caches while still allowing reclaim.
25+
vm.vfs_cache_pressure = 75
26+
27+
# ── Watermarks ──
28+
# Gap between min/low/high watermarks (in 0.01% units).
29+
# Default 10 (0.1%) is too tight for zram. 150 (1.5%) gives
30+
# kswapd enough headroom to reclaim before OOM.
31+
vm.watermark_scale_factor = 150
32+
33+
# Boost watermarks after fragmentation events for compaction recovery.
34+
vm.watermark_boost_factor = 15000
35+
36+
# ── Dirty page writeback ──
37+
# Flush dirty pages to disk early, freeing RAM before swap is needed.
38+
# default background_ratio=10, ratio=20. Lowering frees memory sooner.
39+
vm.dirty_background_ratio = 5
40+
vm.dirty_ratio = 15
41+
42+
# ── Memory compaction ──
43+
# Background defragmentation level. Lower avoids CPU waste when
44+
# unmovable slab pages (zram zsmalloc, btrfs) make compaction futile.
45+
vm.compaction_proactiveness = 20
46+
47+
# Eagerness to compact vs reclaim. Lower = more willing to compact.
48+
vm.extfrag_threshold = 300
49+
50+
# ── Memory reserves ──
51+
# Reserve for root/admin logins during OOM situations.
52+
vm.admin_reserve_kbytes = 131072
53+
54+
# ── OOM killer ──
55+
vm.oom_kill_allocating_task = 1
56+
vm.panic_on_oom = 0

0 commit comments

Comments
 (0)