Merge remote-tracking branch 'github/eliminate_gettimeofday_cached'

This commit is contained in:
Nick Mathewson 2018-05-01 13:27:02 -04:00
commit 5c5392fea7
13 changed files with 200 additions and 72 deletions

6
changes/ticket25927.1 Normal file
View File

@ -0,0 +1,6 @@
o Minor features (timekeeping, circuit scheduling):
- When keeping track of how busy each circuit have been recently on
a given connection, use coarse-grained monotonic timers rather than
gettimeofday(). This change should marginally increase accuracy
and performance. Implements part of ticket 25927.

5
changes/ticket25927.2 Normal file
View File

@ -0,0 +1,5 @@
o Code simplification and refactoring:
- Remove our previous logic for "cached gettimeofday()" -- our coarse
monotonic timers are fast enough for this purpose, and far less
error-prone. Implements part of ticket 25927.

View File

@ -494,51 +494,7 @@ tor_libevent_exit_loop_after_callback(struct event_base *base)
event_base_loopbreak(base);
}
#if defined(LIBEVENT_VERSION_NUMBER) && \
LIBEVENT_VERSION_NUMBER >= V(2,1,1) && \
!defined(TOR_UNIT_TESTS)
void
tor_gettimeofday_cached(struct timeval *tv)
{
event_base_gettimeofday_cached(the_event_base, tv);
}
void
tor_gettimeofday_cache_clear(void)
{
event_base_update_cache_time(the_event_base);
}
#else /* !(defined(LIBEVENT_VERSION_NUMBER) && ...) */
/** Cache the current hi-res time; the cache gets reset when libevent
* calls us. */
static struct timeval cached_time_hires = {0, 0};
/** Return a fairly recent view of the current time. */
void
tor_gettimeofday_cached(struct timeval *tv)
{
if (cached_time_hires.tv_sec == 0) {
tor_gettimeofday(&cached_time_hires);
}
*tv = cached_time_hires;
}
/** Reset the cached view of the current time, so that the next time we try
* to learn it, we will get an up-to-date value. */
void
tor_gettimeofday_cache_clear(void)
{
cached_time_hires.tv_sec = 0;
}
#ifdef TOR_UNIT_TESTS
/** For testing: force-update the cached time to a given value. */
void
tor_gettimeofday_cache_set(const struct timeval *tv)
{
tor_assert(tv);
memcpy(&cached_time_hires, tv, sizeof(*tv));
}
#if defined(TOR_UNIT_TESTS)
/** For testing: called post-fork to make libevent reinitialize
* kernel structures. */
void
@ -548,5 +504,4 @@ tor_libevent_postfork(void)
tor_assert(r == 0);
}
#endif /* defined(TOR_UNIT_TESTS) */
#endif /* defined(LIBEVENT_VERSION_NUMBER) && ... */

View File

@ -68,10 +68,7 @@ void tor_libevent_free_all(void);
int tor_init_libevent_rng(void);
void tor_gettimeofday_cached(struct timeval *tv);
void tor_gettimeofday_cache_clear(void);
#ifdef TOR_UNIT_TESTS
void tor_gettimeofday_cache_set(const struct timeval *tv);
void tor_libevent_postfork(void);
#endif

View File

