# latency reported by fping(8). It is useless on its own, and should be started
# by the accompanying shellscript wrapper found in the same directory.
#
-# This is work in progresa and has severe limitations. Latency upswings that
+# This is work in progress and has severe limitations. Latency upswings that
# are NOT caused by bufferbloat cannot be told apart from those that are caused
# by it, but hopefully will be in a future version. Presently, only downstream
# bandwidth is being shaped.
}
+# Compute a sensible number of steps between the MIN and MAX bandwidth
+# boundaries configured.
+# XXX TODO impl. tunable for acting more fine-grained?
function slice_bw_window_rx(bw_lower_bound_rx, bw_upper_bound_rx) {
bw_bound_delta_rx = (bw_upper_bound_rx - bw_lower_bound_rx)
bw_steps_rx = 1+int((log(bw_bound_delta_rx)/log(10)) ^ 1.6)
}
+# Update bandwidth estimates from interface statistics. Ticks on each latency
+# record, but will throttle if these flow in too fast.
function update_bw() {
# If the last update happened rather recently, do not compute new bw stats.
+ # XXX TODO do we need to snashot ts here?
if (! (update_ts())) {
#print "# " ts " too fast, skiping bw updates " ts - ts_old
return
getline rx < STATS_RX
close(STATS_RX)
+ # Convert to bytes to Kbit, compute rates over ts_delta (given in seconds)
rx_delta=int(((rx - rx_old) / 128))
rx_rate=int((rx_delta / ts_delta))
tx_delta=int(((tx - tx_old) / 128))
tx_rate=int((tx_delta / ts_delta))
+
+ # Record max. rates in each direction
if (tx_rate_max < tx_rate) {
tx_rate_max = tx_rate
print "# " ts " new peak tx_rate=" tx_rate_max
}
+# Called after parsing each latency record in the fping stream.
function process_record(pn, bytes, lat) {
+ # If fing happens to pass in a latency record with a peername that was not
+ # announced upon startup, we exit here. Should never happen, but provides
+ # SOME protection against fping changing record format in a potential future
+ # version.
if (! (pn in slotindex)) {
print "FATAL: BOGUS PEER: " pn
exit 1
}
+ # Timeouts shall not affect latency averages, but will trigger their own kind
+ # of event if need be (SQ bw collapse).
if (! (bytes == 0 && lat == 9999)) {
update_pingstats(pn, lat)
}
+ # Only factor peer latency data into SQM decisions once enough data has been
+ # collected.
if (have_baseline[pn]) {
adjust_sqm(pn, lat)
}
}
+# The main policy engine that decides which direction cake-shaped bandwidth
+# shall be adjusted.
function adjust_sqm(peername, latency) {
adjust_ts_delta = (ts - adjust_old)
- # do not try to set bw more than once a second
+ # Do not try to adjust bw more than once a second.
if (adjust_ts_delta < 1.0) {
# print "# " ts " looping too fast, skipping SQM update adjust_ts_delta=" adjust_ts_delta
return
}
- # Assume links are clogged, so we short-circuit a bandwidth collapse
+ # Having seen too many timeouts in a row we assume links are clogged, so we
+ # short-circuit a bandwidth collapse to the lowest step.
if (consec_timeouts > ((1.5 * peer_count) + 2)) {
print "# " ts " --- TIMEOUT-caused bw collapse triggered"
# print "# " ts " too many TIMEOUTS in a row, decreasing SQM bw"
}
not_increasing_count[peername]++
} else {
+ # Here, we are undecided about any latency trends, and just stay at the
+ # current bw step.
# peer_stats_print(peername, "# adjst_sqm noop")
}
}
+# Helper function to print "interesting" per-peer data
function peer_stats_print(pn, affix) {
printf("# %-12.1f %12s lat=%4.1f plat=%4.1f pplat=%4.1f min=%4.1f avg=%04.1f %s\n",
ts, pn, lat, ping_prev[pn], ping_pprev[pn], ping_min[pn], ping_avgs[pn], affix)
}
+# Udate and conditionally compute averages over per-peer latency records.
function update_pingstats(peername, latency) {
slotindex[peername]++
pingslot = peername ":" slotindex[peername]
print "# READY PEER: " peername
}
slotindex[peername] = 0
+ # Compute a new average whenever we complete a cycle over all configured
+ # PINGSLOTS. XXX TODO maybe change this to a better moving average?
update_ping_avgs(peername)
- # print peername " avg " ping_avgs[peername] " cur " latency
}
- if (latency < ping_min[peername] && latency > FUDGE) {
+ if (latency < ping_min[peername] && latency > FUDGE) { # XXX TODO what about too high static FUDGE here?
ping_min[peername] = latency
- # print peername " lowest " latency
}
}
+# Simple helper function to compute the average over a peer's PINGSLOTS.
function update_ping_avgs(peername) {
ping_sum = 0
for (k = 0; k <= PINGSLOTS; ++k) {
}
+# Shell out to `tc` to update cake bandwidth settings
function set_bw(dev, kbit) {
system("set -x; tc qdisc change root dev " dev " cake bandwidth " kbit "Kbit")
}