net/sockstats: fix calculation of radio power usage

When splitting the radio monitor usage array, we were splitting at now %
3600 to get values into chronological order.  This caused the value for
the final second to be included at the beginning of the ordered slice
rather than the end.  If there was activity during that final second, an
extra five seconds of high power usage would get recorded in some cases.
This could result in a final calculation of greater than 100% usage.

This corrects that by splitting values at (now+1 % 3600).

This also simplifies the percentage calculation by always rounding
values down, which is sufficient for our usage.

Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
Will Norris 2023-04-19 12:56:52 -07:00 committed by Will Norris
parent 7f057d7489
commit 7c386ca6d2
2 changed files with 19 additions and 7 deletions

View File

@ -8,7 +8,6 @@
import (
"context"
"fmt"
"math"
"net"
"strings"
"sync"
@ -347,10 +346,12 @@ func (rm *radioMonitor) radioHighPercent() int64 {
}
periodStart := now - periodLength // start of current reporting period
// slices of radio usage, with values in chronological order
// split into slices of radio usage, with values in chronological order.
// split at now+1 so that the current second is in the second slice.
split := (now + 1) % radioSampleSize
slices := [2][]int64{
rm.usage[now%radioSampleSize:],
rm.usage[:now%radioSampleSize],
rm.usage[split:],
rm.usage[:split],
}
var highPowerSec int64 // total seconds radio was in high power (active or idle)
var c int // counter
@ -369,5 +370,8 @@ func (rm *radioMonitor) radioHighPercent() int64 {
}
}
return int64(math.Round(float64(highPowerSec) / float64(periodLength) * 100))
if highPowerSec == 0 {
return 0
}
return highPowerSec * 100 / periodLength
}

View File

@ -39,7 +39,7 @@ func(tt *testTime, rm *radioMonitor) {
rm.active()
tt.Add(10 * time.Second)
},
50, // radio on 5 seconds of every 10 seconds
50, // radio on 5 seconds of 10 seconds
},
{
"400 iterations: 2 sec active, 1 min idle",
@ -49,11 +49,19 @@ func(tt *testTime, rm *radioMonitor) {
rm.active()
tt.Add(1 * time.Second)
rm.active()
tt.Add(1 * time.Minute)
tt.Add(59 * time.Second)
}
},
10, // radio on 6 seconds of every minute
},
{
"activity at end of time window",
func(tt *testTime, rm *radioMonitor) {
tt.Add(2 * time.Second)
rm.active()
},
50,
},
}
for _, tt := range tests {