diff --git a/changes/1863_bwhist b/changes/1863_bwhist
index b9e8ef4c52..b94250906b 100644
--- a/changes/1863_bwhist
+++ b/changes/1863_bwhist
@@ -1,3 +1,10 @@
+ o Minor features
+ - Servers now save observed maximum bandwidth throughput rates
+ to their state file (along with total usage, which was already
+ saved) so that they can determine their correct estimated
+ bandwidth on restart. Resolves bug 1863, where Tor servers
+ would reset their estimated bandwidth to 0 after restarting.
+
o Minor bugfixes
- Fix a bug in banwidth history state parsing that could have been
triggered if a future version of Tor ever changed the timing
@@ -7,4 +14,3 @@
error parsing any bw history value from the state file. Bugfix on
Tor 0.2.2.15-alpha.
-
diff --git a/src/or/config.c b/src/or/config.c
index 17d776e71e..78978dc08c 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -443,15 +443,19 @@ static config_var_t _state_vars[] = {
V(BWHistoryReadEnds, ISOTIME, NULL),
V(BWHistoryReadInterval, UINT, "900"),
V(BWHistoryReadValues, CSV, ""),
+ V(BWHistoryReadMaxima, CSV, ""),
V(BWHistoryWriteEnds, ISOTIME, NULL),
V(BWHistoryWriteInterval, UINT, "900"),
V(BWHistoryWriteValues, CSV, ""),
+ V(BWHistoryWriteMaxima, CSV, ""),
V(BWHistoryDirReadEnds, ISOTIME, NULL),
V(BWHistoryDirReadInterval, UINT, "900"),
V(BWHistoryDirReadValues, CSV, ""),
+ V(BWHistoryDirReadMaxima, CSV, ""),
V(BWHistoryDirWriteEnds, ISOTIME, NULL),
V(BWHistoryDirWriteInterval, UINT, "900"),
V(BWHistoryDirWriteValues, CSV, ""),
+ V(BWHistoryDirWriteMaxima, CSV, ""),
V(TorVersion, STRING, NULL),
diff --git a/src/or/or.h b/src/or/or.h
index cb36126d99..e0f8babe70 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2856,19 +2856,25 @@ typedef struct {
* bandwidth usage. The "Interval" fields hold the granularity, in seconds,
* of the entries of Values. The "Values" lists hold decimal string
* representations of the number of bytes read or written in each
- * interval. */
+ * interval. The "Maxima" list holds decimal strings describing the highest
+ * rate achieved during the interval.
+ */
time_t BWHistoryReadEnds;
int BWHistoryReadInterval;
smartlist_t *BWHistoryReadValues;
+ smartlist_t *BWHistoryReadMaxima;
time_t BWHistoryWriteEnds;
int BWHistoryWriteInterval;
smartlist_t *BWHistoryWriteValues;
+ smartlist_t *BWHistoryWriteMaxima;
time_t BWHistoryDirReadEnds;
int BWHistoryDirReadInterval;
smartlist_t *BWHistoryDirReadValues;
+ smartlist_t *BWHistoryDirReadMaxima;
time_t BWHistoryDirWriteEnds;
int BWHistoryDirWriteInterval;
smartlist_t *BWHistoryDirWriteValues;
+ smartlist_t *BWHistoryDirWriteMaxima;
/** Build time histogram */
config_line_t * BuildtimeHistogram;
diff --git a/src/or/rephist.c b/src/or/rephist.c
index f0dd45128c..a3a2bab95d 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1261,8 +1261,12 @@ add_obs(bw_array_t *b, time_t when, uint64_t n)
/* If we're currently adding observations for an earlier second than
* 'when', advance b->cur_obs_time and b->cur_obs_idx by an
* appropriate number of seconds, and do all the other housekeeping */
- while (when>b->cur_obs_time)
+ while (when>b->cur_obs_time) {
+ /* Doing this one second at a time is potentially inefficient, if we start
+ with a state file that is very old. Fortunately, it doesn't seem to
+ show up in profiles, so we can just ignore it for now. */
advance_obs(b);
+ }
b->obs[b->cur_obs_idx] += n;
b->total_in_period += n;
@@ -1497,15 +1501,21 @@ static void
rep_hist_update_bwhist_state_section(or_state_t *state,
const bw_array_t *b,
smartlist_t **s_values,
+ smartlist_t **s_maxima,
time_t *s_begins,
int *s_interval)
{
- char buf[20*NUM_TOTALS + 1], *cp;
+ char *cp;
+ int i,j;
if (*s_values) {
SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
smartlist_free(*s_values);
}
+ if (*s_maxima) {
+ SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val));
+ smartlist_free(*s_maxima);
+ }
if (! server_mode(get_options())) {
/* Clients don't need to store bandwidth history persistently;
* force these values to the defaults. */
@@ -1519,18 +1529,30 @@ rep_hist_update_bwhist_state_section(or_state_t *state,
*s_begins = 0;
*s_interval = 900;
*s_values = smartlist_create();
+ *s_maxima = smartlist_create();
return;
}
*s_begins = b->next_period;
*s_interval = NUM_SECS_BW_SUM_INTERVAL;
- cp = buf;
- cp += rep_hist_fill_bandwidth_history(cp, sizeof(buf), b);
- tor_snprintf(cp, sizeof(buf)-(cp-buf),
- cp == buf ? U64_FORMAT : ","U64_FORMAT,
- U64_PRINTF_ARG(b->total_in_period));
+
*s_values = smartlist_create();
- if (server_mode(get_options()))
- smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0);
+ *s_maxima = smartlist_create();
+ /* Set i to first position in circular array */
+ i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx;
+ for (j=0; j < b->num_maxes_set; ++j,++i) {
+ uint64_t maxval;
+ if (i > NUM_TOTALS)
+ i = 0;
+ tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->totals[i] & ~0x3ff));
+ smartlist_add(*s_values, cp);
+ maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE;
+ tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(maxval & ~0x3ff));
+ smartlist_add(*s_maxima, cp);
+ }
+ tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->total_in_period & ~0x3ff));
+ smartlist_add(*s_values, cp);
+ tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->max_total & ~0x3ff));
+ smartlist_add(*s_maxima, cp);
}
/** Update state with the newest bandwidth history. */
@@ -1541,6 +1563,7 @@ rep_hist_update_state(or_state_t *state)
rep_hist_update_bwhist_state_section(state,\
(arrname),\
&state->BWHistory ## st ## Values, \
+ &state->BWHistory ## st ## Maxima, \
&state->BWHistory ## st ## Ends, \
&state->BWHistory ## st ## Interval)
@@ -1560,6 +1583,7 @@ rep_hist_update_state(or_state_t *state)
static int
rep_hist_load_bwhist_state_section(bw_array_t *b,
const smartlist_t *s_values,
+ const smartlist_t *s_maxima,
const time_t s_begins,
const int s_interval)
{
@@ -1567,8 +1591,9 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
int retval = 0;
time_t start;
- uint64_t v;
- int i,ok;
+ uint64_t v, mv;
+ int i,ok,ok_m;
+ int have_maxima = (smartlist_len(s_values) == smartlist_len(s_maxima));
if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
start = s_begins - s_interval*(smartlist_len(s_values));
@@ -1578,12 +1603,22 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) {
v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
- if (!ok) {
+ if (have_maxima) {
+ const char *maxstr = smartlist_get(s_maxima, cp_sl_idx);
+ mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL);
+ mv *= NUM_SECS_ROLLING_MEASURE;
+ } else {
+ /* No maxima known; guess average rate to be conservative. */
+ mv = v / s_interval;
+ }
+ if (!ok || !ok_m) {
retval = -1;
log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp);
}
+
if (start < now) {
add_obs(b, start, v);
+ b->max_total = mv;
/* This will result in some fairly choppy history if s_interval
* is notthe same as NUM_SECS_BW_SUM_INTERVAL. XXXX */
start += s_interval;
@@ -1592,15 +1627,10 @@ rep_hist_load_bwhist_state_section(bw_array_t *b,
}
/* Clean up maxima and observed */
- /* Do we really want to zero this for the purpose of max capacity? */
for (i=0; iobs[i] = 0;
}
b->total_obs = 0;
- for (i=0; imaxima[i] = 0;
- }
- b->max_total = 0;
return retval;
}
@@ -1619,6 +1649,7 @@ rep_hist_load_state(or_state_t *state, char **err)
if (rep_hist_load_bwhist_state_section( \
(arrname), \
state->BWHistory ## st ## Values, \
+ state->BWHistory ## st ## Maxima, \
state->BWHistory ## st ## Ends, \
state->BWHistory ## st ## Interval)<0) \
all_ok = 0