@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void)
* nanoseconds.
*/
static struct mach_timebase_info mach_time_info;
static struct mach_timebase_info mach_time_info_msec_cvt;
static int monotime_shift = 0;
static void
@ -296,6 +297,14 @@ monotime_init_internal(void)
// requires that tor_log2(0) == 0.
monotime_shift = tor_log2(ms_per_tick);
}
{
// For converting ticks to milliseconds in a 32-bit-friendly way, we
// will first right-shift by 20, and then multiply by 20/19, since
// (1<<20) * 19/20 is about 1e6. We precompute a new numerate and
// denominator here to avoid multiple multiplies.
mach_time_info_msec_cvt.numer = mach_time_info.numer * 20;
mach_time_info_msec_cvt.denom = mach_time_info.denom * 19;
}
}
/**
@ -345,6 +354,22 @@ monotime_diff_nsec(const monotime_t *start,
return diff_nsec;
}
int32_t
monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
if (BUG(mach_time_info.denom == 0)) {
monotime_init();
}
const int64_t diff_ticks = end->abstime_ - start->abstime_;
/* We already require in di_ops.c that right-shift performs a sign-extend. */
const int32_t diff_microticks = (int32_t)(diff_ticks >> 20);
return (diff_microticks * mach_time_info_msec_cvt.numer) /
mach_time_info_msec_cvt.denom;
}
uint32_t
monotime_coarse_to_stamp(const monotime_coarse_t *t)
{
@ -443,6 +468,15 @@ monotime_diff_nsec(const monotime_t *start,
return diff_nsec;
}
int32_t
monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
const int32_t diff_sec = (int32_t)(end->ts_.tv_sec - start->ts_.tv_sec);
const int32_t diff_nsec = (int32_t)(end->ts_.tv_nsec - start->ts_.tv_nsec);
return diff_sec * 1000 + diff_nsec / ONE_MILLION;
}
/* This value is ONE_BILLION >> 20. */
static const uint32_t STAMP_TICKS_PER_SECOND = 953;
@ -592,6 +626,13 @@ monotime_coarse_diff_msec(const monotime_coarse_t *start,
return diff_ticks;
}
int32_t
monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
return (int32_t)monotime_coarse_diff_msec(start, end)
}
int64_t
monotime_coarse_diff_usec(const monotime_coarse_t *start,
const monotime_coarse_t *end)
@ -677,6 +718,15 @@ monotime_diff_nsec(const monotime_t *start,
return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000);
}
int32_t
monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
struct timeval diff;
timersub(&end->tv_, &start->tv_, &diff);
return diff.tv_sec * 1000 + diff.tv_usec / 1000;
}
/* This value is ONE_MILLION >> 10. */
static const uint32_t STAMP_TICKS_PER_SECOND = 976;

View File

@ -173,6 +173,33 @@ void monotime_coarse_add_msec(monotime_coarse_t *out,
#define monotime_coarse_add_msec monotime_add_msec
#endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
/**
* As monotime_coarse_diff_msec, but avoid 64-bit division.
*
* Requires that the difference fit into an int32_t; not for use with
* large time differences.
*/
int32_t monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
const monotime_coarse_t *end);
/**
* As monotime_coarse_diff_msec, but avoid 64-bit division if it is expensive.
*
* Requires that the difference fit into an int32_t; not for use with
* large time differences.
*/
static inline int32_t
monotime_coarse_diff_msec32(const monotime_coarse_t *start,
const monotime_coarse_t *end)
{
#if SIZEOF_VOID_P == 8
// on a 64-bit platform, let's assume 64/64 division is cheap.
return (int32_t) monotime_coarse_diff_msec(start, end);
#else
return monotime_coarse_diff_msec32_(start, end);
#endif
}
MOCK_DECL(void, tor_gettimeofday, (struct timeval *timeval));
#ifdef TOR_UNIT_TESTS

View File

