Skip to content

Commit

Permalink
fix: manually load kernel modules for compression
Browse files Browse the repository at this point in the history
Creation of a zram device (with zstd as the compression alg) occasionally
fails with ENOMEM:
Jan 13 09:59:39 vultr.guest systemd[1]: Starting Create swap on /dev/zram3...
Jan 13 09:59:39 vultr.guest kernel: Can't allocate a compression stream
Jan 13 09:59:39 vultr.guest kernel: zram: Cannot initialise zstd compressing backend
Jan 13 09:59:39 vultr.guest zram-generator[997]: Error: Failed to configure disk size into /sys/block/zram3/disksize
Jan 13 09:59:39 vultr.guest zram-generator[997]: Caused by:
Jan 13 09:59:39 vultr.guest zram-generator[997]:     Cannot allocate memory (os error 12)

https://bugzilla.kernel.org/show_bug.cgi?id=211173

The kernel normally loads the compressor module when a device with a
given compression module is loaded, but this doesn't always work.
Apparently, loading the compression module up front avoids the issue.

Fixes systemd#50, systemd#53.

Note: /sys/block/zram0/comp_algorithm lists compressors known to zram,
not the ones currently loaded. /proc/crypto lists currently loaded
crypto & compression modules.
  • Loading branch information
polarathene authored and keszybz committed Jan 14, 2021
1 parent a143b37 commit 2b5bbdd
Showing 1 changed file with 37 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::config::Device;
use anyhow::{anyhow, Context, Result};
use log::{info, warn};
use std::cmp;
use std::collections::BTreeSet;
use std::fs;
use std::os::unix::fs::symlink;
use std::path::Path;
Expand Down Expand Up @@ -96,9 +97,45 @@ pub fn run_generator(devices: &[Device], output_directory: &Path, fake_mode: boo
}
}

let compressors: BTreeSet<&String> = devices
.iter()
.flat_map(|device| device.compression_algorithm.as_ref())
.collect::<BTreeSet<_>>();

if !compressors.is_empty() {
let known = get_known_compressors()?;

for comp in compressors.into_iter().filter(|&c| !known.contains(c)) {
let status = Command::new("modprobe")
.arg(format!("crypto-{}", comp))
.status()
.context("Failed to spawn modprobe")?;
if !status.success() {
warn!("modprobe crypto-{} failed, ignoring: code {}", comp, status);
}
}
}

Ok(())
}

// Returns a list of kernel crypto modules, including available (loaded) compressors
fn get_known_compressors() -> Result<BTreeSet<String>> {
let path = Path::new("/proc/crypto");
let content = fs::read_to_string(path).with_context(|| format!("Failed to read {:?}", path))?;

// Extract algorithm names (this includes non-compression algorithms too)
let available = content
.lines()
.into_iter()
.filter(|line| line.starts_with("name"))
.map(|m| m.rsplit(':').next().unwrap().trim())
.map(String::from)
.collect();

Ok(available)
}

fn write_contents(output_directory: &Path, filename: &str, contents: &str) -> Result<()> {
let path = output_directory.join(filename);
make_parent(&path)?;
Expand Down

0 comments on commit 2b5bbdd

Please sign in to comment.