summaryrefslogtreecommitdiffstats
path: root/vendor/num_cpus/src/linux.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/num_cpus/src/linux.rs')
-rw-r--r--vendor/num_cpus/src/linux.rs407
1 files changed, 292 insertions, 115 deletions
diff --git a/vendor/num_cpus/src/linux.rs b/vendor/num_cpus/src/linux.rs
index 36f472717..295c925fb 100644
--- a/vendor/num_cpus/src/linux.rs
+++ b/vendor/num_cpus/src/linux.rs
@@ -126,6 +126,11 @@ fn init_cgroups() {
// Should only be called once
debug_assert!(CGROUPS_CPUS.load(Ordering::SeqCst) == 0);
+ // Fails in Miri by default (cannot open files), and Miri does not have parallelism anyway.
+ if cfg!(miri) {
+ return;
+ }
+
if let Some(quota) = load_cgroups("/proc/self/cgroup", "/proc/self/mountinfo") {
if quota == 0 {
return;
@@ -144,27 +149,36 @@ where
P2: AsRef<Path>,
{
let subsys = some!(Subsys::load_cpu(cgroup_proc));
- let mntinfo = some!(MountInfo::load_cpu(mountinfo_proc));
+ let mntinfo = some!(MountInfo::load_cpu(mountinfo_proc, subsys.version));
let cgroup = some!(Cgroup::translate(mntinfo, subsys));
cgroup.cpu_quota()
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum CgroupVersion {
+ V1,
+ V2,
+}
+
struct Cgroup {
+ version: CgroupVersion,
base: PathBuf,
}
struct MountInfo {
+ version: CgroupVersion,
root: String,
mount_point: String,
}
struct Subsys {
+ version: CgroupVersion,
base: String,
}
impl Cgroup {
- fn new(dir: PathBuf) -> Cgroup {
- Cgroup { base: dir }
+ fn new(version: CgroupVersion, dir: PathBuf) -> Cgroup {
+ Cgroup { version: version, base: dir }
}
fn translate(mntinfo: MountInfo, subsys: Subsys) -> Option<Cgroup> {
@@ -181,12 +195,14 @@ impl Cgroup {
// join(mp.MountPoint, relPath)
let mut path = PathBuf::from(mntinfo.mount_point);
path.push(rel_from_root);
- Some(Cgroup::new(path))
+ Some(Cgroup::new(mntinfo.version, path))
}
fn cpu_quota(&self) -> Option<usize> {
- let quota_us = some!(self.quota_us());
- let period_us = some!(self.period_us());
+ let (quota_us, period_us) = match self.version {
+ CgroupVersion::V1 => (some!(self.quota_us()), some!(self.period_us())),
+ CgroupVersion::V2 => some!(self.max()),
+ };
// protect against dividing by zero
if period_us == 0 {
@@ -207,25 +223,41 @@ impl Cgroup {
self.param("cpu.cfs_period_us")
}
+ fn max(&self) -> Option<(usize, usize)> {
+ let max = some!(self.raw_param("cpu.max"));
+ let mut max = some!(max.lines().next()).split(' ');
+
+ let quota = some!(max.next().and_then(|quota| quota.parse().ok()));
+ let period = some!(max.next().and_then(|period| period.parse().ok()));
+
+ Some((quota, period))
+ }
+
fn param(&self, param: &str) -> Option<usize> {
+ let buf = some!(self.raw_param(param));
+
+ buf.trim().parse().ok()
+ }
+
+ fn raw_param(&self, param: &str) -> Option<String> {
let mut file = some!(File::open(self.base.join(param)).ok());
let mut buf = String::new();
some!(file.read_to_string(&mut buf).ok());
- buf.trim().parse().ok()
+ Some(buf)
}
}
impl MountInfo {
- fn load_cpu<P: AsRef<Path>>(proc_path: P) -> Option<MountInfo> {
+ fn load_cpu<P: AsRef<Path>>(proc_path: P, version: CgroupVersion) -> Option<MountInfo> {
let file = some!(File::open(proc_path).ok());
let file = BufReader::new(file);
file.lines()
.filter_map(|result| result.ok())
.filter_map(MountInfo::parse_line)
- .next()
+ .find(|mount_info| mount_info.version == version)
}
fn parse_line(line: String) -> Option<MountInfo> {
@@ -247,19 +279,25 @@ impl MountInfo {
};
// 7 5 0:6 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:7 - <cgroup> cgroup rw,cpu,cpuacct
- if fields.next() != Some("cgroup") {
- return None;
- }
+ let version = match fields.next() {
+ Some("cgroup") => CgroupVersion::V1,
+ Some("cgroup2") => CgroupVersion::V2,
+ _ => return None,
+ };
- // 7 5 0:6 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup <rw,cpu,cpuacct>
- let super_opts = some!(fields.nth(1));
+ // cgroups2 only has a single mount point
+ if version == CgroupVersion::V1 {
+ // 7 5 0:6 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup <rw,cpu,cpuacct>
+ let super_opts = some!(fields.nth(1));
- // We only care about the 'cpu' option
- if !super_opts.split(',').any(|opt| opt == "cpu") {
- return None;
+ // We only care about the 'cpu' option
+ if !super_opts.split(',').any(|opt| opt == "cpu") {
+ return None;
+ }
}
Some(MountInfo {
+ version: version,
root: mnt_root.to_owned(),
mount_point: mnt_point.to_owned(),
})
@@ -274,7 +312,14 @@ impl Subsys {
file.lines()
.filter_map(|result| result.ok())
.filter_map(Subsys::parse_line)
- .next()
+ .fold(None, |previous, line| {
+ // already-found v1 trumps v2 since it explicitly specifies its controllers
+ if previous.is_some() && line.version == CgroupVersion::V2 {
+ return previous;
+ }
+
+ Some(line)
+ })
}
fn parse_line(line: String) -> Option<Subsys> {
@@ -284,11 +329,18 @@ impl Subsys {
let sub_systems = some!(fields.nth(1));
- if !sub_systems.split(',').any(|sub| sub == "cpu") {
+ let version = if sub_systems.is_empty() {
+ CgroupVersion::V2
+ } else {
+ CgroupVersion::V1
+ };
+
+ if version == CgroupVersion::V1 && !sub_systems.split(',').any(|sub| sub == "cpu") {
return None;
}
fields.next().map(|path| Subsys {
+ version: version,
base: path.to_owned(),
})
}
@@ -296,123 +348,248 @@ impl Subsys {
#[cfg(test)]
mod tests {
- use super::{Cgroup, MountInfo, Subsys};
- use std::path::{Path, PathBuf};
+ mod v1 {
+ use super::super::{Cgroup, CgroupVersion, MountInfo, Subsys};
+ use std::path::{Path, PathBuf};
- // `static_in_const` feature is not stable in Rust 1.13.
- static FIXTURES_PROC: &'static str = "fixtures/cgroups/proc/cgroups";
+ // `static_in_const` feature is not stable in Rust 1.13.
+ static FIXTURES_PROC: &'static str = "fixtures/cgroups/proc/cgroups";
- static FIXTURES_CGROUPS: &'static str = "fixtures/cgroups/cgroups";
+ static FIXTURES_CGROUPS: &'static str = "fixtures/cgroups/cgroups";
- macro_rules! join {
- ($base:expr, $($path:expr),+) => ({
- Path::new($base)
- $(.join($path))+
- })
- }
+ macro_rules! join {
+ ($base:expr, $($path:expr),+) => ({
+ Path::new($base)
+ $(.join($path))+
+ })
+ }
- #[test]
- fn test_load_mountinfo() {
- // test only one optional fields
- let path = join!(FIXTURES_PROC, "mountinfo");
+ #[test]
+ fn test_load_mountinfo() {
+ // test only one optional fields
+ let path = join!(FIXTURES_PROC, "mountinfo");
- let mnt_info = MountInfo::load_cpu(path).unwrap();
+ let mnt_info = MountInfo::load_cpu(path, CgroupVersion::V1).unwrap();
- assert_eq!(mnt_info.root, "/");
- assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
+ assert_eq!(mnt_info.root, "/");
+ assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
- // test zero optional field
- let path = join!(FIXTURES_PROC, "mountinfo_zero_opt");
+ // test zero optional field
+ let path = join!(FIXTURES_PROC, "mountinfo_zero_opt");
- let mnt_info = MountInfo::load_cpu(path).unwrap();
+ let mnt_info = MountInfo::load_cpu(path, CgroupVersion::V1).unwrap();
- assert_eq!(mnt_info.root, "/");
- assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
+ assert_eq!(mnt_info.root, "/");
+ assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
- // test multi optional fields
- let path = join!(FIXTURES_PROC, "mountinfo_multi_opt");
+ // test multi optional fields
+ let path = join!(FIXTURES_PROC, "mountinfo_multi_opt");
- let mnt_info = MountInfo::load_cpu(path).unwrap();
+ let mnt_info = MountInfo::load_cpu(path, CgroupVersion::V1).unwrap();
- assert_eq!(mnt_info.root, "/");
- assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
- }
+ assert_eq!(mnt_info.root, "/");
+ assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup/cpu,cpuacct");
+ }
- #[test]
- fn test_load_subsys() {
- let path = join!(FIXTURES_PROC, "cgroup");
+ #[test]
+ fn test_load_subsys() {
+ let path = join!(FIXTURES_PROC, "cgroup");
- let subsys = Subsys::load_cpu(path).unwrap();
+ let subsys = Subsys::load_cpu(path).unwrap();
- assert_eq!(subsys.base, "/");
- }
+ assert_eq!(subsys.base, "/");
+ assert_eq!(subsys.version, CgroupVersion::V1);
+ }
- #[test]
- fn test_cgroup_mount() {
- let cases = &[
- ("/", "/sys/fs/cgroup/cpu", "/", Some("/sys/fs/cgroup/cpu")),
- (
- "/docker/01abcd",
- "/sys/fs/cgroup/cpu",
- "/docker/01abcd",
- Some("/sys/fs/cgroup/cpu"),
- ),
- (
- "/docker/01abcd",
- "/sys/fs/cgroup/cpu",
- "/docker/01abcd/",
- Some("/sys/fs/cgroup/cpu"),
- ),
- (
- "/docker/01abcd",
- "/sys/fs/cgroup/cpu",
- "/docker/01abcd/large",
- Some("/sys/fs/cgroup/cpu/large"),
- ),
- // fails
- ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/", None),
- ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/docker", None),
- ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/elsewhere", None),
- (
- "/docker/01abcd",
- "/sys/fs/cgroup/cpu",
- "/docker/01abcd-other-dir",
- None,
- ),
- ];
-
- for &(root, mount_point, subsys, expected) in cases.iter() {
- let mnt_info = MountInfo {
- root: root.into(),
- mount_point: mount_point.into(),
- };
- let subsys = Subsys {
- base: subsys.into(),
- };
+ #[test]
+ fn test_cgroup_mount() {
+ let cases = &[
+ ("/", "/sys/fs/cgroup/cpu", "/", Some("/sys/fs/cgroup/cpu")),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd",
+ Some("/sys/fs/cgroup/cpu"),
+ ),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd/",
+ Some("/sys/fs/cgroup/cpu"),
+ ),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd/large",
+ Some("/sys/fs/cgroup/cpu/large"),
+ ),
+ // fails
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/", None),
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/docker", None),
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/elsewhere", None),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd-other-dir",
+ None,
+ ),
+ ];
+
+ for &(root, mount_point, subsys, expected) in cases.iter() {
+ let mnt_info = MountInfo {
+ version: CgroupVersion::V1,
+ root: root.into(),
+ mount_point: mount_point.into(),
+ };
+ let subsys = Subsys {
+ version: CgroupVersion::V1,
+ base: subsys.into(),
+ };
+
+ let actual = Cgroup::translate(mnt_info, subsys).map(|c| c.base);
+ let expected = expected.map(PathBuf::from);
+ assert_eq!(actual, expected);
+ }
+ }
- let actual = Cgroup::translate(mnt_info, subsys).map(|c| c.base);
- let expected = expected.map(PathBuf::from);
- assert_eq!(actual, expected);
+ #[test]
+ fn test_cgroup_cpu_quota() {
+ let cgroup = Cgroup::new(CgroupVersion::V1, join!(FIXTURES_CGROUPS, "good"));
+ assert_eq!(cgroup.cpu_quota(), Some(6));
}
- }
- #[test]
- fn test_cgroup_cpu_quota() {
- let cgroup = Cgroup::new(join!(FIXTURES_CGROUPS, "good"));
- assert_eq!(cgroup.cpu_quota(), Some(6));
- }
+ #[test]
+ fn test_cgroup_cpu_quota_divide_by_zero() {
+ let cgroup = Cgroup::new(CgroupVersion::V1, join!(FIXTURES_CGROUPS, "zero-period"));
+ assert!(cgroup.quota_us().is_some());
+ assert_eq!(cgroup.period_us(), Some(0));
+ assert_eq!(cgroup.cpu_quota(), None);
+ }
- #[test]
- fn test_cgroup_cpu_quota_divide_by_zero() {
- let cgroup = Cgroup::new(join!(FIXTURES_CGROUPS, "zero-period"));
- assert!(cgroup.quota_us().is_some());
- assert_eq!(cgroup.period_us(), Some(0));
- assert_eq!(cgroup.cpu_quota(), None);
+ #[test]
+ fn test_cgroup_cpu_quota_ceil() {
+ let cgroup = Cgroup::new(CgroupVersion::V1, join!(FIXTURES_CGROUPS, "ceil"));
+ assert_eq!(cgroup.cpu_quota(), Some(2));
+ }
}
- #[test]
- fn test_cgroup_cpu_quota_ceil() {
- let cgroup = Cgroup::new(join!(FIXTURES_CGROUPS, "ceil"));
- assert_eq!(cgroup.cpu_quota(), Some(2));
+ mod v2 {
+ use super::super::{Cgroup, CgroupVersion, MountInfo, Subsys};
+ use std::path::{Path, PathBuf};
+
+ // `static_in_const` feature is not stable in Rust 1.13.
+ static FIXTURES_PROC: &'static str = "fixtures/cgroups2/proc/cgroups";
+
+ static FIXTURES_CGROUPS: &'static str = "fixtures/cgroups2/cgroups";
+
+ macro_rules! join {
+ ($base:expr, $($path:expr),+) => ({
+ Path::new($base)
+ $(.join($path))+
+ })
+ }
+
+ #[test]
+ fn test_load_mountinfo() {
+ // test only one optional fields
+ let path = join!(FIXTURES_PROC, "mountinfo");
+
+ let mnt_info = MountInfo::load_cpu(path, CgroupVersion::V2).unwrap();
+
+ assert_eq!(mnt_info.root, "/");
+ assert_eq!(mnt_info.mount_point, "/sys/fs/cgroup");
+ }
+
+ #[test]
+ fn test_load_subsys() {
+ let path = join!(FIXTURES_PROC, "cgroup");
+
+ let subsys = Subsys::load_cpu(path).unwrap();
+
+ assert_eq!(subsys.base, "/");
+ assert_eq!(subsys.version, CgroupVersion::V2);
+ }
+
+ #[test]
+ fn test_load_subsys_multi() {
+ let path = join!(FIXTURES_PROC, "cgroup_multi");
+
+ let subsys = Subsys::load_cpu(path).unwrap();
+
+ assert_eq!(subsys.base, "/");
+ assert_eq!(subsys.version, CgroupVersion::V1);
+ }
+
+ #[test]
+ fn test_cgroup_mount() {
+ let cases = &[
+ ("/", "/sys/fs/cgroup/cpu", "/", Some("/sys/fs/cgroup/cpu")),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd",
+ Some("/sys/fs/cgroup/cpu"),
+ ),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd/",
+ Some("/sys/fs/cgroup/cpu"),
+ ),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd/large",
+ Some("/sys/fs/cgroup/cpu/large"),
+ ),
+ // fails
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/", None),
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/docker", None),
+ ("/docker/01abcd", "/sys/fs/cgroup/cpu", "/elsewhere", None),
+ (
+ "/docker/01abcd",
+ "/sys/fs/cgroup/cpu",
+ "/docker/01abcd-other-dir",
+ None,
+ ),
+ ];
+
+ for &(root, mount_point, subsys, expected) in cases.iter() {
+ let mnt_info = MountInfo {
+ version: CgroupVersion::V1,
+ root: root.into(),
+ mount_point: mount_point.into(),
+ };
+ let subsys = Subsys {
+ version: CgroupVersion::V1,
+ base: subsys.into(),
+ };
+
+ let actual = Cgroup::translate(mnt_info, subsys).map(|c| c.base);
+ let expected = expected.map(PathBuf::from);
+ assert_eq!(actual, expected);
+ }
+ }
+
+ #[test]
+ fn test_cgroup_cpu_quota() {
+ let cgroup = Cgroup::new(CgroupVersion::V2, join!(FIXTURES_CGROUPS, "good"));
+ assert_eq!(cgroup.cpu_quota(), Some(6));
+ }
+
+ #[test]
+ fn test_cgroup_cpu_quota_divide_by_zero() {
+ let cgroup = Cgroup::new(CgroupVersion::V2, join!(FIXTURES_CGROUPS, "zero-period"));
+ let period = cgroup.max().map(|max| max.1);
+
+ assert_eq!(period, Some(0));
+ assert_eq!(cgroup.cpu_quota(), None);
+ }
+
+ #[test]
+ fn test_cgroup_cpu_quota_ceil() {
+ let cgroup = Cgroup::new(CgroupVersion::V2, join!(FIXTURES_CGROUPS, "ceil"));
+ assert_eq!(cgroup.cpu_quota(), Some(2));
+ }
}
}