#pragma once
#include "ravg.bpf.h"
#define RAVG_FN_ATTRS inline __attribute__((unused, always_inline))
static RAVG_FN_ATTRS void ravg_add(u64 *sum, u64 addend)
{
u64 new = *sum + addend;
if (new >= *sum)
*sum = new;
else
*sum = -1;
}
static RAVG_FN_ATTRS u64 ravg_decay(u64 v, u32 shift)
{
if (shift >= 64)
return 0;
else
return v >> shift;
}
static RAVG_FN_ATTRS u32 ravg_normalize_dur(u32 dur, u32 half_life)
{
if (dur < half_life)
return (((u64)dur << RAVG_FRAC_BITS) + half_life - 1) /
half_life;
else
return 1 << RAVG_FRAC_BITS;
}
static const u64 ravg_full_sum[] = {
524288, 786432, 917504, 983040,
1015808, 1032192, 1040384, 1044480,
1046528, 1047552, 1048064, 1048320,
1048448, 1048512, 1048544, 1048560,
1048568, 1048572, 1048574, 1048575,
};
static const int ravg_full_sum_len = sizeof(ravg_full_sum) / sizeof(ravg_full_sum[0]);
static RAVG_FN_ATTRS void ravg_accumulate(struct ravg_data *rd, u64 new_val, u64 now,
u32 half_life)
{
u32 cur_seq, val_seq, seq_delta;
if (now < rd->val_at)
now = rd->val_at;
cur_seq = now / half_life;
val_seq = rd->val_at / half_life;
seq_delta = cur_seq - val_seq;
if (seq_delta > 0) {
rd->old = ravg_decay(rd->old, seq_delta);
ravg_add(&rd->old, ravg_decay(rd->cur, seq_delta));
rd->cur = 0;
}
if (!rd->val)
goto out;
if (seq_delta > 0) {
u32 dur;
dur = ravg_normalize_dur(half_life - rd->val_at % half_life, half_life);
ravg_add(&rd->old, rd->val * ravg_decay(dur, seq_delta));
if (seq_delta > 1) {
u32 idx = seq_delta - 2;
if (idx >= ravg_full_sum_len)
idx = ravg_full_sum_len - 1;
ravg_add(&rd->old, rd->val * ravg_full_sum[idx]);
}
rd->cur += rd->val * ravg_normalize_dur(now % half_life,
half_life);
} else {
rd->cur += rd->val * ravg_normalize_dur(now - rd->val_at,
half_life);
}
out:
if (new_val >= 1LLU << RAVG_VAL_BITS)
rd->val = (1LLU << RAVG_VAL_BITS) - 1;
else
rd->val = new_val;
rd->val_at = now;
}
static RAVG_FN_ATTRS void ravg_transfer(struct ravg_data *base, u64 base_new_val,
struct ravg_data *xfer, u64 xfer_new_val,
u32 half_life, bool is_xfer_in)
{
if ((s64)(base->val_at - xfer->val_at) < 0)
ravg_accumulate(base, base_new_val, xfer->val_at, half_life);
else if ((s64)(base->val_at - xfer->val_at) > 0)
ravg_accumulate(xfer, xfer_new_val, base->val_at, half_life);
if (is_xfer_in) {
base->old += xfer->old;
base->cur += xfer->cur;
} else {
if (base->old > xfer->old)
base->old -= xfer->old;
else
base->old = 0;
if (base->cur > xfer->cur)
base->cur -= xfer->cur;
else
base->cur = 0;
}
}
static __always_inline u64 u64_x_u32_rshift(u64 a, u32 b, u32 rshift)
{
const u64 mask32 = (u32)-1;
u64 al = a & mask32;
u64 ah = (a & (mask32 << 32)) >> 32;
al *= b;
ah *= b;
al >>= rshift;
if (rshift <= 32)
ah <<= 32 - rshift;
else
ah >>= rshift - 32;
return al + ah;
}
static RAVG_FN_ATTRS void ravg_scale(struct ravg_data *rd, u32 mult, u32 rshift)
{
rd->val = u64_x_u32_rshift(rd->val, mult, rshift);
rd->old = u64_x_u32_rshift(rd->old, mult, rshift);
rd->cur = u64_x_u32_rshift(rd->cur, mult, rshift);
}
static RAVG_FN_ATTRS u64 ravg_read(struct ravg_data *rd, u64 now, u64 half_life)
{
struct ravg_data trd;
u32 elapsed;
if (now < rd->val_at)
now = rd->val_at;
elapsed = now % half_life;
trd = *rd;
rd = &trd;
ravg_accumulate(rd, 0, now, half_life);
if (elapsed) {
u32 progress = ravg_normalize_dur(elapsed, half_life);
u64 old = u64_x_u32_rshift(rd->old,
(1 << RAVG_FRAC_BITS) - progress / 2,
RAVG_FRAC_BITS);
u64 cur = rd->cur / 2;
return old + cur;
} else {
return rd->old;
}
}