Implement missing features, some cleanup
This commit is contained in:
1 parent
c06078fccb
commit
e3b7b7e9ba
12 files changed
+704
-174
No files matched your search
+127
-45
@@ -1,9 +1,12 @@
|
||||
use crate::netlink::Netlink;
|
||||
use crate::{Config, ReflectorStats, Utils};
|
||||
use log::{debug, error, info, warn};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use rand::RngCore;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::net::IpAddr;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -33,26 +36,26 @@ fn get_interface_stats(
|
||||
{
|
||||
Ok(stats) => (stats.rx_bytes as i128, stats.tx_bytes as i128),
|
||||
Err(e) => {
|
||||
println!("Error while retrieving stats: {}", e.to_string());
|
||||
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) => {
|
||||
println!("Error while retrieving stats: {}", e.to_string());
|
||||
error!("Error while retrieving stats: {}", e.to_string());
|
||||
(-1, -1)
|
||||
}
|
||||
};
|
||||
|
||||
let rx_bytes = match down_direction {
|
||||
RX => down_rx,
|
||||
TX => down_tx,
|
||||
StatsDirection::RX => down_rx,
|
||||
StatsDirection::TX => down_tx,
|
||||
};
|
||||
|
||||
let tx_bytes = match up_direction {
|
||||
RX => up_rx,
|
||||
TX => up_tx,
|
||||
StatsDirection::RX => up_rx,
|
||||
StatsDirection::TX => up_tx,
|
||||
};
|
||||
|
||||
(rx_bytes, tx_bytes)
|
||||
@@ -79,26 +82,13 @@ impl Ratecontroller {
|
||||
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let base_dl_rate = 45000.0;
|
||||
let base_ul_rate = 15000.0;
|
||||
let min_dl_rate = 5000.0;
|
||||
let min_ul_rate = 1000.0;
|
||||
let histsize = 100;
|
||||
|
||||
let dl_max_delta_owd = 15.0;
|
||||
let ul_max_delta_owd = 15.0;
|
||||
|
||||
let high_load_level = 0.8;
|
||||
|
||||
let down_ifindex =
|
||||
Netlink::find_interface(self.config.download_interface.as_str()).unwrap();
|
||||
let up_ifindex = Netlink::find_interface(self.config.upload_interface.as_str()).unwrap();
|
||||
let down_qdisc = Netlink::find_qdisc(down_ifindex).unwrap();
|
||||
let up_qdisc = Netlink::find_qdisc(up_ifindex).unwrap();
|
||||
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();
|
||||
|
||||
// set qdisc rates to 60% of base rate to make sure we start with sane baselines
|
||||
let mut cur_dl_rate: f64 = base_dl_rate * 0.6;
|
||||
let mut cur_ul_rate: f64 = base_ul_rate * 0.6;
|
||||
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();
|
||||
|
||||
@@ -113,14 +103,56 @@ impl Ratecontroller {
|
||||
|
||||
let mut safe_dl_rates: Vec<f64> = Vec::new();
|
||||
let mut safe_ul_rates: Vec<f64> = Vec::new();
|
||||
for i in 0..histsize {
|
||||
safe_dl_rates.push((rand::thread_rng().next_u32() as f64 * 0.2 + 0.75) * base_dl_rate);
|
||||
safe_ul_rates.push((rand::thread_rng().next_u32() as f64 * 0.2 + 0.75) * base_dl_rate);
|
||||
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 nrate_up = 0;
|
||||
let mut nrate_down = 0;
|
||||
|
||||
let mut speed_hist_fd: Option<File> = None;
|
||||
let mut speed_hist_fd_inner: File;
|
||||
let mut stats_fd: Option<File> = None;
|
||||
let mut stats_fd_inner: File;
|
||||
|
||||
if !self.config.suppress_statistics {
|
||||
speed_hist_fd_inner = File::options()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(self.config.speed_hist_file.as_str())
|
||||
.unwrap();
|
||||
|
||||
speed_hist_fd_inner
|
||||
.write("time,counter,upspeed,downspeed\n".as_bytes())
|
||||
.unwrap();
|
||||
speed_hist_fd_inner.flush().unwrap();
|
||||
|
||||
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();
|
||||
|
||||
stats_fd_inner
|
||||
.write(
|
||||
"times,timens,rxload,txload,deltadelaydown,deltadelayup,dlrate,uprate\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
stats_fd_inner.flush().unwrap();
|
||||
|
||||
stats_fd = Some(stats_fd_inner);
|
||||
}
|
||||
|
||||
loop {
|
||||
let (mut now_s, now_ns) = Utils::get_current_time();
|
||||
let now_abstime = now_s + now_ns / 1e9;
|
||||
@@ -128,6 +160,8 @@ impl Ratecontroller {
|
||||
let now_t = now_s + now_ns / 1e9;
|
||||
|
||||
if now_t - lastchg_t > self.config.min_change_interval {
|
||||
// if it's been long enough, and the stats indicate needing to change speeds
|
||||
// change speeds here
|
||||
let owd_baseline = self.owd_baseline.lock().unwrap();
|
||||
let owd_recent = self.owd_recent.lock().unwrap();
|
||||
let reflectors = self.reflectors_lock.lock().unwrap();
|
||||
@@ -135,6 +169,8 @@ impl Ratecontroller {
|
||||
let mut down_delta_stat: f64;
|
||||
let mut up_delta_stat: f64;
|
||||
|
||||
// If we have no reflector peers to iterate over, don't attempt any rate changes.
|
||||
// This will occur under normal operation when the reflector peers table is updated.
|
||||
if reflectors.len() > 0 {
|
||||
let mut down_deltas = Vec::new();
|
||||
let mut up_deltas = Vec::new();
|
||||
@@ -153,10 +189,11 @@ impl Ratecontroller {
|
||||
up_delta_stat = 0.0;
|
||||
|
||||
for reflector in reflectors.iter() {
|
||||
// only consider this data if it's less than 2 * tick_duration seconds old
|
||||
if owd_baseline.contains_key(reflector)
|
||||
&& owd_recent.contains_key(reflector)
|
||||
&& owd_recent[reflector].last_receive_time_s
|
||||
> now_abstime - 2.0 * self.config.tick_duration
|
||||
> now_abstime - 2.0 * self.config.tick_interval
|
||||
{
|
||||
down_deltas.push(
|
||||
owd_recent[reflector].down_ewma - owd_baseline[reflector].down_ewma,
|
||||
@@ -164,11 +201,19 @@ impl Ratecontroller {
|
||||
up_deltas.push(
|
||||
owd_recent[reflector].up_ewma - owd_baseline[reflector].up_ewma,
|
||||
);
|
||||
|
||||
debug!(
|
||||
"Reflector: {} down_delay: {} up_delay: {}",
|
||||
reflector,
|
||||
down_deltas.last().unwrap(),
|
||||
up_deltas.last().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if down_deltas.len() < 5 || up_deltas.len() < 5 {
|
||||
// trigger reselection
|
||||
warn!("Not enough delta values, triggering reselection");
|
||||
let _ = self.reselect_trigger.send(true);
|
||||
}
|
||||
|
||||
@@ -178,10 +223,10 @@ impl Ratecontroller {
|
||||
let (cur_rx_bytes, cur_tx_bytes) =
|
||||
get_interface_stats(self.config.clone(), down_direction, up_direction);
|
||||
if cur_rx_bytes == -1 || cur_tx_bytes == -1 {
|
||||
println!("One or both Netlink stats could not be read. Skipping rate control algorithm");
|
||||
warn!("One or both Netlink stats could not be read. Skipping rate control algorithm");
|
||||
} else if down_deltas.len() < 3 || up_deltas.len() < 3 {
|
||||
next_dl_rate = min_dl_rate;
|
||||
next_ul_rate = min_ul_rate;
|
||||
next_dl_rate = self.config.download_min_kbits;
|
||||
next_ul_rate = self.config.upload_min_kbits;
|
||||
} else {
|
||||
down_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
up_deltas.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
@@ -207,34 +252,34 @@ impl Ratecontroller {
|
||||
next_dl_rate = cur_dl_rate;
|
||||
|
||||
if down_delta_stat > 0.0
|
||||
&& down_delta_stat < dl_max_delta_owd
|
||||
&& rx_load > high_load_level
|
||||
&& down_delta_stat < self.config.download_delay_ms
|
||||
&& rx_load > self.config.high_load_level
|
||||
{
|
||||
safe_dl_rates[nrate_down] = (cur_dl_rate * rx_load).floor();
|
||||
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))
|
||||
+ (base_dl_rate * 0.03);
|
||||
+ (self.config.download_base_kbits * 0.03);
|
||||
nrate_down = nrate_down + 1;
|
||||
nrate_down = nrate_down % histsize;
|
||||
nrate_down = nrate_down % self.config.speed_hist_size as usize;
|
||||
}
|
||||
|
||||
if up_delta_stat > 0.0
|
||||
&& up_delta_stat < dl_max_delta_owd
|
||||
&& tx_load > high_load_level
|
||||
&& up_delta_stat < self.config.download_delay_ms
|
||||
&& tx_load > self.config.high_load_level
|
||||
{
|
||||
safe_ul_rates[nrate_up] = (cur_ul_rate * tx_load).floor();
|
||||
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))
|
||||
+ (base_dl_rate * 0.03);
|
||||
+ (self.config.download_base_kbits * 0.03);
|
||||
nrate_up = nrate_up + 1;
|
||||
nrate_up = nrate_up % histsize;
|
||||
nrate_up = nrate_up % self.config.speed_hist_size as usize;
|
||||
}
|
||||
|
||||
if down_delta_stat > dl_max_delta_owd {
|
||||
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,
|
||||
@@ -245,7 +290,7 @@ impl Ratecontroller {
|
||||
}
|
||||
}
|
||||
|
||||
if up_delta_stat > ul_max_delta_owd {
|
||||
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,
|
||||
@@ -262,11 +307,11 @@ impl Ratecontroller {
|
||||
prev_rx_bytes = cur_rx_bytes;
|
||||
prev_tx_bytes = cur_tx_bytes;
|
||||
|
||||
next_dl_rate = f64_max(min_dl_rate, next_dl_rate).floor();
|
||||
next_ul_rate = f64_max(min_ul_rate, next_ul_rate).floor();
|
||||
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();
|
||||
|
||||
if next_dl_rate != cur_dl_rate || next_ul_rate != cur_ul_rate {
|
||||
println!(
|
||||
info!(
|
||||
"next_ul_rate {} next_dl_rate {}",
|
||||
next_ul_rate, next_dl_rate
|
||||
);
|
||||
@@ -290,7 +335,7 @@ impl Ratecontroller {
|
||||
&& down_delta_stat > 0.0
|
||||
&& up_delta_stat > 0.0
|
||||
{
|
||||
println!(
|
||||
debug!(
|
||||
"{},{},{},{},{},{},{},{}",
|
||||
lastchg_s,
|
||||
lastchg_ns,
|
||||
@@ -301,6 +346,25 @@ impl Ratecontroller {
|
||||
cur_dl_rate,
|
||||
cur_ul_rate
|
||||
);
|
||||
|
||||
if let Some(ref mut fd) = stats_fd {
|
||||
if let Err(e) = fd.write(
|
||||
format!(
|
||||
"{},{},{},{},{},{},{},{}\n",
|
||||
lastchg_s,
|
||||
lastchg_ns,
|
||||
rx_load,
|
||||
tx_load,
|
||||
down_delta_stat,
|
||||
up_delta_stat,
|
||||
cur_dl_rate,
|
||||
cur_ul_rate
|
||||
)
|
||||
.as_bytes(),
|
||||
) {
|
||||
warn!("Failed to write statistics: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastchg_s = lastchg_s - start_s;
|
||||
@@ -308,6 +372,24 @@ impl Ratecontroller {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut fd) = speed_hist_fd {
|
||||
if now_t - lastdump_t > 300.0 {
|
||||
for i in 0..self.config.speed_hist_size as usize {
|
||||
if let Err(e) = fd.write(
|
||||
format!(
|
||||
"{},{},{},{}\n",
|
||||
now_t, i, safe_ul_rates[i], safe_dl_rates[i]
|
||||
)
|
||||
.as_bytes(),
|
||||
) {
|
||||
warn!("Failed to write speed history file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
lastdump_t = now_t;
|
||||
}
|
||||
}
|
||||
|
||||
sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user