@ -28,7 +28,7 @@
*
**/
#define TOR_CIRCUITMUX_EWMA_C_
#define CIRCUITMUX_EWMA_PRIVATE
#include "orconfig.h"
@ -169,8 +169,6 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol)
static void add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma);
static int compare_cell_ewma_counts(const void *p1, const void *p2);
static unsigned cell_ewma_tick_from_timeval(const struct timeval *now,
double *remainder_out);
static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma);
static inline double get_scale_factor(unsigned from_tick, unsigned to_tick);
static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol);
@ -239,6 +237,13 @@ circuitmux_policy_t ewma_policy = {
/*.cmp_cmux =*/ ewma_cmp_cmux
};
/** Have we initialized the ewma tick-counting logic? */
static int ewma_ticks_initialized = 0;
/** At what monotime_coarse_t did the current tick begin? */
static monotime_coarse_t start_of_current_tick;
/** What is the number of the current tick? */
static unsigned current_tick_num;
/*** EWMA method implementations using the below EWMA helper functions ***/
/** Compute and return the current cell_ewma tick. */
@ -421,8 +426,6 @@ ewma_notify_xmit_cells(circuitmux_t *cmux,
ewma_policy_circ_data_t *cdata = NULL;
unsigned int tick;
double fractional_tick, ewma_increment;
/* The current (hi-res) time */
struct timeval now_hires;
cell_ewma_t *cell_ewma, *tmp;
tor_assert(cmux);
@ -435,8 +438,7 @@ ewma_notify_xmit_cells(circuitmux_t *cmux,
cdata = TO_EWMA_POL_CIRC_DATA(pol_circ_data);
/* Rescale the EWMAs if needed */
tor_gettimeofday_cached(&now_hires);
tick = cell_ewma_tick_from_timeval(&now_hires, &fractional_tick);
tick = cell_ewma_get_current_tick_and_fraction(&fractional_tick);
if (tick != pol->active_circuit_pqueue_last_recalibrated) {
scale_active_circuits(pol, tick);
@ -597,24 +599,45 @@ cell_ewma_to_circuit(cell_ewma_t *ewma)
rescale.
*/
/** Given a timeval <b>now</b>, compute the cell_ewma tick in which it occurs
* and the fraction of the tick that has elapsed between the start of the tick
* and <b>now</b>. Return the former and store the latter in
* *<b>remainder_out</b>.
/**
* Initialize the system that tells which ewma tick we are in.
*/
STATIC void
cell_ewma_initialize_ticks(void)
{
if (ewma_ticks_initialized)
return;
monotime_coarse_get(&start_of_current_tick);
crypto_rand((char*)&current_tick_num, sizeof(current_tick_num));
ewma_ticks_initialized = 1;
}
/** Compute the current cell_ewma tick and the fraction of the tick that has
* elapsed between the start of the tick and the current time. Return the
* former and store the latter in *<b>remainder_out</b>.
*
* These tick values are not meant to be shared between Tor instances, or used
* for other purposes. */
static unsigned
cell_ewma_tick_from_timeval(const struct timeval *now,
double *remainder_out)
STATIC unsigned
cell_ewma_get_current_tick_and_fraction(double *remainder_out)
{
unsigned res = (unsigned) (now->tv_sec / EWMA_TICK_LEN);
/* rem */
double rem = (now->tv_sec % EWMA_TICK_LEN) +
((double)(now->tv_usec)) / 1.0e6;
*remainder_out = rem / EWMA_TICK_LEN;
return res;
if (BUG(!ewma_ticks_initialized)) {
cell_ewma_initialize_ticks(); // LCOV_EXCL_LINE
}
monotime_coarse_t now;
monotime_coarse_get(&now);
int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
&now);
if (msec_diff > (1000*EWMA_TICK_LEN)) {
unsigned ticks_difference = msec_diff / (1000*EWMA_TICK_LEN);
monotime_coarse_add_msec(&start_of_current_tick,
&start_of_current_tick,
ticks_difference * 1000 * EWMA_TICK_LEN);
current_tick_num += ticks_difference;
msec_diff %= 1000*EWMA_TICK_LEN;
}
*remainder_out = ((double)msec_diff) / (1.0e3 * EWMA_TICK_LEN);
return current_tick_num;
}
/* Default value for the CircuitPriorityHalflifeMsec consensus parameter in
@ -678,6 +701,8 @@ cmux_ewma_set_options(const or_options_t *options,
double halflife;
const char *source;
cell_ewma_initialize_ticks();
/* Both options and consensus can be NULL. This assures us to either get a
* valid configured value or the default one. */
halflife = get_circuit_priority_halflife(options, consensus, &source);
@ -788,3 +813,12 @@ pop_first_cell_ewma(ewma_policy_data_t *pol)
offsetof(cell_ewma_t, heap_index));
}
/**
* Drop all resources held by circuitmux_ewma.c, and deinitialize the
* module. */
void
circuitmux_ewma_free_all(void)
{
ewma_ticks_initialized = 0;
}

View File

@ -19,5 +19,12 @@ extern circuitmux_policy_t ewma_policy;
void cmux_ewma_set_options(const or_options_t *options,
const networkstatus_t *consensus);
void circuitmux_ewma_free_all(void);
#ifdef CIRCUITMUX_EWMA_PRIVATE
STATIC unsigned cell_ewma_get_current_tick_and_fraction(double *remainder_out);
STATIC void cell_ewma_initialize_ticks(void);
#endif
#endif /* !defined(TOR_CIRCUITMUX_EWMA_H) */

