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