From bf7853658abed73f4354a5c87a75a10eef860981 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Tue, 14 Oct 2025 17:46:49 +0200 Subject: [PATCH] setup: send supplemental params to algorithm_params zram-generator used to route secondary compressor parameter blobs to /sys/block/zramX/recompress while setting up the device. The kernel's recompress knob refuses writes until the zram device is initialised (disksize set), so CONFIG_ZRAM_MULTI_COMP setups emitted EINVAL warnings like: Warning: algorithm "zstd" supplemental data "algo=zstd priority=1" not written: Invalid argument Route all supplemental data through /sys/block/zramX/algorithm_params instead, tagging secondary algorithms with their priority. This matches the driver interface, prevents the warning, and still supports primary and secondary parameter payloads. Include unit coverage for the new helper that builds the payload. --- src/setup.rs | 73 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/src/setup.rs b/src/setup.rs index daad2a4..73aa604 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -7,7 +7,7 @@ use std::fs; use std::io::ErrorKind; use std::os::unix::ffi::OsStrExt; use std::os::unix::process::ExitStatusExt; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; pub const SYSTEMD_MAKEFS_COMMAND: &str = concat!( @@ -24,6 +24,29 @@ pub const AFTER_HELP: &str = concat!( "Uses ", env!("SYSTEMD_UTIL_DIR"), "/systemd-makefs", "." ); +fn supplemental_params_target( + device_sysfs_path: &Path, + algo: &str, + params: Option<&str>, + prio: usize, +) -> Option<(PathBuf, String)> { + let params = params?.trim(); + if params.is_empty() { + return None; + } + + let mut payload = if prio == 0 { + format!("algo={algo}") + } else { + format!("priority={prio} algo={algo}") + }; + + payload.push(' '); + payload.push_str(params); + + Some((device_sysfs_path.join("algorithm_params"), payload)) +} + pub fn run_device_setup(device: Option, device_name: &str) -> Result<()> { let device = device.ok_or_else(|| anyhow!("Device {} not found", device_name))?; @@ -38,29 +61,19 @@ pub fn run_device_setup(device: Option, device_name: &str) -> Result<()> let params = if params.is_empty() { None } else { - Some(params) + Some(params.as_str()) }; let (path, data, add_pathdata) = if prio == 0 { ( device_sysfs_path.join("comp_algorithm"), algo, - params.as_ref().map(|p| { - ( - device_sysfs_path.join("algorithm_params"), - format!("algo={algo} {p}"), - ) - }), + supplemental_params_target(&device_sysfs_path, algo, params, prio), ) } else { ( device_sysfs_path.join("recomp_algorithm"), &format!("algo={algo} priority={prio}"), - params.as_ref().map(|p| { - ( - device_sysfs_path.join("recompress"), - format!("{p} priority={prio}"), - ) - }), + supplemental_params_target(&device_sysfs_path, algo, params, prio), ) }; @@ -153,3 +166,35 @@ pub fn run_device_reset(device_name: &str) -> Result<()> { fs::write(reset, b"1")?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn supplemental_params_primary_targets_algorithm_params() { + let base = Path::new("/sys/block/zram0"); + let (path, data) = supplemental_params_target(base, "zstd", Some("level=3"), 0) + .expect("expected supplemental params"); + + assert_eq!(path, base.join("algorithm_params")); + assert_eq!(data, "algo=zstd level=3"); + } + + #[test] + fn supplemental_params_secondary_includes_priority() { + let base = Path::new("/sys/block/zram1"); + let (path, data) = supplemental_params_target(base, "lz4hc", Some("dict=/opt/dict"), 2) + .expect("expected supplemental params"); + + assert_eq!(path, base.join("algorithm_params")); + assert_eq!(data, "priority=2 algo=lz4hc dict=/opt/dict"); + } + + #[test] + fn supplemental_params_none_when_empty() { + let base = Path::new("/sys/block/zram0"); + assert!(supplemental_params_target(base, "zstd", None, 1).is_none()); + assert!(supplemental_params_target(base, "zstd", Some(" \t"), 1).is_none()); + } +}