Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cgroups support for Linux targets #96

Merged
merged 2 commits into from
Apr 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
language: rust
rust: stable
dist: trusty
dist: xenial
sudo: false
notifications:
email: disabled

cache: cargo

Expand Down Expand Up @@ -65,3 +67,15 @@ matrix:
- env: TARGET=x86_64-unknown-netbsd
# Emscripten
- env: TARGET=asmjs-unknown-emscripten

# CGroups in Docker
- name: Docker CGroups
install:
script:
- docker build -f ci/cgroups/Dockerfile -t num-cpus-cgroups .
# Test without cgroups
- docker run -it -e NUM_CPUS_TEST_GET=2 num-cpus-cgroups
# Only 1 CPU
- docker run -it --cpus="1" -e NUM_CPUS_TEST_GET=1 num-cpus-cgroups
# 1.5 CPUs
- docker run -it --cpus="1.5" -e NUM_CPUS_TEST_GET=2 num-cpus-cgroups
9 changes: 9 additions & 0 deletions ci/cgroups/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM rust:1.40

WORKDIR /usr/num_cpus

COPY . .

RUN cargo build

CMD [ "cargo", "test", "--lib" ]
2 changes: 2 additions & 0 deletions fixtures/cgroups/cgroups/ceil/cpu.cfs_period_us
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
100000

2 changes: 2 additions & 0 deletions fixtures/cgroups/cgroups/ceil/cpu.cfs_quota_us
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
150000

2 changes: 2 additions & 0 deletions fixtures/cgroups/cgroups/good/cpu.cfs_period_us
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
100000

2 changes: 2 additions & 0 deletions fixtures/cgroups/cgroups/good/cpu.cfs_quota_us
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
600000

2 changes: 2 additions & 0 deletions fixtures/cgroups/cgroups/zero-period/cpu.cfs_period_us
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0

1 change: 1 addition & 0 deletions fixtures/cgroups/cgroups/zero-period/cpu.cfs_quota_us
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
600000
3 changes: 3 additions & 0 deletions fixtures/cgroups/proc/cgroups/cgroup
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
12:perf_event:/
11:cpu,cpuacct:/
3:devices:/user.slice
8 changes: 8 additions & 0 deletions fixtures/cgroups/proc/cgroups/mountinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
1 0 8:1 / / rw,noatime shared:1 - ext4 /dev/sda1 rw,errors=remount-ro,data=reordered
2 1 0:1 / /dev rw,relatime shared:2 - devtmpfs udev rw,size=10240k,nr_inodes=16487629,mode=755
3 1 0:2 / /proc rw,nosuid,nodev,noexec,relatime shared:3 - proc proc rw
4 1 0:3 / /sys rw,nosuid,nodev,noexec,relatime shared:4 - sysfs sysfs rw
5 4 0:4 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:5 - tmpfs tmpfs ro,mode=755
6 5 0:5 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:6 - cgroup cgroup rw,cpuset
7 5 0:6 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup rw,cpu,cpuacct
8 5 0:7 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:8 - cgroup cgroup rw,memory
83 changes: 10 additions & 73 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ extern crate libc;
#[cfg(target_os = "hermit")]
extern crate hermit_abi;

#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
use linux::{get_num_cpus, get_num_physical_cpus};

/// Returns the number of available CPUs of the current system.
///
/// This function will get the number of logical cores. Sometimes this is different from the number
Expand All @@ -55,11 +60,14 @@ extern crate hermit_abi;
///
/// # Note
///
/// This will check [sched affinity] on Linux, showing a lower number of CPUs if the current
/// thread does not have access to all the computer's CPUs.
/// This will check [sched affinity] on Linux, showing a lower number of CPUs if the current
/// thread does not have access to all the computer's CPUs.
///
/// This will also check [cgroups], frequently used in containers to constrain CPU usage.
///
/// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
/// [sched affinity]: http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
/// [cgroups]: https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
#[inline]
pub fn get() -> usize {
get_num_cpus()
Expand Down Expand Up @@ -189,56 +197,6 @@ fn get_num_physical_cpus_windows() -> Option<usize> {
}
}

#[cfg(target_os = "linux")]
fn get_num_physical_cpus() -> usize {
use std::collections::HashMap;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;

let file = match File::open("/proc/cpuinfo") {
Ok(val) => val,
Err(_) => return get_num_cpus(),
};
let reader = BufReader::new(file);
let mut map = HashMap::new();
let mut physid: u32 = 0;
let mut cores: usize = 0;
let mut chgcount = 0;
for line in reader.lines().filter_map(|result| result.ok()) {
let mut it = line.split(':');
let (key, value) = match (it.next(), it.next()) {
(Some(key), Some(value)) => (key.trim(), value.trim()),
_ => continue,
};
if key == "physical id" {
match value.parse() {
Ok(val) => physid = val,
Err(_) => break,
};
chgcount += 1;
}
if key == "cpu cores" {
match value.parse() {
Ok(val) => cores = val,
Err(_) => break,
};
chgcount += 1;
}
if chgcount == 2 {
map.insert(physid, cores);
chgcount = 0;
}
}
let count = map.into_iter().fold(0, |acc, (_, cores)| acc + cores);

if count == 0 {
get_num_cpus()
} else {
count
}
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
#[repr(C)]
Expand Down Expand Up @@ -366,27 +324,6 @@ fn get_num_physical_cpus() -> usize {
cpus as usize
}

#[cfg(target_os = "linux")]
fn get_num_cpus() -> usize {
let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
let mut count: u32 = 0;
for i in 0..libc::CPU_SETSIZE as usize {
if unsafe { libc::CPU_ISSET(i, &set) } {
count += 1
}
}
count as usize
} else {
let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
if cpus < 1 {
1
} else {
cpus as usize
}
}
}

#[cfg(any(
target_os = "nacl",
target_os = "macos",
Expand Down
Loading