Skip to content

Commit 2e38e84

Browse files
authored
Mesclagem automática: dev-talesam → master
Mesclagem automatizada realizada pelo build_package.py
2 parents c4a9c0c + c603e93 commit 2e38e84

7 files changed

Lines changed: 218 additions & 75 deletions

File tree

.gitignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,3 @@ cython_debug/
167167

168168
# PyPI configuration file
169169
.pypirc
170-
171-
# Claude Code
172-
CLAUDE.md
173-
.claude/

CLAUDE.md

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
systemd-swap is a dynamic swap management daemon for Linux, written in Rust. It automatically configures optimal swap strategies based on the system's filesystem and requirements, managing zswap, zram, and dynamic swap files (SwapFC).
8+
9+
The project replaces a previous Python implementation with a lightweight (~250 KB) Rust binary that provides:
10+
- Auto-detection of optimal swap strategy based on filesystem type
11+
- Zswap (compressed RAM cache) + SwapFC (dynamic swap files)
12+
- Zram (compressed block device in RAM) with optional writeback to disk
13+
- Multi-filesystem support: btrfs, ext4, xfs
14+
15+
## Building and Testing
16+
17+
```bash
18+
# Build the project (requires Rust 1.70+)
19+
cargo build --release
20+
21+
# Install to system (requires root)
22+
sudo make install
23+
24+
# Enable and start the service
25+
sudo systemctl enable --now systemd-swap
26+
27+
# Check service status
28+
systemctl status systemd-swap
29+
30+
# View detailed swap status (with extra stats as root)
31+
systemd-swap status
32+
33+
# View logs
34+
journalctl -u systemd-swap -f
35+
```
36+
37+
## Build for Arch Linux/BigLinux
38+
39+
```bash
40+
cd pkgbuild
41+
makepkg -si
42+
```
43+
44+
## Architecture
45+
46+
### Swap Modes
47+
48+
The daemon operates in one of several modes (auto-detected or manually configured):
49+
50+
1. **auto** (default): Detects filesystem and chooses optimal strategy
51+
- btrfs/ext4/xfs → zswap+swapfc
52+
- other filesystems → zram only
53+
54+
2. **zswap+swapfc**: Zswap compresses pages in RAM, SwapFC provides backing swap files
55+
- Default for btrfs/ext4/xfs systems
56+
- Zswap shrinker proactively moves cold pages to disk
57+
- Best desktop performance with efficient memory usage
58+
59+
3. **zram+swapfc**: Zram as primary compressed swap with swap files for overflow
60+
- Optional zram writeback moves idle pages to disk
61+
62+
4. **zram**: Zram-only mode (no disk swap)
63+
64+
5. **manual**: Explicit configuration via config file flags
65+
66+
### Core Components
67+
68+
**src/main.rs** (479 lines)
69+
- Entry point with CLI parsing (start/stop/status commands)
70+
- Swap mode selection logic based on filesystem detection
71+
- Main daemon loop orchestrating zswap/zram/swapfc
72+
- Signal handling for graceful shutdown
73+
74+
**src/config.rs** (172 lines)
75+
- Configuration parser supporting hierarchical config files:
76+
- `/usr/share/systemd-swap/swap-default.conf` (defaults)
77+
- `/etc/systemd/swap.conf` (user overrides)
78+
- `{/usr/lib,/run,/etc}/systemd/swap.conf.d/*.conf` (fragments)
79+
- Shell variable expansion in config values
80+
- Type-safe getters for configuration values
81+
82+
**src/swapfc.rs** (558 lines)
83+
- Dynamic swap file management
84+
- Creates/removes swap files based on memory pressure
85+
- Supports both pre-allocated (fallocate) and sparse files
86+
- Btrfs-specific: subvolume setup, compression mode support
87+
- Auto-detects filesystem type and validates support (btrfs/ext4/xfs)
88+
89+
**src/zswap.rs** (265 lines)
90+
- Zswap configuration via `/sys/module/zswap/parameters`
91+
- Parameter backup/restore on daemon stop
92+
- Status reporting with debugfs statistics (pool size, compression ratio, etc.)
93+
94+
**src/zram.rs** (380 lines)
95+
- Zram device creation and configuration
96+
- Writeback support (CONFIG_ZRAM_WRITEBACK kernel feature)
97+
- Auto-creation of loop devices for writeback when no partition specified
98+
- Size parsing: absolute (1G, 512M) or percentage (50%, 100%)
99+
100+
**src/systemd.rs** (145 lines)
101+
- Systemd integration: sd_notify, swap unit generation
102+
- Swap unit management via systemctl
103+
104+
**src/meminfo.rs** (300 lines)
105+
- Memory statistics parsing from `/proc/meminfo`
106+
- CPU count, RAM size, free RAM/swap percentage calculations
107+
- Page size detection
108+
109+
**src/helpers.rs** (148 lines)
110+
- Common utilities: file I/O, directory creation, command execution
111+
- Root privilege checking
112+
113+
**src/pre-systemd-swap** (124 lines, bash)
114+
- Pre-service script run before main daemon
115+
- Btrfs subvolume setup for swap files
116+
- Disables existing swap partitions
117+
- Adds fstab entries for swap subvolumes
118+
119+
### Configuration Flow
120+
121+
1. Load defaults from `/usr/share/systemd-swap/swap-default.conf`
122+
2. Override with `/etc/systemd/swap.conf`
123+
3. Apply fragments from conf.d directories (etc > run > lib precedence)
124+
4. Expand shell variables in values
125+
126+
### Filesystem Detection Logic
127+
128+
- Uses `findmnt` to detect filesystem type
129+
- For swap file paths, checks both the path and its parent
130+
- Validates filesystem against `SWAPFILE_SUPPORTED_FS` (btrfs/ext4/xfs)
131+
- Falls back to zram-only for unsupported filesystems
132+
133+
### Pre-allocated vs Sparse Files
134+
135+
By default, SwapFC uses **pre-allocated** files via fallocate (disabled sparse):
136+
- More stable under memory pressure
137+
- Reserves disk space upfront
138+
- Set `swapfc_use_sparse=1` to enable sparse/thin provisioning
139+
140+
### Btrfs-Specific Handling
141+
142+
- Automatically creates `@swapfc` subvolume on btrfs root
143+
- Optional compression mode: creates loop device over file on compressed subvolume
144+
- Disables CoW (copy-on-write) for swap files
145+
146+
## File Locations
147+
148+
| Path | Description |
149+
|------|-------------|
150+
| `/usr/bin/systemd-swap` | Main Rust binary |
151+
| `/usr/bin/pre-systemd-swap` | Bash pre-setup script |
152+
| `/etc/systemd/swap.conf` | User configuration |
153+
| `/usr/share/systemd-swap/swap-default.conf` | Default configuration |
154+
| `/run/systemd/swap/` | Runtime state directory |
155+
| `/swapfc/` | Default swap file location |
156+
| `/usr/lib/systemd/system/systemd-swap.service` | Main systemd unit |
157+
| `/usr/lib/systemd/system/pre-systemd-swap.service` | Pre-setup systemd unit |
158+
159+
## Key Configuration Parameters
160+
161+
**swap_mode**: auto | zswap+swapfc | zram+swapfc | zram | manual
162+
163+
**Zswap (for zswap+swapfc mode)**:
164+
- `zswap_compressor`: zstd (default), lzo, lz4, lzo-rle, lz4hc
165+
- `zswap_max_pool_percent`: Max RAM % for pool (default: 45)
166+
- `zswap_shrinker_enabled`: Proactive writeback (default: 1)
167+
168+
**Zram (for zram modes)**:
169+
- `zram_size`: 1G, 512M, 50%, 100% (default: 80%)
170+
- `zram_alg`: Compression algorithm (default: zstd)
171+
- `zram_writeback`: Enable writeback to disk (default: 0)
172+
173+
**SwapFC**:
174+
- `swapfc_chunk_size`: Per-file size, e.g., 512M, 10% of RAM
175+
- `swapfc_path`: Location for swap files (default: /swapfc/swapfile)
176+
- `swapfc_free_ram_perc`: Trigger creation when free RAM < % (default: 35)
177+
- `swapfc_use_sparse`: Enable sparse files (default: 0)
178+
179+
## Dependencies
180+
181+
- Rust 1.70+ (build time)
182+
- `libsystemd` (for sd_notify integration)
183+
- `btrfs-progs` (for btrfs features)
184+
- `util-linux` (zramctl, losetup, findmnt)
185+
186+
## Testing Configuration Changes
187+
188+
After modifying `/etc/systemd/swap.conf`:
189+
190+
```bash
191+
sudo systemctl restart systemd-swap
192+
journalctl -u systemd-swap -f
193+
systemd-swap status
194+
```
195+
196+
## Package Building
197+
198+
The project uses GitHub Actions to trigger builds on push:
199+
- Webhook dispatches to BigLinux-Package-Build repositories
200+
- Builds for both x86_64 and aarch64 (ARM)
201+
202+
## Code Style Notes
203+
204+
- Error handling uses `thiserror` crate for typed errors
205+
- Logging via custom macros: `info!()`, `warn!()`, `error!()`, `debug!()`
206+
- Systemd integration via `libsystemd` crate
207+
- Signal handling for graceful shutdown (SIGTERM, SIGINT)
208+
- Configuration uses lazy string parsing with typed getters

