blob: 2f4d1760c02b7ed78fcda80fae82baaf2e6edb58 [file] [log] [blame]
Keir Fraser933f0ac2022-10-12 08:23:28 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Logic for configuring and enabling a ZRAM-backed swap device.
16
17use anyhow::{anyhow, Context, Result};
18use std::fs::{read_to_string, OpenOptions};
19use std::io::{Error, Seek, SeekFrom, Write};
20use uuid::Uuid;
21
22const SWAP_DEV: &str = "block/zram0";
23
24/// Parse "MemTotal: N kB" from /proc/meminfo
25fn get_total_memory_kb() -> Result<u32> {
26 let s = read_to_string("/proc/meminfo")?;
27 let mut iter = s.split_whitespace();
28 while let Some(x) = iter.next() {
29 if x.starts_with("MemTotal:") {
30 let n = iter.next().context("No text after MemTotal")?;
31 return n.parse::<u32>().context("No u32 after MemTotal");
32 }
33 }
34 Err(anyhow!("MemTotal not found in /proc/meminfo"))
35}
36
37/// Simple "mkswap": Writes swap-device header into specified device.
38/// The header has no formal public definition, but it can be found in the
39/// Linux source tree at include/linux/swap.h (union swap_header).
40/// This implementation is inspired by the one in Toybox.
41fn mkswap(dev: &str) -> Result<()> {
42 // Size of device, in bytes.
43 let sysfs_size = format!("/sys/{}/size", dev);
44 let len = read_to_string(&sysfs_size)?
45 .trim()
Inseob Kim888f48d2022-11-11 16:49:37 +090046 .parse::<u64>()
47 .context(format!("No u64 in {}", &sysfs_size))?
48 .checked_mul(512)
49 .ok_or_else(|| anyhow!("sysfs_size too large"))?;
Keir Fraser933f0ac2022-10-12 08:23:28 +000050
Keir Fraser933f0ac2022-10-12 08:23:28 +000051 // safe because we give a constant and known-valid sysconf parameter
Inseob Kim888f48d2022-11-11 16:49:37 +090052 let pagesize = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) as u64 };
Keir Fraser933f0ac2022-10-12 08:23:28 +000053
54 let mut f = OpenOptions::new().read(false).write(true).open(format!("/dev/{}", dev))?;
55
Inseob Kim888f48d2022-11-11 16:49:37 +090056 let last_page = len / pagesize - 1;
57
Keir Fraser933f0ac2022-10-12 08:23:28 +000058 // Write the info fields: [ version, last_page ]
Inseob Kim888f48d2022-11-11 16:49:37 +090059 let info: [u32; 2] = [1, last_page.try_into().context("Number of pages out of range")?];
60
Keir Fraser933f0ac2022-10-12 08:23:28 +000061 f.seek(SeekFrom::Start(1024))?;
62 f.write_all(&info.iter().flat_map(|v| v.to_ne_bytes()).collect::<Vec<u8>>())?;
63
64 // Write a random version 4 UUID
65 f.seek(SeekFrom::Start(1024 + 12))?;
66 f.write_all(Uuid::new_v4().as_bytes())?;
67
68 // Write the magic signature string.
Charisee96113f32023-01-26 09:00:42 +000069 f.seek(SeekFrom::Start(pagesize - 10))?;
Keir Fraser933f0ac2022-10-12 08:23:28 +000070 f.write_all("SWAPSPACE2".as_bytes())?;
71
72 Ok(())
73}
74
75/// Simple "swapon", using libc:: wrapper.
76fn swapon(dev: &str) -> Result<()> {
77 let swapon_arg = std::ffi::CString::new(format!("/dev/{}", dev))?;
78 // safe because we give a nul-terminated string and check the result
79 let res = unsafe { libc::swapon(swapon_arg.as_ptr(), 0) };
80 if res != 0 {
81 return Err(anyhow!("Failed to swapon: {}", Error::last_os_error()));
82 }
83 Ok(())
84}
85
86/// Turn on ZRAM-backed swap
87pub fn init_swap() -> Result<()> {
88 let dev = SWAP_DEV;
89
90 // Create a ZRAM block device the same size as total VM memory.
91 let mem_kb = get_total_memory_kb()?;
92 OpenOptions::new()
93 .read(false)
94 .write(true)
95 .open(format!("/sys/{}/disksize", dev))?
96 .write_all(format!("{}K", mem_kb).as_bytes())?;
97
98 mkswap(dev)?;
99
100 swapon(dev)?;
101
102 Ok(())
103}