Use proper error handling instead of lurking panics everywhere
This commit is contained in:
1 parent
999987d157
commit
214ffef6f3
11 files changed
+349
-392
No files matched your search
+139
-88
@@ -1,5 +1,6 @@
|
||||
use crate::netlink::Netlink;
|
||||
use crate::{Config, ReflectorStats, Utils};
|
||||
use crate::netlink::{Netlink, NetlinkError, Qdisc};
|
||||
use crate::utils::Utils;
|
||||
use crate::{Config, ReflectorStats};
|
||||
use log::{debug, error, info, warn};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
@@ -12,6 +13,16 @@ use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RatecontrolError {
|
||||
#[error("Netlink error")]
|
||||
Netlink(#[from] NetlinkError),
|
||||
|
||||
#[error("Error sorting")]
|
||||
Sorting,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum StatsDirection {
|
||||
@@ -19,34 +30,25 @@ pub enum StatsDirection {
|
||||
TX,
|
||||
}
|
||||
|
||||
fn f64_max(a: f64, b: f64) -> f64 {
|
||||
*[a, b].iter().max_by(|a, b| a.total_cmp(b)).unwrap()
|
||||
}
|
||||
fn generate_initial_speeds(base_speed: f64, size: u32) -> Vec<f64> {
|
||||
let mut rates = Vec::new();
|
||||
|
||||
fn f64_min(a: f64, b: f64) -> f64 {
|
||||
*[a, b].iter().min_by(|a, b| a.total_cmp(b)).unwrap()
|
||||
for _ in 0..size {
|
||||
rates.push((rand::thread_rng().next_u64() as f64 * 0.2 + 0.75) * base_speed);
|
||||
}
|
||||
|
||||
rates
|
||||
}
|
||||
|
||||
fn get_interface_stats(
|
||||
config: Config,
|
||||
down_direction: StatsDirection,
|
||||
up_direction: StatsDirection,
|
||||
) -> (i128, i128) {
|
||||
let (down_rx, down_tx) = match Netlink::get_interface_stats(config.download_interface.as_str())
|
||||
{
|
||||
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
|
||||
Err(e) => {
|
||||
error!("Error while retrieving stats: {}", e.to_string());
|
||||
(-1, -1)
|
||||
}
|
||||
};
|
||||
let (up_rx, up_tx) = match Netlink::get_interface_stats(config.upload_interface.as_str()) {
|
||||
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
|
||||
Err(e) => {
|
||||
error!("Error while retrieving stats: {}", e.to_string());
|
||||
(-1, -1)
|
||||
}
|
||||
};
|
||||
) -> Result<(i128, i128), RatecontrolError> {
|
||||
let down_stats = Netlink::get_interface_stats(config.download_interface.as_str())?;
|
||||
let up_stats = Netlink::get_interface_stats(config.upload_interface.as_str())?;
|
||||
let (down_rx, down_tx) = (down_stats.rx_bytes, down_stats.tx_bytes);
|
||||
let (up_rx, up_tx) = (up_stats.rx_bytes, up_stats.tx_bytes);
|
||||
|
||||
let rx_bytes = match down_direction {
|
||||
StatsDirection::RX => down_rx,
|
||||
@@ -58,42 +60,106 @@ fn get_interface_stats(
|
||||
StatsDirection::TX => up_tx,
|
||||
};
|
||||
|
||||
(rx_bytes, tx_bytes)
|
||||
Ok((rx_bytes.into(), tx_bytes.into()))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct State {
|
||||
current_bytes: i128,
|
||||
current_rate: f64,
|
||||
qdisc: Qdisc,
|
||||
nrate: usize,
|
||||
previous_bytes: i128,
|
||||
safe_rates: Vec<f64>,
|
||||
stats_direction: StatsDirection,
|
||||
}
|
||||
|
||||
pub struct Ratecontroller {
|
||||
pub(crate) config: Config,
|
||||
pub(crate) owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
pub(crate) owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
pub(crate) reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||
pub(crate) reselect_trigger: Sender<bool>,
|
||||
config: Config,
|
||||
owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||
reselect_trigger: Sender<bool>,
|
||||
|
||||
state_dl: State,
|
||||
state_ul: State,
|
||||
}
|
||||
|
||||
impl Ratecontroller {
|
||||
pub fn run(&self, down_direction: StatsDirection, up_direction: StatsDirection) {
|
||||
fn adjust_rate(&self) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
config: Config,
|
||||
owd_baseline: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
owd_recent: Arc<Mutex<HashMap<IpAddr, ReflectorStats>>>,
|
||||
reflectors_lock: Arc<RwLock<Vec<IpAddr>>>,
|
||||
reselect_trigger: Sender<bool>,
|
||||
down_direction: StatsDirection,
|
||||
up_direction: StatsDirection,
|
||||
) -> anyhow::Result<Self> {
|
||||
let dl_qdisc = Netlink::qdisc_from_ifname(config.download_interface.as_str())?;
|
||||
let dl_safe_rates =
|
||||
generate_initial_speeds(config.download_base_kbits, config.speed_hist_size);
|
||||
let ul_qdisc = Netlink::qdisc_from_ifname(config.upload_interface.as_str())?;
|
||||
let ul_safe_rates =
|
||||
generate_initial_speeds(config.upload_base_kbits, config.speed_hist_size);
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
owd_baseline,
|
||||
owd_recent,
|
||||
reflectors_lock,
|
||||
reselect_trigger,
|
||||
state_dl: State {
|
||||
current_bytes: 0,
|
||||
current_rate: 0.0,
|
||||
qdisc: dl_qdisc,
|
||||
nrate: 0,
|
||||
previous_bytes: 0,
|
||||
safe_rates: dl_safe_rates,
|
||||
stats_direction: down_direction,
|
||||
},
|
||||
state_ul: State {
|
||||
current_bytes: 0,
|
||||
current_rate: 0.0,
|
||||
qdisc: ul_qdisc,
|
||||
nrate: 0,
|
||||
previous_bytes: 0,
|
||||
safe_rates: ul_safe_rates,
|
||||
stats_direction: up_direction,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
&self,
|
||||
down_direction: StatsDirection,
|
||||
up_direction: StatsDirection,
|
||||
) -> anyhow::Result<()> {
|
||||
let sleep_time_s = self.config.min_change_interval.floor() as u64;
|
||||
let sleep_time_ns = ((self.config.min_change_interval % 1.0) * 1e9) as u32;
|
||||
let sleep_time = Duration::new(sleep_time_s, sleep_time_ns);
|
||||
|
||||
let (start_s, _) = Utils::get_current_time();
|
||||
let (mut lastchg_s, mut lastchg_ns) = Utils::get_current_time();
|
||||
let (start_s, _) = Utils::get_current_time()?;
|
||||
let (mut lastchg_s, mut lastchg_ns) = Utils::get_current_time()?;
|
||||
let mut lastchg_t = lastchg_s - start_s + lastchg_ns / 1e9;
|
||||
let mut lastdump_t = lastchg_t - 310.0;
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let down_qdisc =
|
||||
Netlink::qdisc_from_ifname(self.config.download_interface.as_str()).unwrap();
|
||||
let up_qdisc = Netlink::qdisc_from_ifname(self.config.upload_interface.as_str()).unwrap();
|
||||
let down_qdisc = Netlink::qdisc_from_ifname(self.config.download_interface.as_str())?;
|
||||
let up_qdisc = Netlink::qdisc_from_ifname(self.config.upload_interface.as_str())?;
|
||||
|
||||
// set qdisc rates to 60% of base rate to make sure we start with sane baselines
|
||||
let mut cur_dl_rate: f64 = self.config.download_base_kbits * 0.6;
|
||||
let mut cur_ul_rate: f64 = self.config.upload_base_kbits * 0.6;
|
||||
Netlink::set_qdisc_rate(down_qdisc, cur_dl_rate.floor() as u64).unwrap();
|
||||
Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.floor() as u64).unwrap();
|
||||
Netlink::set_qdisc_rate(down_qdisc, cur_dl_rate.round() as u64)?;
|
||||
Netlink::set_qdisc_rate(up_qdisc, cur_ul_rate.round() as u64)?;
|
||||
|
||||
let (mut prev_rx_bytes, mut prev_tx_bytes) =
|
||||
get_interface_stats(self.config.clone(), down_direction, up_direction);
|
||||
get_interface_stats(self.config.clone(), down_direction, up_direction)?;
|
||||
|
||||
if prev_rx_bytes == -1 || prev_tx_bytes == -1 {
|
||||
panic!("Couldn't retrieve stats from interface.");
|
||||
@@ -101,18 +167,10 @@ impl Ratecontroller {
|
||||
|
||||
let mut t_prev_bytes = lastchg_t;
|
||||
|
||||
let mut safe_dl_rates: Vec<f64> = Vec::new();
|
||||
let mut safe_ul_rates: Vec<f64> = Vec::new();
|
||||
for _ in 0..self.config.speed_hist_size {
|
||||
safe_dl_rates.push(
|
||||
(rand::thread_rng().next_u32() as f64 * 0.2 + 0.75)
|
||||
* self.config.download_base_kbits,
|
||||
);
|
||||
safe_ul_rates.push(
|
||||
(rand::thread_rng().next_u32() as f64 * 0.2 + 0.75)
|
||||
* self.config.download_base_kbits,
|
||||
);
|
||||
}
|
||||
let mut safe_dl_rates: Vec<f64> =
|
||||
generate_initial_speeds(self.config.download_base_kbits, self.config.speed_hist_size);
|
||||
let mut safe_ul_rates: Vec<f64> =
|
||||
generate_initial_speeds(self.config.upload_base_kbits, self.config.speed_hist_size);
|
||||
|
||||
let mut nrate_up = 0;
|
||||
let mut nrate_down = 0;
|
||||
@@ -126,35 +184,28 @@ impl Ratecontroller {
|
||||
speed_hist_fd_inner = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(self.config.speed_hist_file.as_str())
|
||||
.unwrap();
|
||||
.open(self.config.speed_hist_file.as_str())?;
|
||||
|
||||
speed_hist_fd_inner
|
||||
.write("time,counter,upspeed,downspeed\n".as_bytes())
|
||||
.unwrap();
|
||||
speed_hist_fd_inner.flush().unwrap();
|
||||
speed_hist_fd_inner.write("time,counter,upspeed,downspeed\n".as_bytes())?;
|
||||
speed_hist_fd_inner.flush()?;
|
||||
|
||||
speed_hist_fd = Some(speed_hist_fd_inner);
|
||||
|
||||
stats_fd_inner = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(self.config.stats_file.as_str())
|
||||
.unwrap();
|
||||
.open(self.config.stats_file.as_str())?;
|
||||
|
||||
stats_fd_inner
|
||||
.write(
|
||||
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
stats_fd_inner.flush().unwrap();
|
||||
stats_fd_inner.write(
|
||||
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n".as_bytes(),
|
||||
)?;
|
||||
stats_fd_inner.flush()?;
|
||||
|
||||
stats_fd = Some(stats_fd_inner);
|
||||
}
|
||||
|
||||
loop {
|
||||
let (mut now_s, now_ns) = Utils::get_current_time();
|
||||
let (mut now_s, now_ns) = Utils::get_current_time()?;
|
||||
let now_abstime = now_s + now_ns / 1e9;
|
||||
now_s = now_s - start_s;
|
||||
let now_t = now_s + now_ns / 1e9;
|
||||
@@ -221,7 +272,7 @@ impl Ratecontroller {
|
||||
next_ul_rate = cur_ul_rate;
|
||||
|
||||
let (cur_rx_bytes, cur_tx_bytes) =
|
||||
get_interface_stats(self.config.clone(), down_direction, up_direction);
|
||||
get_interface_stats(self.config.clone(), down_direction, up_direction)?;
|
||||
if cur_rx_bytes == -1 || cur_tx_bytes == -1 {
|
||||
warn!("One or both Netlink stats could not be read. Skipping rate control algorithm");
|
||||
} else if down_deltas.len() < 3 || up_deltas.len() < 3 {
|
||||
@@ -259,7 +310,7 @@ impl Ratecontroller {
|
||||
let max_dl =
|
||||
safe_dl_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
||||
next_dl_rate = cur_dl_rate
|
||||
* (1.0 + 0.1 * f64_max(0.0, 1.0 - cur_dl_rate / max_dl))
|
||||
* (1.0 + 0.1 * (1.0 - cur_dl_rate / max_dl).max(0.0))
|
||||
+ (self.config.download_base_kbits * 0.03);
|
||||
nrate_down = nrate_down + 1;
|
||||
nrate_down = nrate_down % self.config.speed_hist_size as usize;
|
||||
@@ -273,31 +324,31 @@ impl Ratecontroller {
|
||||
let max_ul =
|
||||
safe_ul_rates.iter().max_by(|a, b| a.total_cmp(b)).unwrap();
|
||||
next_ul_rate = cur_ul_rate
|
||||
* (1.0 + 0.1 * f64_max(0.0, 1.0 - cur_ul_rate / max_ul))
|
||||
* (1.0 + 0.1 * (1.0 - cur_ul_rate / max_ul).max(0.0))
|
||||
+ (self.config.download_base_kbits * 0.03);
|
||||
nrate_up = nrate_up + 1;
|
||||
nrate_up = nrate_up % self.config.speed_hist_size as usize;
|
||||
}
|
||||
|
||||
if down_delta_stat > self.config.download_delay_ms {
|
||||
if safe_dl_rates.len() > 0 {
|
||||
next_dl_rate = f64_min(
|
||||
0.9 * cur_dl_rate * rx_load,
|
||||
*safe_dl_rates.choose(&mut rng).unwrap(),
|
||||
)
|
||||
} else {
|
||||
next_dl_rate = 0.9 * cur_dl_rate * rx_load;
|
||||
match safe_dl_rates.choose(&mut rng) {
|
||||
Some(rnd_rate) => {
|
||||
next_dl_rate = rnd_rate.min(0.9 * cur_dl_rate * rx_load);
|
||||
}
|
||||
None => {
|
||||
next_dl_rate = 0.9 * cur_dl_rate * rx_load;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if up_delta_stat > self.config.upload_delay_ms {
|
||||
if safe_ul_rates.len() > 0 {
|
||||
next_ul_rate = f64_min(
|
||||
0.9 * cur_ul_rate * tx_load,
|
||||
*safe_ul_rates.choose(&mut rng).unwrap(),
|
||||
)
|
||||
} else {
|
||||
next_ul_rate = 0.9 * cur_ul_rate * tx_load;
|
||||
match safe_ul_rates.choose(&mut rng) {
|
||||
Some(rnd_rate) => {
|
||||
next_ul_rate = rnd_rate.min(0.9 * cur_ul_rate * tx_load);
|
||||
}
|
||||
None => {
|
||||
next_ul_rate = 0.9 * cur_ul_rate * tx_load;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,8 +358,8 @@ impl Ratecontroller {
|
||||
prev_rx_bytes = cur_rx_bytes;
|
||||
prev_tx_bytes = cur_tx_bytes;
|
||||
|
||||
next_dl_rate = f64_max(self.config.download_min_kbits, next_dl_rate).floor();
|
||||
next_ul_rate = f64_max(self.config.upload_min_kbits, next_ul_rate).floor();
|
||||
next_dl_rate = next_dl_rate.max(self.config.download_min_kbits).round();
|
||||
next_ul_rate = next_ul_rate.max(self.config.upload_min_kbits).round();
|
||||
|
||||
if next_dl_rate != cur_dl_rate || next_ul_rate != cur_ul_rate {
|
||||
info!(
|
||||
@@ -318,17 +369,17 @@ impl Ratecontroller {
|
||||
}
|
||||
|
||||
if next_dl_rate != cur_dl_rate {
|
||||
Netlink::set_qdisc_rate(down_qdisc, next_dl_rate as u64).unwrap();
|
||||
Netlink::set_qdisc_rate(down_qdisc, next_dl_rate as u64)?;
|
||||
}
|
||||
|
||||
if next_ul_rate != cur_ul_rate {
|
||||
Netlink::set_qdisc_rate(up_qdisc, next_ul_rate as u64).unwrap();
|
||||
Netlink::set_qdisc_rate(up_qdisc, next_ul_rate as u64)?;
|
||||
}
|
||||
|
||||
cur_dl_rate = next_dl_rate;
|
||||
cur_ul_rate = next_ul_rate;
|
||||
|
||||
(lastchg_s, lastchg_ns) = Utils::get_current_time();
|
||||
(lastchg_s, lastchg_ns) = Utils::get_current_time()?;
|
||||
|
||||
if rx_load > 0.0
|
||||
&& tx_load > 0.0
|
||||
|
||||
Reference in new issue
Block a user