162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * lib/minmax.c: windowed min/max tracker 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Kathleen Nichols' algorithm for tracking the minimum (or maximum) 662306a36Sopenharmony_ci * value of a data stream over some fixed time interval. (E.g., 762306a36Sopenharmony_ci * the minimum RTT over the past five minutes.) It uses constant 862306a36Sopenharmony_ci * space and constant time per update yet almost always delivers 962306a36Sopenharmony_ci * the same minimum as an implementation that has to keep all the 1062306a36Sopenharmony_ci * data in the window. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The algorithm keeps track of the best, 2nd best & 3rd best min 1362306a36Sopenharmony_ci * values, maintaining an invariant that the measurement time of 1462306a36Sopenharmony_ci * the n'th best >= n-1'th best. It also makes sure that the three 1562306a36Sopenharmony_ci * values are widely separated in the time window since that bounds 1662306a36Sopenharmony_ci * the worse case error when that data is monotonically increasing 1762306a36Sopenharmony_ci * over the window. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Upon getting a new min, we can forget everything earlier because 2062306a36Sopenharmony_ci * it has no value - the new min is <= everything else in the window 2162306a36Sopenharmony_ci * by definition and it's the most recent. So we restart fresh on 2262306a36Sopenharmony_ci * every new min and overwrites 2nd & 3rd choices. The same property 2362306a36Sopenharmony_ci * holds for 2nd & 3rd best. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/win_minmax.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* As time advances, update the 1st, 2nd, and 3rd choices. */ 2962306a36Sopenharmony_cistatic u32 minmax_subwin_update(struct minmax *m, u32 win, 3062306a36Sopenharmony_ci const struct minmax_sample *val) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u32 dt = val->t - m->s[0].t; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (unlikely(dt > win)) { 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci * Passed entire window without a new val so make 2nd 3762306a36Sopenharmony_ci * choice the new val & 3rd choice the new 2nd choice. 3862306a36Sopenharmony_ci * we may have to iterate this since our 2nd choice 3962306a36Sopenharmony_ci * may also be outside the window (we checked on entry 4062306a36Sopenharmony_ci * that the third choice was in the window). 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci m->s[0] = m->s[1]; 4362306a36Sopenharmony_ci m->s[1] = m->s[2]; 4462306a36Sopenharmony_ci m->s[2] = *val; 4562306a36Sopenharmony_ci if (unlikely(val->t - m->s[0].t > win)) { 4662306a36Sopenharmony_ci m->s[0] = m->s[1]; 4762306a36Sopenharmony_ci m->s[1] = m->s[2]; 4862306a36Sopenharmony_ci m->s[2] = *val; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci } else if (unlikely(m->s[1].t == m->s[0].t) && dt > win/4) { 5162306a36Sopenharmony_ci /* 5262306a36Sopenharmony_ci * We've passed a quarter of the window without a new val 5362306a36Sopenharmony_ci * so take a 2nd choice from the 2nd quarter of the window. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci m->s[2] = m->s[1] = *val; 5662306a36Sopenharmony_ci } else if (unlikely(m->s[2].t == m->s[1].t) && dt > win/2) { 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * We've passed half the window without finding a new val 5962306a36Sopenharmony_ci * so take a 3rd choice from the last half of the window 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci m->s[2] = *val; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci return m->s[0].v; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Check if new measurement updates the 1st, 2nd or 3rd choice max. */ 6762306a36Sopenharmony_ciu32 minmax_running_max(struct minmax *m, u32 win, u32 t, u32 meas) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct minmax_sample val = { .t = t, .v = meas }; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (unlikely(val.v >= m->s[0].v) || /* found new max? */ 7262306a36Sopenharmony_ci unlikely(val.t - m->s[2].t > win)) /* nothing left in window? */ 7362306a36Sopenharmony_ci return minmax_reset(m, t, meas); /* forget earlier samples */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (unlikely(val.v >= m->s[1].v)) 7662306a36Sopenharmony_ci m->s[2] = m->s[1] = val; 7762306a36Sopenharmony_ci else if (unlikely(val.v >= m->s[2].v)) 7862306a36Sopenharmony_ci m->s[2] = val; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return minmax_subwin_update(m, win, &val); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL(minmax_running_max); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Check if new measurement updates the 1st, 2nd or 3rd choice min. */ 8562306a36Sopenharmony_ciu32 minmax_running_min(struct minmax *m, u32 win, u32 t, u32 meas) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct minmax_sample val = { .t = t, .v = meas }; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (unlikely(val.v <= m->s[0].v) || /* found new min? */ 9062306a36Sopenharmony_ci unlikely(val.t - m->s[2].t > win)) /* nothing left in window? */ 9162306a36Sopenharmony_ci return minmax_reset(m, t, meas); /* forget earlier samples */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (unlikely(val.v <= m->s[1].v)) 9462306a36Sopenharmony_ci m->s[2] = m->s[1] = val; 9562306a36Sopenharmony_ci else if (unlikely(val.v <= m->s[2].v)) 9662306a36Sopenharmony_ci m->s[2] = val; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return minmax_subwin_update(m, win, &val); 9962306a36Sopenharmony_ci} 100