blob: 0fd1468f45b3ae231041fe4b410254a1deff2aeb [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()
46 .parse::<u32>()
47 .context(format!("No u32 in {}", &sysfs_size))?
48 * 512;
49
50 let pagesize: libc::c_uint;
51 // safe because we give a constant and known-valid sysconf parameter
52 unsafe {
53 pagesize = libc::sysconf(libc::_SC_PAGE_SIZE) as libc::c_uint;
54 }
55
56 let mut f = OpenOptions::new().read(false).write(true).open(format!("/dev/{}", dev))?;
57
58 // Write the info fields: [ version, last_page ]
59 let info: [u32; 2] = [1, (len / pagesize) - 1];
60 f.seek(SeekFrom::Start(1024))?;
61 f.write_all(&info.iter().flat_map(|v| v.to_ne_bytes()).collect::<Vec<u8>>())?;
62
63 // Write a random version 4 UUID
64 f.seek(SeekFrom::Start(1024 + 12))?;
65 f.write_all(Uuid::new_v4().as_bytes())?;
66
67 // Write the magic signature string.
68 f.seek(SeekFrom::Start((pagesize - 10) as u64))?;
69 f.write_all("SWAPSPACE2".as_bytes())?;
70
71 Ok(())
72}
73
74/// Simple "swapon", using libc:: wrapper.
75fn swapon(dev: &str) -> Result<()> {
76 let swapon_arg = std::ffi::CString::new(format!("/dev/{}", dev))?;
77 // safe because we give a nul-terminated string and check the result
78 let res = unsafe { libc::swapon(swapon_arg.as_ptr(), 0) };
79 if res != 0 {
80 return Err(anyhow!("Failed to swapon: {}", Error::last_os_error()));
81 }
82 Ok(())
83}
84
85/// Turn on ZRAM-backed swap
86pub fn init_swap() -> Result<()> {
87 let dev = SWAP_DEV;
88
89 // Create a ZRAM block device the same size as total VM memory.
90 let mem_kb = get_total_memory_kb()?;
91 OpenOptions::new()
92 .read(false)
93 .write(true)
94 .open(format!("/sys/{}/disksize", dev))?
95 .write_all(format!("{}K", mem_kb).as_bytes())?;
96
97 mkswap(dev)?;
98
99 swapon(dev)?;
100
101 Ok(())
102}