Skip to content
Snippets Groups Projects
Commit e476ffc2 authored by Nick Mathewson's avatar Nick Mathewson :game_die:
Browse files

Merge branch 'bug1789'

parents fb34c66e 9cba61eb
No related branches found
No related tags found
No related merge requests found
o Minor features:
- Be more generous with how much bandwidth we'd use up (with
accounting enabled) before entering "soft hibernation".
Previously, we'd hibernate once we'd used up 95% of our
allotment. Now, we use up 95% of our allotment, AND make sure
that we have no more than 500MB (or 3 hours of expected traffic,
whichever is lower) remaining before we enter soft hibernation.
o Minor bugfixes:
- For bandwidth accounting, calculate our expected bandwidth rate
based on the time during which we were active and not in
soft-hibernation during the last interval. Previously, we were
also considering the time spent in soft-hibernation. If this
was a long time, we would wind up underestimating our bandwidth
by a lot, and skewing our wakeup time towards the start of the
accounting interval. Fixes bug 1789. Bugfix on 0.0.9pre5.
...@@ -428,6 +428,9 @@ static config_var_t _state_vars[] = { ...@@ -428,6 +428,9 @@ static config_var_t _state_vars[] = {
V(AccountingExpectedUsage, MEMUNIT, NULL), V(AccountingExpectedUsage, MEMUNIT, NULL),
V(AccountingIntervalStart, ISOTIME, NULL), V(AccountingIntervalStart, ISOTIME, NULL),
V(AccountingSecondsActive, INTERVAL, NULL), V(AccountingSecondsActive, INTERVAL, NULL),
V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
V(AccountingSoftLimitHitAt, ISOTIME, NULL),
V(AccountingBytesAtSoftLimit, MEMUNIT, NULL),
VAR("EntryGuard", LINELIST_S, EntryGuards, NULL), VAR("EntryGuard", LINELIST_S, EntryGuards, NULL),
VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL), VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL),
......
...@@ -95,6 +95,13 @@ static uint64_t n_bytes_read_in_interval = 0; ...@@ -95,6 +95,13 @@ static uint64_t n_bytes_read_in_interval = 0;
static uint64_t n_bytes_written_in_interval = 0; static uint64_t n_bytes_written_in_interval = 0;
/** How many seconds have we been running this interval? */ /** How many seconds have we been running this interval? */
static uint32_t n_seconds_active_in_interval = 0; static uint32_t n_seconds_active_in_interval = 0;
/** How many seconds were we active in this interval before we hit our soft
* limit? */
static int n_seconds_to_hit_soft_limit = 0;
/** When in this interval was the soft limit hit. */
static time_t soft_limit_hit_at = 0;
/** How many bytes had we read/written when we hit the soft limit? */
static uint64_t n_bytes_at_soft_limit = 0;
/** When did this accounting interval start? */ /** When did this accounting interval start? */
static time_t interval_start_time = 0; static time_t interval_start_time = 0;
/** When will this accounting interval end? */ /** When will this accounting interval end? */
...@@ -374,23 +381,42 @@ configure_accounting(time_t now) ...@@ -374,23 +381,42 @@ configure_accounting(time_t now)
static void static void
update_expected_bandwidth(void) update_expected_bandwidth(void)
{ {
uint64_t used, expected; uint64_t expected;
uint64_t max_configured = (get_options()->BandwidthRate * 60); or_options_t *options= get_options();
uint64_t max_configured = (options->RelayBandwidthRate > 0 ?
if (n_seconds_active_in_interval < 1800) { options->RelayBandwidthRate :
options->BandwidthRate) * 60;
#define MIN_TIME_FOR_MEASUREMENT (1800)
if (soft_limit_hit_at > interval_start_time && n_bytes_at_soft_limit &&
(soft_limit_hit_at - interval_start_time) > MIN_TIME_FOR_MEASUREMENT) {
/* If we hit our soft limit last time, only count the bytes up to that
* time. This is a better predictor of our actual bandwidth than
* considering the entirety of the last interval, since we likely started
* using bytes very slowly once we hit our soft limit. */
expected = n_bytes_at_soft_limit /
(soft_limit_hit_at - interval_start_time);
expected /= 60;
} else if (n_seconds_active_in_interval >= MIN_TIME_FOR_MEASUREMENT) {
/* Otherwise, we either measured enough time in the last interval but
* never hit our soft limit, or we're using a state file from a Tor that
* doesn't know to store soft-limit info. Just take rate at which
* we were reading/writing in the last interval as our expected rate.
*/
uint64_t used = MAX(n_bytes_written_in_interval,
n_bytes_read_in_interval);
expected = used / (n_seconds_active_in_interval / 60);
} else {
/* If we haven't gotten enough data last interval, set 'expected' /* If we haven't gotten enough data last interval, set 'expected'
* to 0. This will set our wakeup to the start of the interval. * to 0. This will set our wakeup to the start of the interval.
* Next interval, we'll choose our starting time based on how much * Next interval, we'll choose our starting time based on how much
* we sent this interval. * we sent this interval.
*/ */
expected = 0; expected = 0;
} else {
used = n_bytes_written_in_interval < n_bytes_read_in_interval ?
n_bytes_read_in_interval : n_bytes_written_in_interval;
expected = used / (n_seconds_active_in_interval / 60);
if (expected > max_configured)
expected = max_configured;
} }
if (expected > max_configured)
expected = max_configured;
expected_bandwidth_usage = expected; expected_bandwidth_usage = expected;
} }
...@@ -408,6 +434,9 @@ reset_accounting(time_t now) ...@@ -408,6 +434,9 @@ reset_accounting(time_t now)
n_bytes_read_in_interval = 0; n_bytes_read_in_interval = 0;
n_bytes_written_in_interval = 0; n_bytes_written_in_interval = 0;
n_seconds_active_in_interval = 0; n_seconds_active_in_interval = 0;
n_bytes_at_soft_limit = 0;
soft_limit_hit_at = 0;
n_seconds_to_hit_soft_limit = 0;
} }
/** Return true iff we should save our bandwidth usage to disk. */ /** Return true iff we should save our bandwidth usage to disk. */
...@@ -568,6 +597,10 @@ accounting_record_bandwidth_usage(time_t now, or_state_t *state) ...@@ -568,6 +597,10 @@ accounting_record_bandwidth_usage(time_t now, or_state_t *state)
state->AccountingSecondsActive = n_seconds_active_in_interval; state->AccountingSecondsActive = n_seconds_active_in_interval;
state->AccountingExpectedUsage = expected_bandwidth_usage; state->AccountingExpectedUsage = expected_bandwidth_usage;
state->AccountingSecondsToReachSoftLimit = n_seconds_to_hit_soft_limit;
state->AccountingSoftLimitHitAt = soft_limit_hit_at;
state->AccountingBytesAtSoftLimit = n_bytes_at_soft_limit;
or_state_mark_dirty(state, or_state_mark_dirty(state,
now+(get_options()->AvoidDiskWrites ? 7200 : 60)); now+(get_options()->AvoidDiskWrites ? 7200 : 60));
...@@ -591,10 +624,6 @@ read_bandwidth_usage(void) ...@@ -591,10 +624,6 @@ read_bandwidth_usage(void)
if (!state) if (!state)
return -1; return -1;
/* Okay; it looks like the state file is more up-to-date than the
* bw_accounting file, or the bw_accounting file is nonexistent,
* or the bw_accounting file is corrupt.
*/
log_info(LD_ACCT, "Reading bandwidth accounting data from state file"); log_info(LD_ACCT, "Reading bandwidth accounting data from state file");
n_bytes_read_in_interval = state->AccountingBytesReadInInterval; n_bytes_read_in_interval = state->AccountingBytesReadInInterval;
n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval; n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval;
...@@ -602,6 +631,21 @@ read_bandwidth_usage(void) ...@@ -602,6 +631,21 @@ read_bandwidth_usage(void)
interval_start_time = state->AccountingIntervalStart; interval_start_time = state->AccountingIntervalStart;
expected_bandwidth_usage = state->AccountingExpectedUsage; expected_bandwidth_usage = state->AccountingExpectedUsage;
/* Older versions of Tor (before 0.2.2.17-alpha or so) didn't generate these
* fields. If you switch back and forth, you might get an
* AccountingSoftLimitHitAt value from long before the most recent
* interval_start_time. If that's so, then ignore the softlimit-related
* values. */
if (state->AccountingSoftLimitHitAt > interval_start_time) {
soft_limit_hit_at = state->AccountingSoftLimitHitAt;
n_bytes_at_soft_limit = state->AccountingBytesAtSoftLimit;
n_seconds_to_hit_soft_limit = state->AccountingSecondsToReachSoftLimit;
} else {
soft_limit_hit_at = 0;
n_bytes_at_soft_limit = 0;
n_seconds_to_hit_soft_limit = 0;
}
{ {
char tbuf1[ISO_TIME_LEN+1]; char tbuf1[ISO_TIME_LEN+1];
char tbuf2[ISO_TIME_LEN+1]; char tbuf2[ISO_TIME_LEN+1];
...@@ -641,8 +685,27 @@ hibernate_hard_limit_reached(void) ...@@ -641,8 +685,27 @@ hibernate_hard_limit_reached(void)
static int static int
hibernate_soft_limit_reached(void) hibernate_soft_limit_reached(void)
{ {
uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(get_options()->AccountingMax) const uint64_t acct_max = get_options()->AccountingMax;
* .95); #define SOFT_LIM_PCT (.95)
#define SOFT_LIM_BYTES (500*1024*1024)
#define SOFT_LIM_MINUTES (3*60)
/* The 'soft limit' is a fair bit more complicated now than once it was.
* We want to stop accepting connections when ALL of the following are true:
* - We expect to use up the remaining bytes in under 3 hours
* - We have used up 95% of our bytes.
* - We have less than 500MB of bytes left.
*/
uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(acct_max) * SOFT_LIM_PCT);
if (acct_max > SOFT_LIM_BYTES && acct_max - SOFT_LIM_BYTES > soft_limit) {
soft_limit = acct_max - SOFT_LIM_BYTES;
}
if (expected_bandwidth_usage) {
const uint64_t expected_usage =
expected_bandwidth_usage * SOFT_LIM_MINUTES;
if (acct_max > expected_usage && acct_max - expected_usage > soft_limit)
soft_limit = acct_max - expected_usage;
}
if (!soft_limit) if (!soft_limit)
return 0; return 0;
return n_bytes_read_in_interval >= soft_limit return n_bytes_read_in_interval >= soft_limit
...@@ -667,6 +730,14 @@ hibernate_begin(hibernate_state_t new_state, time_t now) ...@@ -667,6 +730,14 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
exit(0); exit(0);
} }
if (new_state == HIBERNATE_STATE_LOWBANDWIDTH &&
hibernate_state == HIBERNATE_STATE_LIVE) {
soft_limit_hit_at = now;
n_seconds_to_hit_soft_limit = n_seconds_active_in_interval;
n_bytes_at_soft_limit = MAX(n_bytes_read_in_interval,
n_bytes_written_in_interval);
}
/* close listeners. leave control listener(s). */ /* close listeners. leave control listener(s). */
while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
......
...@@ -2831,6 +2831,9 @@ typedef struct { ...@@ -2831,6 +2831,9 @@ typedef struct {
uint64_t AccountingBytesReadInInterval; uint64_t AccountingBytesReadInInterval;
uint64_t AccountingBytesWrittenInInterval; uint64_t AccountingBytesWrittenInInterval;
int AccountingSecondsActive; int AccountingSecondsActive;
int AccountingSecondsToReachSoftLimit;
time_t AccountingSoftLimitHitAt;
uint64_t AccountingBytesAtSoftLimit;
uint64_t AccountingExpectedUsage; uint64_t AccountingExpectedUsage;
/** A list of Entry Guard-related configuration lines. */ /** A list of Entry Guard-related configuration lines. */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment