Use proper error handling instead of lurking panics everywhere

This commit is contained in:
Nils Andreas Svee committed 2022-10-04 16:20:24 +02:00
1 parent 999987d157
commit 214ffef6f3
11 files changed
+349 -392

No files matched your search

+139 -88
View File
@@ -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