Commit e476ffc2 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Merge branch 'bug1789'

parents fb34c66e 9cba61eb
Loading
Loading
Loading
Loading

changes/bug1789

0 → 100644
+16 −0
Original line number Diff line number Diff line
  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.
+3 −0
Original line number Diff line number Diff line
@@ -428,6 +428,9 @@ static config_var_t _state_vars[] = {
  V(AccountingExpectedUsage,          MEMUNIT,  NULL),
  V(AccountingIntervalStart,          ISOTIME,  NULL),
  V(AccountingSecondsActive,          INTERVAL, NULL),
  V(AccountingSecondsToReachSoftLimit,INTERVAL, NULL),
  V(AccountingSoftLimitHitAt,         ISOTIME,  NULL),
  V(AccountingBytesAtSoftLimit,       MEMUNIT,  NULL),

  VAR("EntryGuard",              LINELIST_S,  EntryGuards,             NULL),
  VAR("EntryGuardDownSince",     LINELIST_S,  EntryGuards,             NULL),
+87 −16
Original line number Diff line number Diff line
@@ -95,6 +95,13 @@ static uint64_t n_bytes_read_in_interval = 0;
static uint64_t n_bytes_written_in_interval = 0;
/** How many seconds have we been running this interval? */
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? */
static time_t interval_start_time = 0;
/** When will this accounting interval end? */
@@ -374,23 +381,42 @@ configure_accounting(time_t now)
static void
update_expected_bandwidth(void)
{
  uint64_t used, expected;
  uint64_t max_configured = (get_options()->BandwidthRate * 60);

  if (n_seconds_active_in_interval < 1800) {
  uint64_t expected;
  or_options_t *options= get_options();
  uint64_t max_configured = (options->RelayBandwidthRate > 0 ?
                             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'
     * 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
     * we sent this interval.
     */
    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;
  }
  expected_bandwidth_usage = expected;
}

@@ -408,6 +434,9 @@ reset_accounting(time_t now)
  n_bytes_read_in_interval = 0;
  n_bytes_written_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. */
@@ -568,6 +597,10 @@ accounting_record_bandwidth_usage(time_t now, or_state_t *state)
  state->AccountingSecondsActive = n_seconds_active_in_interval;
  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,
                      now+(get_options()->AvoidDiskWrites ? 7200 : 60));

@@ -591,10 +624,6 @@ read_bandwidth_usage(void)
  if (!state)
    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");
  n_bytes_read_in_interval = state->AccountingBytesReadInInterval;
  n_bytes_written_in_interval = state->AccountingBytesWrittenInInterval;
@@ -602,6 +631,21 @@ read_bandwidth_usage(void)
  interval_start_time = state->AccountingIntervalStart;
  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 tbuf2[ISO_TIME_LEN+1];
@@ -641,8 +685,27 @@ hibernate_hard_limit_reached(void)
static int
hibernate_soft_limit_reached(void)
{
  uint64_t soft_limit = DBL_TO_U64(U64_TO_DBL(get_options()->AccountingMax)
                                   * .95);
  const uint64_t acct_max = get_options()->AccountingMax;
#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)
    return 0;
  return n_bytes_read_in_interval >= soft_limit
@@ -667,6 +730,14 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
    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). */
  while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) ||
         (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
+3 −0
Original line number Diff line number Diff line
@@ -2831,6 +2831,9 @@ typedef struct {
  uint64_t AccountingBytesReadInInterval;
  uint64_t AccountingBytesWrittenInInterval;
  int AccountingSecondsActive;
  int AccountingSecondsToReachSoftLimit;
  time_t AccountingSoftLimitHitAt;
  uint64_t AccountingBytesAtSoftLimit;
  uint64_t AccountingExpectedUsage;

  /** A list of Entry Guard-related configuration lines. */