mirror of
https://github.com/LibreQoE/LibreQoS.git
synced 2025-02-25 18:55:32 -06:00
Replace unsafe bpf_num_possible_cpus() with safe Rust function.
Port the C from `bpf_num_possible_cpus()` over to safe Rust, with proper error handling and unit tests to ensure that it is giving the correct answers. Change usages to the new safe function.
This commit is contained in:
parent
130c888e22
commit
93e8afae71
1
src/rust/Cargo.lock
generated
1
src/rust/Cargo.lock
generated
@ -1505,6 +1505,7 @@ dependencies = [
|
|||||||
"lqos_utils",
|
"lqos_utils",
|
||||||
"nix",
|
"nix",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2,6 +2,7 @@ use crate::queue_structure::QUEUE_STRUCTURE;
|
|||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use lqos_bus::TcHandle;
|
use lqos_bus::TcHandle;
|
||||||
|
use lqos_sys::num_possible_cpus;
|
||||||
use lqos_utils::unix_time::unix_now;
|
use lqos_utils::unix_time::unix_now;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ pub fn expiration_in_the_future() -> u64 {
|
|||||||
|
|
||||||
pub fn add_watched_queue(circuit_id: &str) {
|
pub fn add_watched_queue(circuit_id: &str) {
|
||||||
//info!("Watching queue {circuit_id}");
|
//info!("Watching queue {circuit_id}");
|
||||||
let max = unsafe { lqos_sys::libbpf_num_possible_cpus() } * 2;
|
let max = num_possible_cpus().unwrap() * 2;
|
||||||
{
|
{
|
||||||
if WATCHED_QUEUES.contains_key(circuit_id) {
|
if WATCHED_QUEUES.contains_key(circuit_id) {
|
||||||
warn!("Queue {circuit_id} is already being watched. Duplicate ignored.");
|
warn!("Queue {circuit_id} is already being watched. Duplicate ignored.");
|
||||||
|
@ -14,6 +14,7 @@ log = "0"
|
|||||||
lqos_utils = { path = "../lqos_utils" }
|
lqos_utils = { path = "../lqos_utils" }
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
dashmap = "5"
|
dashmap = "5"
|
||||||
|
thiserror = "1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "0"
|
bindgen = "0"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use libbpf_sys::{
|
use libbpf_sys::{
|
||||||
bpf_map_get_next_key, bpf_map_lookup_elem, bpf_obj_get,
|
bpf_map_get_next_key, bpf_map_lookup_elem, bpf_obj_get,
|
||||||
libbpf_num_possible_cpus,
|
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::{
|
use std::{
|
||||||
@ -10,6 +9,8 @@ use std::{
|
|||||||
ptr::null_mut,
|
ptr::null_mut,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::num_possible_cpus;
|
||||||
|
|
||||||
/// Represents an underlying BPF map, accessed via the filesystem.
|
/// Represents an underlying BPF map, accessed via the filesystem.
|
||||||
/// `BpfMap` *only* talks to PER-CPU variants of maps.
|
/// `BpfMap` *only* talks to PER-CPU variants of maps.
|
||||||
///
|
///
|
||||||
@ -43,7 +44,7 @@ where
|
|||||||
/// and allocating, calls `callback` for each key/value slice
|
/// and allocating, calls `callback` for each key/value slice
|
||||||
/// with references to the data returned from C.
|
/// with references to the data returned from C.
|
||||||
pub(crate) fn for_each(&self, callback: &mut dyn FnMut(&K, &[V])) {
|
pub(crate) fn for_each(&self, callback: &mut dyn FnMut(&K, &[V])) {
|
||||||
let num_cpus = unsafe { libbpf_num_possible_cpus() };
|
let num_cpus = num_possible_cpus().unwrap();
|
||||||
let mut prev_key: *mut K = null_mut();
|
let mut prev_key: *mut K = null_mut();
|
||||||
let mut key: K = K::default();
|
let mut key: K = K::default();
|
||||||
let key_ptr: *mut K = &mut key;
|
let key_ptr: *mut K = &mut key;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use libbpf_sys::{bpf_map_update_elem, bpf_obj_get, libbpf_num_possible_cpus};
|
use libbpf_sys::{bpf_map_update_elem, bpf_obj_get};
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::{ffi::CString, os::raw::c_void};
|
use std::{ffi::CString, os::raw::c_void};
|
||||||
|
|
||||||
|
use crate::num_possible_cpus;
|
||||||
|
|
||||||
//* Provides an interface for querying the number of CPUs eBPF can
|
//* Provides an interface for querying the number of CPUs eBPF can
|
||||||
//* see, and marking CPUs as available. Currently marks ALL eBPF
|
//* see, and marking CPUs as available. Currently marks ALL eBPF
|
||||||
//* usable CPUs as available.
|
//* usable CPUs as available.
|
||||||
@ -33,7 +35,7 @@ impl CpuMapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn mark_cpus_available(&self) -> Result<()> {
|
pub(crate) fn mark_cpus_available(&self) -> Result<()> {
|
||||||
let cpu_count = unsafe { libbpf_num_possible_cpus() } as u32;
|
let cpu_count = num_possible_cpus()?;
|
||||||
|
|
||||||
let queue_size = 2048u32;
|
let queue_size = 2048u32;
|
||||||
let val_ptr: *const u32 = &queue_size;
|
let val_ptr: *const u32 = &queue_size;
|
||||||
|
@ -17,6 +17,7 @@ mod lqos_kernel;
|
|||||||
mod tcp_rtt;
|
mod tcp_rtt;
|
||||||
mod throughput;
|
mod throughput;
|
||||||
mod xdp_ip_address;
|
mod xdp_ip_address;
|
||||||
|
mod linux;
|
||||||
|
|
||||||
pub use heimdall_map::{
|
pub use heimdall_map::{
|
||||||
heimdall_expire, heimdall_for_each, heimdall_watch_ip, set_heimdall_mode,
|
heimdall_expire, heimdall_for_each, heimdall_watch_ip, set_heimdall_mode,
|
||||||
@ -26,7 +27,7 @@ pub use ip_mapping::{
|
|||||||
add_ip_to_tc, clear_ips_from_tc, del_ip_from_tc, list_mapped_ips,
|
add_ip_to_tc, clear_ips_from_tc, del_ip_from_tc, list_mapped_ips,
|
||||||
};
|
};
|
||||||
pub use kernel_wrapper::LibreQoSKernels;
|
pub use kernel_wrapper::LibreQoSKernels;
|
||||||
pub use libbpf_sys::libbpf_num_possible_cpus;
|
pub use linux::num_possible_cpus;
|
||||||
pub use lqos_kernel::max_tracked_ips;
|
pub use lqos_kernel::max_tracked_ips;
|
||||||
pub use tcp_rtt::{rtt_for_each, RttTrackingEntry};
|
pub use tcp_rtt::{rtt_for_each, RttTrackingEntry};
|
||||||
pub use throughput::{throughput_for_each, HostCounter};
|
pub use throughput::{throughput_for_each, HostCounter};
|
||||||
|
4
src/rust/lqos_sys/src/linux/mod.rs
Normal file
4
src/rust/lqos_sys/src/linux/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//! Ports of C code that is very Linux specific.
|
||||||
|
|
||||||
|
mod possible_cpus;
|
||||||
|
pub use possible_cpus::num_possible_cpus;
|
79
src/rust/lqos_sys/src/linux/possible_cpus.rs
Normal file
79
src/rust/lqos_sys/src/linux/possible_cpus.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use std::{fs::read_to_string, path::Path};
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const POSSIBLE_CPUS_PATH: &str = "/sys/devices/system/cpu/possible";
|
||||||
|
|
||||||
|
/// Query the number of available CPUs from `/sys/devices/system/cpu/possible`,
|
||||||
|
/// and return the last digit (it will be formatted 0-3 or similar) plus one.
|
||||||
|
/// So on a 16 CPU system, `0-15` will return `16`.
|
||||||
|
pub fn num_possible_cpus() -> Result<u32, PossibleCpuError> {
|
||||||
|
let path = Path::new(POSSIBLE_CPUS_PATH);
|
||||||
|
if !path.exists() {
|
||||||
|
error!("Unable to read /sys/devices/system/cpu/possible");
|
||||||
|
return Err(PossibleCpuError::FileNotFound);
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_contents = read_to_string(path);
|
||||||
|
if file_contents.is_err() {
|
||||||
|
error!("Unable to read contents of /sys/devices/system/cpu/possible");
|
||||||
|
error!("{file_contents:?}");
|
||||||
|
return Err(PossibleCpuError::UnableToRead);
|
||||||
|
}
|
||||||
|
let file_contents = file_contents.unwrap();
|
||||||
|
|
||||||
|
parse_cpu_string(&file_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_cpu_string(possible_cpus: &str) -> Result<u32, PossibleCpuError> {
|
||||||
|
if let Some(last_digit) = possible_cpus.trim().split('-').last() {
|
||||||
|
if let Ok(n) = last_digit.parse::<u32>() {
|
||||||
|
Ok(n + 1)
|
||||||
|
} else {
|
||||||
|
error!("Unable to parse /sys/devices/system/cpu/possible");
|
||||||
|
error!("{possible_cpus}");
|
||||||
|
Err(PossibleCpuError::ParseError)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Unable to parse /sys/devices/system/cpu/possible");
|
||||||
|
error!("{possible_cpus}");
|
||||||
|
Err(PossibleCpuError::ParseError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, PartialEq)]
|
||||||
|
pub enum PossibleCpuError {
|
||||||
|
#[error("Unable to access /sys/devices/system/cpu/possible")]
|
||||||
|
FileNotFound,
|
||||||
|
#[error("Unable to read /sys/devices/system/cpu/possible")]
|
||||||
|
UnableToRead,
|
||||||
|
#[error("Unable to parse contents of /sys/devices/system/cpu/possible")]
|
||||||
|
ParseError,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unable_to_parse() {
|
||||||
|
assert_eq!(parse_cpu_string("blah").err().unwrap(), PossibleCpuError::ParseError);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_four_cpus() {
|
||||||
|
assert_eq!(4, parse_cpu_string("0-3").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sixteen_cpus() {
|
||||||
|
assert_eq!(16, parse_cpu_string("0-15").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_againt_c() {
|
||||||
|
let cpu_count = unsafe { libbpf_sys::libbpf_num_possible_cpus() } as u32;
|
||||||
|
assert_eq!(cpu_count, num_possible_cpus().unwrap());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user