@@ -43,10 +43,10 @@ enum Commands {
4343#[ derive( Debug , Clone , Copy , PartialEq ) ]
4444enum SwapMode {
4545 Auto ,
46- ZramSwapfc , // zram + writeback to swap files (best for desktop!)
47- ZswapSwapfc , // zswap + swap files (alternative)
48- ZramOnly , // zram only (for non-btrfs)
49- Disabled , // Swap management disabled (service exits cleanly)
46+ ZramSwapfc , // zram + writeback to swap files (best for desktop!)
47+ ZswapSwapfc , // zswap + swap files (alternative)
48+ ZramOnly , // zram only (for non-btrfs)
49+ Disabled , // Swap management disabled (service exits cleanly)
5050}
5151
5252fn main ( ) {
@@ -72,11 +72,14 @@ fn main() {
7272 }
7373}
7474
75-
76-
7775/// Parse swap_mode from config
7876fn get_swap_mode ( config : & Config ) -> SwapMode {
79- match config. get ( "swap_mode" ) . unwrap_or ( "auto" ) . to_lowercase ( ) . as_str ( ) {
77+ match config
78+ . get ( "swap_mode" )
79+ . unwrap_or ( "auto" )
80+ . to_lowercase ( )
81+ . as_str ( )
82+ {
8083 "zram+swapfc" | "zram_swapfc" => SwapMode :: ZramSwapfc ,
8184 "zswap+swapfc" | "zswap" | "zswap+swapfile" => SwapMode :: ZswapSwapfc ,
8285 "zram" | "zram_only" => SwapMode :: ZramOnly ,
@@ -94,7 +97,7 @@ fn get_swap_mode(config: &Config) -> SwapMode {
9497/// - Unpredictable memory pressure behavior
9598fn disable_zswap_for_zram ( ) {
9699 use systemd_swap:: zswap;
97-
100+
98101 if zswap:: is_available ( ) && zswap:: is_enabled ( ) {
99102 info ! ( "Disabling zswap (recommended when using zram)" ) ;
100103 let zswap_enabled = "/sys/module/zswap/parameters/enabled" ;
@@ -110,32 +113,35 @@ fn disable_zswap_for_zram() {
110113/// Sets min_ttl_ms which protects the working set from premature eviction
111114fn configure_mglru ( config : & Config , recommended : Option < & RecommendedConfig > ) {
112115 const MGLRU_MIN_TTL_PATH : & str = "/sys/kernel/mm/lru_gen/min_ttl_ms" ;
113-
116+
114117 // Check if MGLRU is available
115118 if !Path :: new ( MGLRU_MIN_TTL_PATH ) . exists ( ) {
116119 return ;
117120 }
118-
121+
119122 // Get configured value, or use recommended value from autoconfig
120- let min_ttl_ms: u32 = config. get_opt ( "mglru_min_ttl_ms" )
123+ let min_ttl_ms: u32 = config
124+ . get_opt ( "mglru_min_ttl_ms" )
121125 . and_then ( |v| v. parse :: < u32 > ( ) . ok ( ) )
122126 . unwrap_or_else ( || {
123127 // Use recommended value if available, otherwise default based on RAM
124- recommended. map ( |r| r. mglru_min_ttl_ms )
125- . unwrap_or_else ( || {
126- // Fallback: detect RAM and use appropriate value
127- use systemd_swap:: autoconfig:: RamProfile ;
128- RamProfile :: detect ( ) . recommended_mglru_min_ttl ( )
129- } )
128+ recommended. map ( |r| r. mglru_min_ttl_ms ) . unwrap_or_else ( || {
129+ // Fallback: detect RAM and use appropriate value
130+ use systemd_swap:: autoconfig:: RamProfile ;
131+ RamProfile :: detect ( ) . recommended_mglru_min_ttl ( )
132+ } )
130133 } ) ;
131-
134+
132135 if min_ttl_ms == 0 {
133136 return ;
134137 }
135-
138+
136139 // Apply the setting
137140 match fs:: write ( MGLRU_MIN_TTL_PATH , min_ttl_ms. to_string ( ) ) {
138- Ok ( _) => info ! ( "MGLRU: min_ttl_ms = {} (working set protection)" , min_ttl_ms) ,
141+ Ok ( _) => info ! (
142+ "MGLRU: min_ttl_ms = {} (working set protection)" ,
143+ min_ttl_ms
144+ ) ,
139145 Err ( e) => warn ! ( "MGLRU: failed to set min_ttl_ms: {}" , e) ,
140146 }
141147}
@@ -147,26 +153,43 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
147153 // Clean up any previous instance
148154 let _ = stop ( true ) ;
149155
156+ // Clean up legacy swapfc/swapfile path
157+ let legacy_path = Path :: new ( "/swapfc/swapfile" ) ;
158+ if legacy_path. exists ( ) {
159+ info ! ( "Removing legacy path: {}" , legacy_path. display( ) ) ;
160+ if legacy_path. is_dir ( ) {
161+ let _ = fs:: remove_dir_all ( legacy_path) ;
162+ } else {
163+ let _ = fs:: remove_file ( legacy_path) ;
164+ }
165+ }
166+
150167 // Initialize directories
151168 makedirs ( WORK_DIR ) ?;
152- makedirs ( format ! ( "{}/system/local-fs.target.wants" , systemd_swap:: config:: RUN_SYSD ) ) ?;
153- makedirs ( format ! ( "{}/system/swap.target.wants" , systemd_swap:: config:: RUN_SYSD ) ) ?;
169+ makedirs ( format ! (
170+ "{}/system/local-fs.target.wants" ,
171+ systemd_swap:: config:: RUN_SYSD
172+ ) ) ?;
173+ makedirs ( format ! (
174+ "{}/system/swap.target.wants" ,
175+ systemd_swap:: config:: RUN_SYSD
176+ ) ) ?;
154177
155178 let mut config = Config :: load ( ) ?;
156179 let swap_mode = get_swap_mode ( & config) ;
157-
180+
158181 // Detect system capabilities early for autoconfig
159182 let caps = SystemCapabilities :: detect ( ) ;
160183 let recommended = RecommendedConfig :: from_capabilities ( & caps) ;
161-
184+
162185 // Configure MGLRU early (protects working set during swap operations)
163186 configure_mglru ( & config, Some ( & recommended) ) ;
164187
165188 // Determine effective mode based on filesystem type
166189 let effective_mode = match swap_mode {
167190 SwapMode :: Auto => {
168191 config. apply_autoconfig ( & recommended) ;
169-
192+
170193 if recommended. use_zswap {
171194 info ! ( "Auto-detected: using zswap + swapfc" ) ;
172195 SwapMode :: ZswapSwapfc
@@ -185,10 +208,10 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
185208 SwapMode :: ZramSwapfc => {
186209 // Desktop-optimized mode: zram for speed + swapfc for overflow
187210 // zram is faster than zswap because it's a dedicated block device
188-
211+
189212 // Disable zswap when using zram (per kernel documentation)
190213 disable_zswap_for_zram ( ) ;
191-
214+
192215 // Set up signal handler
193216 signal_hook:: flag:: register (
194217 signal_hook:: consts:: SIGTERM ,
@@ -208,7 +231,7 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
208231 if config. get_bool ( "zram_writeback" ) {
209232 let wb_config = systemd_swap:: zram:: ZramWritebackConfig :: from_config ( & config) ;
210233 let mut wb_manager = systemd_swap:: zram:: ZramWritebackManager :: new ( wb_config) ;
211-
234+
212235 // Run writeback manager in background thread
213236 std:: thread:: spawn ( move || {
214237 if let Err ( e) = wb_manager. run ( ) {
@@ -220,7 +243,7 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
220243 // Create swapfc for overflow/writeback (lower priority)
221244 info ! ( "Setting up swapfc as secondary swap for overflow..." ) ;
222245 let mut swapfc = SwapFile :: new ( & config) ?;
223-
246+
224247 // Create initial swap file
225248 swapfc. create_initial_swap ( ) ?;
226249
@@ -231,7 +254,7 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
231254 SwapMode :: ZswapSwapfc => {
232255 // For zswap: create swap file FIRST, then enable zswap
233256 // zswap needs a backing swap device to work
234-
257+
235258 // Set up signal handler
236259 signal_hook:: flag:: register (
237260 signal_hook:: consts:: SIGTERM ,
@@ -243,7 +266,7 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
243266
244267 // Create initial swap file and start monitoring
245268 let mut swapfc = SwapFile :: new ( & config) ?;
246-
269+
247270 // Force create first swap chunk immediately (zswap needs backing swap)
248271 info ! ( "Creating initial swap file for zswap backing..." ) ;
249272 swapfc. create_initial_swap ( ) ?;
@@ -263,10 +286,10 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
263286
264287 SwapMode :: ZramOnly => {
265288 // For zram: just set up zram, no swap files needed
266-
289+
267290 // Disable zswap when using zram (per kernel documentation)
268291 disable_zswap_for_zram ( ) ;
269-
292+
270293 // Set up signal handler for clean shutdown
271294 signal_hook:: flag:: register (
272295 signal_hook:: consts:: SIGTERM ,
@@ -275,18 +298,18 @@ fn start() -> Result<(), Box<dyn std::error::Error>> {
275298 ctrlc:: set_handler ( move || {
276299 request_shutdown ( ) ;
277300 } ) ?;
278-
301+
279302 if let Err ( e) = systemd_swap:: zram:: start ( & config) {
280303 error ! ( "Zram: {}" , e) ;
281304 }
282305 notify_ready ( ) ;
283306 info ! ( "Zram setup complete" ) ;
284-
307+
285308 // If writeback is enabled, run the smart writeback manager
286309 if config. get_bool ( "zram_writeback" ) {
287310 let wb_config = systemd_swap:: zram:: ZramWritebackConfig :: from_config ( & config) ;
288311 let mut wb_manager = systemd_swap:: zram:: ZramWritebackManager :: new ( wb_config) ;
289-
312+
290313 // This blocks and monitors zram writeback
291314 if let Err ( e) = wb_manager. run ( ) {
292315 warn ! ( "Zram writeback manager error: {}" , e) ;
@@ -340,7 +363,7 @@ fn stop(on_init: bool) -> Result<(), Box<dyn std::error::Error>> {
340363 let config = Config :: load ( ) ?;
341364
342365 // Stop all managed swap units
343- for subsystem in [ "swapfc " , "zram" ] {
366+ for subsystem in [ "swapfile " , "zram" ] {
344367 for unit_path in find_swap_units ( ) {
345368 if let Ok ( content) = read_file ( & unit_path) {
346369 if content. to_lowercase ( ) . contains ( subsystem) {
@@ -349,7 +372,7 @@ fn stop(on_init: bool) -> Result<(), Box<dyn std::error::Error>> {
349372 let _ = swapoff ( & dev) ;
350373 force_remove ( & unit_path, true ) ;
351374
352- if subsystem == "swapfc " && Path :: new ( & dev) . is_file ( ) {
375+ if subsystem == "swapfile " && Path :: new ( & dev) . is_file ( ) {
353376 force_remove ( & dev, true ) ;
354377 } else if subsystem == "zram" {
355378 let _ = systemd_swap:: zram:: release ( & dev) ;
@@ -383,8 +406,7 @@ fn stop(on_init: bool) -> Result<(), Box<dyn std::error::Error>> {
383406 let _ = fs:: remove_dir_all ( WORK_DIR ) ;
384407
385408 // Remove swap files
386- let swapfile_path = config. get ( "swapfile_path" )
387- . unwrap_or ( "/swapfile" ) ;
409+ let swapfile_path = config. get ( "swapfile_path" ) . unwrap_or ( "/swapfile" ) ;
388410 info ! ( "Removing files in {}..." , swapfile_path) ;
389411 if let Ok ( entries) = fs:: read_dir ( swapfile_path) {
390412 for entry in entries. flatten ( ) {
@@ -419,7 +441,7 @@ fn status() -> Result<(), Box<dyn std::error::Error>> {
419441 if usage. zswap_active {
420442 let zswap_original = swap_used. saturating_sub ( usage. swap_used_disk ) ;
421443 let zswap_compressed = usage. zswap_pool_bytes ;
422-
444+
423445 let ratio = if zswap_original > 0 {
424446 ( zswap_compressed as f64 / zswap_original as f64 ) * 100.0
425447 } else {
@@ -428,22 +450,30 @@ fn status() -> Result<(), Box<dyn std::error::Error>> {
428450
429451 println ! ( ) ;
430452 println ! ( " === Pool Statistics ===" ) ;
431- println ! ( " pool_size: {:.1} MiB (compressed)" , zswap_compressed as f64 / 1024.0 / 1024.0 ) ;
432- println ! ( " stored_data: {:.1} MiB (original)" , zswap_original as f64 / 1024.0 / 1024.0 ) ;
453+ println ! (
454+ " pool_size: {:.1} MiB (compressed)" ,
455+ zswap_compressed as f64 / 1024.0 / 1024.0
456+ ) ;
457+ println ! (
458+ " stored_data: {:.1} MiB (original)" ,
459+ zswap_original as f64 / 1024.0 / 1024.0
460+ ) ;
433461 println ! ( " pool_utilization: {}%" , usage. zswap_pool_percent) ;
434462 println ! ( " compress_ratio: {:.0}%" , ratio) ;
435463
436464 // If running as root, show additional debugfs stats
437465 if is_root && ( zswap. stored_pages > 0 || zswap. written_back_pages > 0 ) {
438466 let page_size = get_page_size ( ) ;
439-
467+
440468 println ! ( ) ;
441469 println ! ( " === Writeback Statistics (debugfs) ===" ) ;
442470 println ! ( " stored_pages: {}" , zswap. stored_pages) ;
443471 println ! ( " same_filled_pages: {}" , zswap. same_filled_pages) ;
444- println ! ( " written_back_pages: {} ({:.1} MiB)" ,
445- zswap. written_back_pages,
446- ( zswap. written_back_pages * page_size) as f64 / 1024.0 / 1024.0 ) ;
472+ println ! (
473+ " written_back_pages: {} ({:.1} MiB)" ,
474+ zswap. written_back_pages,
475+ ( zswap. written_back_pages * page_size) as f64 / 1024.0 / 1024.0
476+ ) ;
447477 println ! ( " pool_limit_hit: {}" , zswap. pool_limit_hit) ;
448478 println ! ( " reject_reclaim_fail: {}" , zswap. reject_reclaim_fail) ;
449479 }
@@ -452,9 +482,18 @@ fn status() -> Result<(), Box<dyn std::error::Error>> {
452482 if swap_used > 0 {
453483 println ! ( ) ;
454484 println ! ( " === Effective Swap Usage ===" ) ;
455- println ! ( " kernel_reported_used: {:.1} MiB" , swap_used as f64 / 1024.0 / 1024.0 ) ;
456- println ! ( " in_zswap_pool (RAM): {:.1} MiB" , zswap_original as f64 / 1024.0 / 1024.0 ) ;
457- println ! ( " actual_disk_used: {:.1} MiB" , usage. swap_used_disk as f64 / 1024.0 / 1024.0 ) ;
485+ println ! (
486+ " kernel_reported_used: {:.1} MiB" ,
487+ swap_used as f64 / 1024.0 / 1024.0
488+ ) ;
489+ println ! (
490+ " in_zswap_pool (RAM): {:.1} MiB" ,
491+ zswap_original as f64 / 1024.0 / 1024.0
492+ ) ;
493+ println ! (
494+ " actual_disk_used: {:.1} MiB" ,
495+ usage. swap_used_disk as f64 / 1024.0 / 1024.0
496+ ) ;
458497 let percent_in_ram = ( zswap_original as f64 / swap_used as f64 ) * 100.0 ;
459498 println ! ( " swap_in_ram: {:.0}%" , percent_in_ram) ;
460499 }
@@ -463,26 +502,27 @@ fn status() -> Result<(), Box<dyn std::error::Error>> {
463502 }
464503
465504 // Zram status
466- let zramctl_output = Command :: new ( "zramctl" )
467- . stdout ( Stdio :: piped ( ) )
468- . output ( ) ;
505+ let zramctl_output = Command :: new ( "zramctl" ) . stdout ( Stdio :: piped ( ) ) . output ( ) ;
469506
470507 if let Ok ( output) = zramctl_output {
471508 let output_str = String :: from_utf8_lossy ( & output. stdout ) ;
472509 if output_str. contains ( "[SWAP]" ) {
473510 println ! ( "\n Zram:" ) ;
474511 for line in output_str. lines ( ) {
475512 if line. starts_with ( "NAME" ) || line. contains ( "[SWAP]" ) {
476- let line = line. trim_end_matches ( "[SWAP]" ) . trim_end_matches ( "MOUNTPOINT" ) . trim ( ) ;
513+ let line = line
514+ . trim_end_matches ( "[SWAP]" )
515+ . trim_end_matches ( "MOUNTPOINT" )
516+ . trim ( ) ;
477517 println ! ( " {}" , line) ;
478518 }
479519 }
480520 }
481521 }
482522
483- // SwapFC status
484- if Path :: new ( & format ! ( "{}/swapfc " , WORK_DIR ) ) . is_dir ( ) {
485- println ! ( "\n swapFC :" ) ;
523+ // SwapFile status
524+ if Path :: new ( & format ! ( "{}/swapfile " , WORK_DIR ) ) . is_dir ( ) {
525+ println ! ( "\n swapfile :" ) ;
486526 let swapon_output = Command :: new ( "swapon" )
487527 . arg ( "--raw" )
488528 . stdout ( Stdio :: piped ( ) )
@@ -501,23 +541,22 @@ fn status() -> Result<(), Box<dyn std::error::Error>> {
501541/// Show recommended configuration based on system hardware
502542fn autoconfig ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
503543 println ! ( "Detecting system capabilities...\n " ) ;
504-
544+
505545 let caps = SystemCapabilities :: detect ( ) ;
506546 let recommended = RecommendedConfig :: from_capabilities ( & caps) ;
507-
547+
508548 println ! ( "=== System Information ===" ) ;
509549 println ! ( "Swap path filesystem: {:?}" , caps. swap_path_fstype) ;
510-
550+
511551 println ! ( "\n === Recommended Configuration ===" ) ;
512-
552+
513553 if recommended. use_zswap {
514554 println ! ( "Mode: zswap + swapfc (best for desktop with supported filesystem)" ) ;
515555 } else {
516556 println ! ( "Mode: zram only (fallback for unsupported filesystem)" ) ;
517557 }
518-
558+
519559 println ! ( "\n NOTE: Specific parameters (compressor, sizes, etc.) are controlled via /etc/systemd/swap.conf" ) ;
520-
560+
521561 Ok ( ( ) )
522562}
523-
0 commit comments