Makefile

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ SVC_T := $(DESTDIR)$(libdir)/systemd/system/systemd-swap.service
3333
PRE_SVC_T := $(DESTDIR)$(libdir)/systemd/system/pre-systemd-swap.service
3434
DFL_T := $(DESTDIR)$(datadir)/systemd-swap/swap-default.conf
3535
CNF_T := $(DESTDIR)$(sysconfdir)/systemd/swap.conf
36-
SYSCTL_T := $(DESTDIR)$(libdir)/sysctl.d/99-systemd-swap.conf
3736
MAN5_T := $(DESTDIR)$(mandir)/man5/swap.conf.5
3837
MAN8_T := $(DESTDIR)$(mandir)/man8/systemd-swap.8
3938

@@ -67,9 +66,6 @@ $(DFL_T): include/swap-default.conf
6766
$(CNF_T): swap.conf
6867
install -p -bDm644 -S .old $< $@
6968

70-
$(SYSCTL_T): include/99-systemd-swap.conf
71-
install -p -Dm644 $< $@
72-
7369
$(MAN5_T): man/swap.conf.5
7470
install -p -Dm644 $< $@
7571

@@ -87,15 +83,15 @@ swap.conf: include/swap-default.conf ## Generate swap.conf
8783

8884
target/release/systemd-swap: build
8985

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

