Skip to content

Commit 87a5b91

Browse files
alexandruagalxiord
authored andcommitted
jailer: added --node option
The mandatory --node command line option allows the user to specify which numa node should the microVM be assigned to. This is done by writing the specified value to the cpuset.mems special file within the cpuset controller. Signed-off-by: Alexandru Agache <[email protected]>
1 parent 46fa6e7 commit 87a5b91

File tree

4 files changed

+46
-13
lines changed

4 files changed

+46
-13
lines changed

jailer/src/cgroup.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashMap;
22
use std::ffi::OsStr;
33
use std::fs::{self, File};
44
use std::io::{BufRead, BufReader, Write};
5-
use std::path::PathBuf;
5+
use std::path::{Path, PathBuf};
66
use std::process;
77

88
use regex::Regex;
@@ -17,8 +17,25 @@ pub struct Cgroup {
1717
tasks_files: Vec<PathBuf>,
1818
}
1919

20+
// This is called writeln_special because we have to use this rather convoluted way of writing
21+
// to avoid getting all sorts of weird errors. I would be nice to know why that happens.
22+
fn writeln_special<T, V>(file_path: T, value: V) -> Result<()>
23+
where
24+
T: AsRef<Path>,
25+
V: ::std::fmt::Display,
26+
{
27+
// Open does not work here, or so it seemed at one point :-s
28+
let mut f = File::create(file_path.as_ref())
29+
.map_err(|e| Error::FileCreate(PathBuf::from(file_path.as_ref()), e))?;
30+
31+
// For some reason, using writeln!(f, "{}", pid) doesn't work :(
32+
let mut bytes = format!("{}\n", value).into_bytes();
33+
f.write_all(bytes.as_mut())
34+
.map_err(|e| Error::Write(PathBuf::from(file_path.as_ref()), e))
35+
}
36+
2037
impl Cgroup {
21-
pub fn new(id: &str, exec_file_name: &OsStr) -> Result<Self> {
38+
pub fn new(id: &str, numa_node: u32, exec_file_name: &OsStr) -> Result<Self> {
2239
let f =
2340
File::open(PROC_MOUNTS).map_err(|e| Error::FileOpen(PathBuf::from(PROC_MOUNTS), e))?;
2441

@@ -65,14 +82,21 @@ impl Cgroup {
6582
// We now both create the cgroup subfolders, and fill the tasks_files vector.
6683
let mut tasks_files = Vec::with_capacity(keys_len);
6784

68-
for (_controller, mut path_buf) in found_controllers.drain() {
85+
for (controller, mut path_buf) in found_controllers.drain() {
6986
path_buf.push(exec_file_name);
7087
path_buf.push(id);
7188

7289
// Create the folders first. The cpuset.cpus and cpuset.mems files really do appear to
7390
// be inherited AND populated automatically :-s
7491
fs::create_dir_all(&path_buf).map_err(|e| Error::CreateDir(path_buf.clone(), e))?;
7592

93+
if controller == "cpuset" {
94+
// Enforce NUMA node restriction.
95+
path_buf.push("cpuset.mems");
96+
writeln_special(&path_buf, numa_node)?;
97+
path_buf.pop();
98+
}
99+
76100
// And now add "tasks" to get the path of the corresponding tasks file.
77101
path_buf.push("tasks");
78102

@@ -87,15 +111,9 @@ impl Cgroup {
87111
// This writes the pid of the current process to each tasks file. These are special files that,
88112
// when written to, will assign the process associated with the pid to the respective cgroup.
89113
pub fn attach_pid(&self) -> Result<()> {
114+
let pid = process::id();
90115
for tasks_file in &self.tasks_files {
91-
// Open does not work here, or so it seemed at one point :-s
92-
let mut f =
93-
File::create(tasks_file).map_err(|e| Error::FileCreate(tasks_file.clone(), e))?;
94-
95-
// For some reason, using write!("{}\n", pid) doesn't work :( I wonder why ...
96-
let mut bytes = format!("{}\n", process::id()).into_bytes();
97-
f.write_all(bytes.as_mut())
98-
.map_err(|e| Error::Write(tasks_file.clone(), e))?;
116+
writeln_special(tasks_file, pid)?;
99117
}
100118
Ok(())
101119
}

jailer/src/env.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct Env {
2020
impl Env {
2121
pub fn new(args: JailerArgs) -> Result<Self> {
2222
let exec_file_name = args.exec_file_name()?;
23-
let cgroup = Cgroup::new(args.id, exec_file_name)?;
23+
let cgroup = Cgroup::new(args.id, args.numa_node, exec_file_name)?;
2424

2525
let mut chroot_dir = PathBuf::from(&args.chroot_base_dir);
2626

jailer/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub enum Error {
4040
NotAFile(PathBuf),
4141
NotAFolder(PathBuf),
4242
NotAlphanumeric(String),
43+
NumaNode(String),
4344
OpenDevKvm(sys_util::Error),
4445
OpenDevNetTun(sys_util::Error),
4546
ReadLine(PathBuf, io::Error),
@@ -57,6 +58,7 @@ pub type Result<T> = result::Result<T, Error>;
5758

5859
pub struct JailerArgs<'a> {
5960
id: &'a str,
61+
numa_node: u32,
6062
exec_file_path: PathBuf,
6163
chroot_base_dir: PathBuf,
6264
uid: u32,
@@ -66,6 +68,7 @@ pub struct JailerArgs<'a> {
6668
impl<'a> JailerArgs<'a> {
6769
pub fn new(
6870
id: &'a str,
71+
node: &str,
6972
exec_file: &str,
7073
chroot_base: &str,
7174
uid: &str,
@@ -78,6 +81,9 @@ impl<'a> JailerArgs<'a> {
7881
}
7982
}
8083

84+
let numa_node = node.parse::<u32>()
85+
.map_err(|_| Error::NumaNode(String::from(node)))?;
86+
8187
let exec_file_path =
8288
canonicalize(exec_file).map_err(|e| Error::Canonicalize(PathBuf::from(exec_file), e))?;
8389

@@ -105,6 +111,7 @@ impl<'a> JailerArgs<'a> {
105111

106112
Ok(JailerArgs {
107113
id,
114+
numa_node,
108115
exec_file_path,
109116
chroot_base_dir,
110117
uid,

src/bin/jailer.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,17 @@ fn main() -> jailer::Result<()> {
2020
// Initially, the uid and gid params had default values, but it turns out that it's quite
2121
// easy to shoot yourself in the foot by not setting proper permissions when preparing the
2222
// contents of the jail, so I think their values should be provided explicitly.
23-
let cmd_arguments = App::new("firejailer")
23+
let cmd_arguments = App::new("jailer")
2424
.version(crate_version!())
2525
.author(crate_authors!())
2626
.about("Jail a microVM.")
27+
.arg(
28+
Arg::with_name("numa_node")
29+
.long("node")
30+
.help("NUMA node to assign this microVM to.")
31+
.required(true)
32+
.takes_value(true),
33+
)
2734
.arg(
2835
Arg::with_name("id")
2936
.long("id")
@@ -65,6 +72,7 @@ fn main() -> jailer::Result<()> {
6572
// All arguments are either mandatory, or have default values, so the unwraps should not fail.
6673
let args = JailerArgs::new(
6774
cmd_arguments.value_of("id").unwrap(),
75+
cmd_arguments.value_of("numa_node").unwrap(),
6876
cmd_arguments.value_of("exec_file").unwrap(),
6977
cmd_arguments.value_of("chroot_base").unwrap(),
7078
cmd_arguments.value_of("uid").unwrap(),

0 commit comments

Comments
 (0)