diff --git a/changes/bug6538 b/changes/bug6538 new file mode 100644 index 0000000000000000000000000000000000000000..1e882eb1cce11aa49e4ff1b9ec9ed58df6686b77 --- /dev/null +++ b/changes/bug6538 @@ -0,0 +1,4 @@ + o Minor bugfixes: + - Switch weighted node selection rule from using a list of doubles + to using a list of int64_t. This should make the process slightly + easier to debug and maintain. Needed for fix for bug 6538. diff --git a/configure.in b/configure.in index 1db7d08e20b13d47eb183fd29f7a39f0f0fd53bb..f5f8eacb7e605d31601391d8c5258c0f86907653 100644 --- a/configure.in +++ b/configure.in @@ -300,6 +300,7 @@ AC_CHECK_FUNCS( gmtime_r \ inet_aton \ ioctl \ + llround \ localtime_r \ lround \ memmem \ diff --git a/src/common/util.c b/src/common/util.c index a0dff2e35b698eba4dc0ef0836bb9ec807ef497e..60222f40839cbf04c6c8069acc1ea60cd4011262 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -378,6 +378,21 @@ tor_lround(double d) #endif } +/** Return the 64-bit integer closest to d. We define this wrapper here so + * that not all users of math.h need to use the right incancations to get the + * c99 functions. */ +int64_t +tor_llround(double d) +{ +#if defined(HAVE_LLROUND) + return (int64_t)llround(d); +#elif defined(HAVE_RINT) + return (int64_t)rint(d); +#else + return (int64_t)(d > 0 ? d + 0.5 : ceil(d - 0.5)); +#endif +} + /** Returns floor(log2(u64)). If u64 is 0, (incorrectly) returns 0. */ int tor_log2(uint64_t u64) diff --git a/src/common/util.h b/src/common/util.h index a2ab0ccac873d8c6a616580455fad729be3441aa..f700e9f4cbd6905f4826b93203f9ae2f2f367594 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -161,6 +161,7 @@ void tor_log_mallinfo(int severity); /* Math functions */ double tor_mathlog(double d) ATTR_CONST; long tor_lround(double d) ATTR_CONST; +int64_t tor_llround(double d) ATTR_CONST; int tor_log2(uint64_t u64) ATTR_CONST; uint64_t round_to_power_of_2(uint64_t u64); unsigned round_to_next_multiple_of(unsigned number, unsigned divisor); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 4979b933ade100dd55a2acc6e0ace6a125cd4c2c..382d45746c0b4b3a122bba371855b43129a13ae0 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -1702,12 +1702,12 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl, bandwidth_weight_rule_t rule) { int64_t weight_scale; - int64_t rand_bw; + uint64_t rand_bw; double Wg = -1, Wm = -1, We = -1, Wd = -1; double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1; - double weighted_bw = 0, unweighted_bw = 0; - double *bandwidths; - double tmp = 0; + uint64_t weighted_bw = 0, unweighted_bw = 0; + uint64_t *bandwidths; + uint64_t tmp; unsigned int i; unsigned int i_chosen; unsigned int i_has_been_chosen; @@ -1792,7 +1792,7 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl, Web /= weight_scale; Wdb /= weight_scale; - bandwidths = tor_malloc_zero(sizeof(double)*smartlist_len(sl)); + bandwidths = tor_malloc_zero(sizeof(uint64_t)*smartlist_len(sl)); // Cycle through smartlist and total the bandwidth. SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { @@ -1831,24 +1831,30 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl, } else { // middle weight = (is_dir ? Wmb*Wm : Wm); } - - bandwidths[node_sl_idx] = weight*this_bw; - weighted_bw += weight*this_bw; + /* These should be impossible; but overflows here would be bad, so let's + * make sure. */ + if (this_bw < 0) + this_bw = 0; + if (weight < 0.0) + weight = 0.0; + + bandwidths[node_sl_idx] = tor_llround(weight*this_bw + 0.5); + weighted_bw += bandwidths[node_sl_idx]; unweighted_bw += this_bw; if (is_me) - sl_last_weighted_bw_of_me = weight*this_bw; + sl_last_weighted_bw_of_me = bandwidths[node_sl_idx]; } SMARTLIST_FOREACH_END(node); /* XXXX this is a kludge to expose these values. */ sl_last_total_weighted_bw = weighted_bw; log_debug(LD_CIRC, "Choosing node for rule %s based on weights " - "Wg=%f Wm=%f We=%f Wd=%f with total bw %f", + "Wg=%f Wm=%f We=%f Wd=%f with total bw "U64_FORMAT, bandwidth_weight_rule_to_string(rule), - Wg, Wm, We, Wd, weighted_bw); + Wg, Wm, We, Wd, U64_PRINTF_ARG(weighted_bw)); /* If there is no bandwidth, choose at random */ - if (DBL_TO_U64(weighted_bw) == 0) { + if (weighted_bw == 0) { /* Don't warn when using bridges/relays not in the consensus */ if (!have_unknown) { #define ZERO_BANDWIDTH_WARNING_INTERVAL (15) @@ -1858,24 +1864,25 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl, if ((msg = rate_limit_log(&zero_bandwidth_warning_limit, approx_time()))) { log_warn(LD_CIRC, - "Weighted bandwidth is %f in node selection for rule %s " - "(unweighted was %f) %s", - weighted_bw, bandwidth_weight_rule_to_string(rule), - unweighted_bw, msg); + "Weighted bandwidth is "U64_FORMAT" in node selection for " + "rule %s (unweighted was "U64_FORMAT") %s", + U64_PRINTF_ARG(weighted_bw), + bandwidth_weight_rule_to_string(rule), + U64_PRINTF_ARG(unweighted_bw), msg); } } tor_free(bandwidths); return smartlist_choose(sl); } - rand_bw = crypto_rand_uint64(DBL_TO_U64(weighted_bw)); + rand_bw = crypto_rand_uint64(weighted_bw); rand_bw++; /* crypto_rand_uint64() counts from 0, and we need to count * from 1 below. See bug 1203 for details. */ /* Last, count through sl until we get to the element we picked */ i_chosen = (unsigned)smartlist_len(sl); i_has_been_chosen = 0; - tmp = 0.0; + tmp = 0; for (i=0; i < (unsigned)smartlist_len(sl); i++) { tmp += bandwidths[i]; if (tmp >= rand_bw && !i_has_been_chosen) { @@ -1892,8 +1899,9 @@ smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl, --i; log_warn(LD_BUG, "Round-off error in computing bandwidth had an effect on " " which router we chose. Please tell the developers. " - "%f " U64_FORMAT " %f", tmp, U64_PRINTF_ARG(rand_bw), - weighted_bw); + U64_FORMAT" "U64_FORMAT" "U64_FORMAT, + U64_PRINTF_ARG(tmp), U64_PRINTF_ARG(rand_bw), + U64_PRINTF_ARG(weighted_bw)); } tor_free(bandwidths); return smartlist_get(sl, i);