9288
install: ## Install systemd-swap
9389
install: build dirs files
9490

9591
uninstall: ## Delete systemd-swap (stop systemd-swap first)
9692
uninstall:
9793
test ! -f /run/systemd/swap/swap.conf
98-
rm -v $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(SYSCTL_T) $(MAN5_T) $(MAN8_T)
94+
rm -v $(BIN_T) $(PRE_BIN_T) $(SVC_T) $(PRE_SVC_T) $(DFL_T) $(CNF_T) $(MAN5_T) $(MAN8_T)
9995
rm -rv $(LIB_T) $(DESTDIR)$(datadir)/systemd-swap
10096

10197
clean: ## Remove generated files

include/swap-default.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ swap_mode=auto
5757

5858
## swapfile_enabled=1 # Enable swap file management
5959
## swapfile_path=/swapfile # Base path for swap files
60-
## swapfile_chunk_size=1G # Size of each swap file (larger = less fragmentation)
60+
## swapfile_chunk_size=512M # Size of each swap file
6161
## swapfile_max_count=32 # Maximum number of swap files
6262

6363
################################################################################

src/autoconfig.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -351,11 +351,11 @@ impl RamProfile {
351351
pub fn recommended_mglru_min_ttl(&self) -> u32 {
352352
match self {
353353
RamProfile::UltraLow => 5000, // 5s - maximum protection
354-
RamProfile::Low => 4000, // 4s
355-
RamProfile::Medium => 3000, // 3s
356-
RamProfile::Standard => 3000, // 3s - better desktop protection
357-
RamProfile::High => 2000, // 2s
358-
RamProfile::VeryHigh => 2000, // 2s - workstations can have heavy loads too
354+
RamProfile::Low => 3000, // 3s
355+
RamProfile::Medium => 2000, // 2s
356+
RamProfile::Standard => 1000, // 1s
357+
RamProfile::High => 500, // 0.5s
358+
RamProfile::VeryHigh => 250, // 0.25s - RAM is abundant
359359
}
360360
}
361361

@@ -366,22 +366,6 @@ impl RamProfile {
366366
_ => "lz4",
367367
}
368368
}
369-
370-
/// Adjust MGLRU based on current memory pressure
371-
/// Call this at daemon startup to adapt to real conditions
372-
pub fn recommended_mglru_with_pressure(&self, pressure: crate::meminfo::MemoryPressure) -> u32 {
373-
use crate::meminfo::MemoryPressure;
374-
375-
let base = self.recommended_mglru_min_ttl();
376-
377-
// Increase protection under pressure
378-
match pressure {
379-
MemoryPressure::Low => base,
380-
MemoryPressure::Medium => base * 15 / 10, // +50%
381-
MemoryPressure::High => base * 2, // +100%
382-
MemoryPressure::Critical => base * 3, // +200%
383-
}
384-
}
385369
}
386370

