accounting: make --bwlimit set with the rc options/set take effect

Setting the bandwidth limit with options/set changed the option but
never reached the token bucket, so it had no effect, whereas
core/bwlimit worked. Apply the limit when the config is reloaded.

Note that a bandwidth timetable set this way isn't scheduled - the
limit for the current time is applied.
This commit is contained in:
Nick Craig-Wood committed 2026-09-21 17:50:43 +01:00
1 parent 3cd41095b4
commit 48da73ad5a
3 files changed
+70

No files matched your search

+22
View File
@@ -35,9 +35,30 @@ type tokenBucket struct {
curr buckets
prev buckets
toggledOff bool
started bool
currLimit fs.BwTimeSlot
}
func init() {
// Apply --bwlimit when the config is reloaded, e.g. by the rc
fs.BwLimitReload = TokenBucket.reload
}
// reload applies a changed --bwlimit to the token bucket.
//
// This does nothing before the token bucket is started as
// StartTokenBucket reads the limit itself.
func (tb *tokenBucket) reload(ci *fs.ConfigInfo) error {
limitNow := ci.BwLimit.LimitAt(time.Now())
tb.mu.Lock()
defer tb.mu.Unlock()
if !tb.started || tb.currLimit.Bandwidth == limitNow.Bandwidth {
return nil
}
tb._setBwLimit(limitNow.Bandwidth)
return nil
}
// Return true if limit is disabled
//
// Call with lock held
@@ -111,6 +132,7 @@ func (tb *tokenBucket) StartTokenBucket(ctx context.Context) {
tb.mu.Lock()
defer tb.mu.Unlock()
ci := fs.GetConfig(ctx)
tb.started = true
tb.currLimit = ci.BwLimit.LimitAt(time.Now())
if tb.currLimit.Bandwidth.IsSet() {
tb.curr = newTokenBucket(tb.currLimit.Bandwidth)
+40
View File
@@ -115,6 +115,46 @@ func TestRcBwLimit(t *testing.T) {
}
// Check options/set applies a changed --bwlimit to the token bucket
func TestBwLimitOptionsSet(t *testing.T) {
ctx := context.Background()
ci := fs.GetConfig(ctx)
oldBwLimit, oldStarted, oldCurr, oldCurrLimit := ci.BwLimit, TokenBucket.started, TokenBucket.curr, TokenBucket.currLimit
defer func() {
ci.BwLimit = oldBwLimit
TokenBucket.started, TokenBucket.curr, TokenBucket.currLimit = oldStarted, oldCurr, oldCurrLimit
}()
setBwLimit := func(rate string) {
t.Helper()
call := rc.Calls.Get("options/set")
require.NotNil(t, call)
_, err := call.Fn(ctx, rc.Params{"main": rc.Params{"BwLimit": rate}})
require.NoError(t, err)
}
// Nothing happens before the token bucket is started
TokenBucket.started = false
TokenBucket.curr = buckets{}
setBwLimit("100k")
assert.Nil(t, TokenBucket.curr[TokenBucketSlotAccounting])
// Once started the limit is applied
TokenBucket.started = true
TokenBucket.currLimit = fs.BwTimeSlot{}
setBwLimit("200k")
require.NotNil(t, TokenBucket.curr[TokenBucketSlotTransportTx])
assert.Equal(t, rate.Limit(200*1024), TokenBucket.curr[TokenBucketSlotTransportTx].Limit())
// and changed again
setBwLimit("300k")
assert.Equal(t, rate.Limit(300*1024), TokenBucket.curr[TokenBucketSlotTransportTx].Limit())
// and turned off
setBwLimit("off")
assert.Nil(t, TokenBucket.curr[TokenBucketSlotTransportTx])
}
// Check setting the limit records it and respects the SIGUSR2 toggle
func TestSetBwLimitToggledOff(t *testing.T) {
oldCurr, oldPrev, oldToggledOff, oldCurrLimit := TokenBucket.curr, TokenBucket.prev, TokenBucket.toggledOff, TokenBucket.currLimit
+8
View File
@@ -713,6 +713,10 @@ func init() {
// be there but we can't move due to them being visible here in the rc.
var LogReload = func(*ConfigInfo) error { return nil }
// BwLimitReload is written by fs/accounting to apply --bwlimit when
// the config is reloaded, e.g. by the rc options/set.
var BwLimitReload = func(*ConfigInfo) error { return nil }
// Reload assumes the config has been edited and does what is necessary to make it live
func (ci *ConfigInfo) Reload(ctx context.Context) error {
// Set -vv if --dump is in use
@@ -765,6 +769,10 @@ func (ci *ConfigInfo) Reload(ctx context.Context) error {
nonZero(&ci.Transfers)
nonZero(&ci.Checkers)
if err := BwLimitReload(ci); err != nil {
return err
}
return LogReload(ci)
}