View File

@ -3440,7 +3440,6 @@ connection_handle_read(connection_t *conn)
{
int res;
tor_gettimeofday_cache_clear();
res = connection_handle_read_impl(conn);
return res;
}
@ -3983,7 +3982,6 @@ int
connection_handle_write(connection_t *conn, int force)
{
int res;
tor_gettimeofday_cache_clear();
conn->in_connection_handle_write = 1;
res = connection_handle_write_impl(conn, force);
conn->in_connection_handle_write = 0;

View File

@ -59,6 +59,7 @@
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "circuitmux_ewma.h"
#include "command.h"
#include "compress.h"
#include "config.h"
@ -3590,6 +3591,7 @@ tor_free_all(int postfork)
consdiffmgr_free_all();
hs_free_all();
dos_free_all();
circuitmux_ewma_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();

View File

@ -544,6 +544,8 @@ test_channel_outbound_cell(void *arg)
(void) arg;
cmux_ewma_set_options(NULL,NULL);
/* The channel will be freed so we need to hijack this so the scheduler
* doesn't get confused. */
MOCK(scheduler_release_channel, scheduler_release_channel_mock);

View File

@ -3,6 +3,7 @@
#define TOR_CHANNEL_INTERNAL_
#define CIRCUITMUX_PRIVATE
#define CIRCUITMUX_EWMA_PRIVATE
#define RELAY_PRIVATE
#include "or.h"
#include "channel.h"
@ -79,8 +80,47 @@ test_cmux_destroy_cell_queue(void *arg)
tor_free(dc);
}
static void
test_cmux_compute_ticks(void *arg)
{
const int64_t NS_PER_S = 1000 * 1000 * 1000;
const int64_t START_NS = U64_LITERAL(1217709000)*NS_PER_S;
int64_t now;
double rem;
unsigned tick;
(void)arg;
circuitmux_ewma_free_all();
monotime_enable_test_mocking();
monotime_coarse_set_mock_time_nsec(START_NS);
cell_ewma_initialize_ticks();
const unsigned tick_zero = cell_ewma_get_current_tick_and_fraction(&rem);
tt_double_op(rem, OP_GT, -1e-9);
tt_double_op(rem, OP_LT, 1e-9);
/* 1.5 second later and we should still be in the same tick. */
now = START_NS + NS_PER_S + NS_PER_S/2;
monotime_coarse_set_mock_time_nsec(now);
tick = cell_ewma_get_current_tick_and_fraction(&rem);
tt_uint_op(tick, OP_EQ, tick_zero);
tt_double_op(rem, OP_GT, .149999999);
tt_double_op(rem, OP_LT, .150000001);
/* 25 second later and we should be in another tick. */
now = START_NS + NS_PER_S * 25;
monotime_coarse_set_mock_time_nsec(now);
tick = cell_ewma_get_current_tick_and_fraction(&rem);
tt_uint_op(tick, OP_EQ, tick_zero + 2);
tt_double_op(rem, OP_GT, .499999999);
tt_double_op(rem, OP_LT, .500000001);
done:
;
}
struct testcase_t circuitmux_tests[] = {
{ "destroy_cell_queue", test_cmux_destroy_cell_queue, TT_FORK, NULL, NULL },
{ "compute_ticks", test_cmux_compute_ticks, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};

View File

@ -6035,6 +6035,9 @@ test_util_monotonic_time_add_msec(void *arg)
monotime_coarse_add_msec(&ct2, &ct1, 1337);
tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337);
tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337);
// The 32-bit variant must be within 1% of the regular one.
tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 1323);
tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 1350);
/* Add 1337 msec twice more; make sure that any second rollover issues
* worked. */
@ -6044,6 +6047,8 @@ test_util_monotonic_time_add_msec(void *arg)
monotime_coarse_add_msec(&ct2, &ct2, 1337);
tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3);
tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3);
tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 3970);
tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 4051);
done:
;