387371
/// Full system capabilities
@@ -612,7 +596,7 @@ impl RecommendedConfig {
612596
zswap_max_pool_percent: 25, // Uniform for all RAM profiles
613597
swapfc_enabled: true,
614598
swapfc_directio: is_nvme, // Direct I/O only on NVMe
615-
swapfc_chunk_size: if is_nvme { "1G" } else { "512M" }.to_string(),
599+
swapfc_chunk_size: if is_nvme { "512M" } else { "256M" }.to_string(),
616600
mglru_min_ttl_ms: ram.recommended_mglru_min_ttl(),
617601
}
618602
}

src/main.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,7 @@ fn configure_mglru(config: &Config, recommended: Option<&RecommendedConfig>) {
128128
recommended.map(|r| r.mglru_min_ttl_ms).unwrap_or_else(|| {
129129
// Fallback: detect RAM and use appropriate value
130130
use systemd_swap::autoconfig::RamProfile;
131-
let ram_profile = RamProfile::detect();
132-
133-
// Detect memory pressure and adjust MGLRU accordingly
134-
if let Ok(pressure) = systemd_swap::meminfo::get_memory_pressure() {
135-
info!("Memory pressure detected: {:?}, adjusting MGLRU", pressure);
136-
ram_profile.recommended_mglru_with_pressure(pressure)
137-
} else {
138-
ram_profile.recommended_mglru_min_ttl()
139-
}
131+
RamProfile::detect().recommended_mglru_min_ttl()
140132
})
141133
});
142134

src/meminfo.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -108,39 +108,6 @@ pub fn get_cpu_count() -> usize {
108108
.unwrap_or(1)
109109
}
110110

111-
/// Memory pressure levels
112-
#[derive(Debug, Clone, Copy, PartialEq)]
113-
pub enum MemoryPressure {
114-
Low, // > 40% available
115-
Medium, // 20-40% available
116-
High, // 10-20% available
117-
Critical, // < 10% available
118-
}
119-
120-
/// Detect current memory pressure based on MemAvailable
121-
pub fn get_memory_pressure() -> Result<MemoryPressure> {
122-
let stats = get_mem_stats(&["MemTotal", "MemAvailable"])?;
123-
let percent = (stats["MemAvailable"] * 100) / stats["MemTotal"];
124-
125-
let pressure = match percent {
126-
p if p > 40 => MemoryPressure::Low,
127-
p if p > 20 => MemoryPressure::Medium,
128-
p if p > 10 => MemoryPressure::High,
129-
_ => MemoryPressure::Critical,
130-
};
131-
132-
Ok(pressure)
133-
}
134-
135-
/// Get percentage of available memory (MemAvailable)
136-
/// This is more accurate than MemFree for detecting usable memory
137-
/// as it includes reclaimable cache and buffers
138-
pub fn get_available_ram_percent() -> Result<u8> {
139-
let stats = get_mem_stats(&["MemTotal", "MemAvailable"])?;
140-
let percent = (stats["MemAvailable"] * 100) / stats["MemTotal"];
141-
Ok(percent as u8)
142-
}
143-
144111
/// Zswap statistics from debugfs
145112
#[derive(Debug, Default, Clone)]
146113
pub struct ZswapStats {

0 commit comments

Comments
 (0)