Merge branch 'ticket26063_squashed'

This commit is contained in:
Nick Mathewson 2018-05-10 09:13:28 -04:00
commit 1eede00a4b
15 changed files with 208 additions and 51 deletions

5
changes/ticket26063 Normal file
View File

@ -0,0 +1,5 @@
o Major features (CPU usage, mobile):
- When Tor is disabled (via DisableNetwork or via hibernation), it
no longer needs to run any per-second events. This change should
make it easier for mobile applications to disable Tor while the
device is sleeping, or Tor is not running. Closes ticket 26063.

View File

@ -253,10 +253,39 @@ periodic_timer_new(struct event_base *base,
}
timer->cb = cb;
timer->data = data;
event_add(timer->ev, (struct timeval *)tv); /*drop const for old libevent*/
periodic_timer_launch(timer, tv);
return timer;
}
/**
* Launch the timer <b>timer</b> to run at <b>tv</b> from now, and every
* <b>tv</b> thereafter.
*
* If the timer is already enabled, this function does nothing.
*/
void
periodic_timer_launch(periodic_timer_t *timer, const struct timeval *tv)
{
tor_assert(timer);
if (event_pending(timer->ev, EV_TIMEOUT, NULL))
return;
event_add(timer->ev, tv);
}
/**
* Disable the provided <b>timer</b>, but do not free it.
*
* You can reenable the same timer later with periodic_timer_launch.
*
* If the timer is already disabled, this function does nothing.
*/
void
periodic_timer_disable(periodic_timer_t *timer)
{
tor_assert(timer);
event_del(timer->ev);
}
/** Stop and free a periodic timer */
void
periodic_timer_free_(periodic_timer_t *timer)

View File

@ -31,6 +31,8 @@ periodic_timer_t *periodic_timer_new(struct event_base *base,
void (*cb)(periodic_timer_t *timer, void *data),
void *data);
void periodic_timer_free_(periodic_timer_t *);
void periodic_timer_launch(periodic_timer_t *, const struct timeval *tv);
void periodic_timer_disable(periodic_timer_t *);
#define periodic_timer_free(t) \
FREE_AND_NULL(periodic_timer_t, periodic_timer_free_, (t))

View File

@ -1449,9 +1449,9 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to
* ports under 1024.) We don't want to rebind if we're hibernating. If
* networking is disabled, this will close all but the control listeners,
* but disable those. */
* ports under 1024.) We don't want to rebind if we're hibernating or
* shutting down. If networking is disabled, this will close all but the
* control listeners, but disable those. */
if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners,
options->DisableNetwork) < 0) {
@ -2001,6 +2001,9 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
/* See whether we need to enable/disable our once-a-second timer. */
reschedule_per_second_timer();
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||

View File

@ -1762,13 +1762,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_assert(sa);
tor_assert(socket_error);
if (get_options()->DisableNetwork) {
/* We should never even try to connect anyplace if DisableNetwork is set.
* Warn if we do, and refuse to make the connection.
if (net_is_completely_disabled()) {
/* We should never even try to connect anyplace if the network is
* completely shut off.
*
* We only check DisableNetwork here, not we_are_hibernating(), since
* we'll still try to fulfill client requests sometimes in the latter case
* (if it is soft hibernation) */
* (We don't check net_is_disabled() here, since we still sometimes
* want to open connections when we're in soft hibernation.)
*/
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
*socket_error = SOCK_ERRNO(ENETUNREACH);
log_fn_ratelim(&disablenet_violated, LOG_WARN, LD_BUG,

View File

@ -3537,6 +3537,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->base_.state = EXIT_CONN_STATE_RESOLVEFAILED;
/* default to failed, change in dns_resolve if it turns out not to fail */
/* If we're hibernating or shutting down, we refuse to open new streams. */
if (we_are_hibernating()) {
relay_send_end_cell_from_edge(rh.stream_id, circ,
END_STREAM_REASON_HIBERNATING, NULL);

View File

@ -1,3 +1,4 @@
/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
@ -112,6 +113,10 @@ static int disable_log_messages = 0;
#define EVENT_IS_INTERESTING(e) \
(!! (global_event_mask & EVENT_MASK_(e)))
/** Macro: true if any event from the bitfield 'e' is interesting. */
#define ANY_EVENT_IS_INTERESTING(e) \
EVENT_IS_INTERESTING(e)
/** If we're using cookie-type authentication, how long should our cookies be?
*/
#define AUTHENTICATION_COOKIE_LEN 32
@ -219,6 +224,7 @@ static void set_cached_network_liveness(int liveness);
static void flush_queued_events_cb(mainloop_event_t *event, void *arg);
static char * download_status_to_string(const download_status_t *dl);
static void control_get_bytes_rw_last_sec(uint64_t *r, uint64_t *w);
/** Given a control event code for a message event, return the corresponding
* log severity. */
@ -271,6 +277,7 @@ control_update_global_event_mask(void)
smartlist_t *conns = get_connection_array();
event_mask_t old_mask, new_mask;
old_mask = global_event_mask;
int any_old_per_sec_events = control_any_per_second_event_enabled();
global_event_mask = 0;
SMARTLIST_FOREACH(conns, connection_t *, _conn,
@ -288,10 +295,13 @@ control_update_global_event_mask(void)
* we want to hear...*/
control_adjust_event_log_severity();
/* Macro: true if ev was false before and is true now. */
#define NEWLY_ENABLED(ev) \
(! (old_mask & (ev)) && (new_mask & (ev)))
/* ...then, if we've started logging stream or circ bw, clear the
* appropriate fields. */
if (! (old_mask & EVENT_STREAM_BANDWIDTH_USED) &&
(new_mask & EVENT_STREAM_BANDWIDTH_USED)) {
if (NEWLY_ENABLED(EVENT_STREAM_BANDWIDTH_USED)) {
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
if (conn->type == CONN_TYPE_AP) {
@ -300,10 +310,18 @@ control_update_global_event_mask(void)
}
});
}
if (! (old_mask & EVENT_CIRC_BANDWIDTH_USED) &&
(new_mask & EVENT_CIRC_BANDWIDTH_USED)) {
if (NEWLY_ENABLED(EVENT_CIRC_BANDWIDTH_USED)) {
clear_circ_bw_fields();
}
if (NEWLY_ENABLED(EVENT_BANDWIDTH_USED)) {
uint64_t r, w;
control_get_bytes_rw_last_sec(&r, &w);
}
if (any_old_per_sec_events != control_any_per_second_event_enabled()) {
reschedule_per_second_timer();
}
#undef NEWLY_ENABLED
}
/** Adjust the log severities that result in control_event_logmsg being called
@ -352,6 +370,65 @@ control_event_is_interesting(int event)
return EVENT_IS_INTERESTING(event);
}
/** Return true if any event that needs to fire once a second is enabled. */
int
control_any_per_second_event_enabled(void)
{
return ANY_EVENT_IS_INTERESTING(
EVENT_BANDWIDTH_USED |
EVENT_CELL_STATS |
EVENT_CIRC_BANDWIDTH_USED |
EVENT_CONN_BW |
EVENT_STREAM_BANDWIDTH_USED
);
}
/* The value of 'get_bytes_read()' the previous time that
* control_get_bytes_rw_last_sec() as called. */
static uint64_t stats_prev_n_read = 0;
/* The value of 'get_bytes_written()' the previous time that
* control_get_bytes_rw_last_sec() as called. */
static uint64_t stats_prev_n_written = 0;
/**
* Set <b>n_read</b> and <b>n_written</b> to the total number of bytes read
* and written by Tor since the last call to this function.
*
* Call this only from the main thread.
*/
static void
control_get_bytes_rw_last_sec(uint64_t *n_read,
uint64_t *n_written)
{
const uint64_t stats_n_bytes_read = get_bytes_read();
const uint64_t stats_n_bytes_written = get_bytes_written();
*n_read = stats_n_bytes_read - stats_prev_n_read;
*n_written = stats_n_bytes_written - stats_prev_n_written;
stats_prev_n_read = stats_n_bytes_read;
stats_prev_n_written = stats_n_bytes_written;
}
/**
* Run all the controller events (if any) that are scheduled to trigger once
* per second.
*/
void
control_per_second_events(void)
{
if (!control_any_per_second_event_enabled())
return;
uint64_t bytes_read, bytes_written;
control_get_bytes_rw_last_sec(&bytes_read, &bytes_written);
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
control_event_conn_bandwidth_used();
control_event_circ_bandwidth_used();
control_event_circuit_cell_stats();
}
/** Append a NUL-terminated string <b>s</b> to the end of
* <b>conn</b>-\>outbuf.
*/
@ -7035,6 +7112,8 @@ control_event_bootstrap_problem(const char *warn, const char *reason,
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
dowarn = 1;
/* Don't warn about our bootstrapping status if we are hibernating or
* shutting down. */
if (we_are_hibernating())
dowarn = 0;
@ -7606,6 +7685,8 @@ control_free_all(void)
{
smartlist_t *queued_events = NULL;
stats_prev_n_read = stats_prev_n_written = 0;
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
if (detached_onion_services) { /* Free the detached onion services */

View File

@ -40,6 +40,9 @@ int connection_control_process_inbuf(control_connection_t *conn);
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
void control_per_second_events(void);
int control_any_per_second_event_enabled(void);
int control_event_circuit_status(origin_circuit_t *circ,
circuit_status_event_t e, int reason);
int control_event_circuit_purpose_changed(origin_circuit_t *circ,

View File

@ -955,7 +955,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
tor_assert(node);
if (router_is_me(router)) {
/* We always know if we are down ourselves. */
/* We always know if we are shutting down or hibernating ourselves. */
answer = ! we_are_hibernating();
} else if (router->is_hibernating &&
(router->cache_info.published_on +

View File

@ -883,13 +883,26 @@ hibernate_begin_shutdown(void)
hibernate_begin(HIBERNATE_STATE_EXITING, time(NULL));
}
/** Return true iff we are currently hibernating. */
/**
* Return true iff we are currently hibernating -- that is, if we are in
* any non-live state.
*/
MOCK_IMPL(int,
we_are_hibernating,(void))
{
return hibernate_state != HIBERNATE_STATE_LIVE;
}
/**
* Return true iff we are currently _fully_ hibernating -- that is, if we are
* in a state where we expect to handle no network activity at all.
*/
MOCK_IMPL(int,
we_are_fully_hibernating,(void))
{
return hibernate_state == HIBERNATE_STATE_DORMANT;
}
/** If we aren't currently dormant, close all connections and become
* dormant. */
static void
@ -1187,6 +1200,8 @@ on_hibernate_state_change(hibernate_state_t prev_state)
if (prev_state != HIBERNATE_STATE_INITIAL) {
rescan_periodic_events(get_options());
}
reschedule_per_second_timer();
}
/** Free all resources held by the accounting module */

View File

@ -25,6 +25,7 @@ void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
int accounting_record_bandwidth_usage(time_t now, or_state_t *state);
void hibernate_begin_shutdown(void);
MOCK_DECL(int, we_are_hibernating, (void));
MOCK_DECL(int, we_are_fully_hibernating,(void));
void consider_hibernation(time_t now);
int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer,

View File

@ -163,11 +163,6 @@ token_bucket_rw_t global_bucket;
/* Token bucket for relayed traffic. */
token_bucket_rw_t global_relayed_bucket;
/* DOCDOC stats_prev_n_read */
static uint64_t stats_prev_n_read = 0;
/* DOCDOC stats_prev_n_written */
static uint64_t stats_prev_n_written = 0;
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
static uint64_t stats_n_bytes_read = 0;
@ -1258,7 +1253,8 @@ run_connection_housekeeping(int i, time_t now)
} else if (we_are_hibernating() &&
! have_any_circuits &&
!connection_get_outbuf_len(conn)) {
/* We're hibernating, there's no circuits, and nothing to flush.*/
/* We're hibernating or shutting down, there's no circuits, and nothing to
* flush.*/
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
(int)conn->s,conn->address, conn->port);
@ -2495,6 +2491,36 @@ hs_service_callback(time_t now, const or_options_t *options)
/** Timer: used to invoke second_elapsed_callback() once per second. */
static periodic_timer_t *second_timer = NULL;
/**
* Enable or disable the per-second timer as appropriate, creating it if
* necessary.
*/
void
reschedule_per_second_timer(void)
{
struct timeval one_second;
one_second.tv_sec = 1;
one_second.tv_usec = 0;
if (! second_timer) {
second_timer = periodic_timer_new(tor_libevent_get_base(),
&one_second,
second_elapsed_callback,
NULL);
tor_assert(second_timer);
}
const bool run_per_second_events =
control_any_per_second_event_enabled() || ! net_is_completely_disabled();
if (run_per_second_events) {
periodic_timer_launch(second_timer, &one_second);
} else {
periodic_timer_disable(second_timer);
}
}
/** Last time that update_current_time was called. */
static time_t current_second = 0;
/** Last time that update_current_time updated current_second. */
@ -2568,8 +2594,6 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
* could use Libevent's timers for this rather than checking the current
* time against a bunch of timeouts every second. */
time_t now;
size_t bytes_written;
size_t bytes_read;
(void)timer;
(void)arg;
@ -2581,18 +2605,8 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
*/
update_current_time(now);
/* the second has rolled over. check more stuff. */
// remove this once it's unneeded
bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
stats_prev_n_read = stats_n_bytes_read;
stats_prev_n_written = stats_n_bytes_written;
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
control_event_conn_bandwidth_used();
control_event_circ_bandwidth_used();
control_event_circuit_cell_stats();
/* Maybe some controller events are ready to fire */
control_per_second_events();
run_scheduled_events(now);
}
@ -2872,17 +2886,7 @@ do_main_loop(void)
}
/* set up once-a-second callback. */
if (! second_timer) {
struct timeval one_second;
one_second.tv_sec = 1;
one_second.tv_usec = 0;
second_timer = periodic_timer_new(tor_libevent_get_base(),
&one_second,
second_elapsed_callback,
NULL);
tor_assert(second_timer);
}
reschedule_per_second_timer();
#ifdef HAVE_SYSTEMD_209
uint64_t watchdog_delay;
@ -3697,7 +3701,6 @@ tor_free_all(int postfork)
memset(&global_bucket, 0, sizeof(global_bucket));
memset(&global_relayed_bucket, 0, sizeof(global_relayed_bucket));
stats_prev_n_read = stats_prev_n_written = 0;
stats_n_bytes_read = stats_n_bytes_written = 0;
time_of_process_start = 0;
time_of_last_signewnym = 0;

View File

@ -94,6 +94,7 @@ uint64_t get_main_loop_error_count(void);
uint64_t get_main_loop_idle_count(void);
void periodic_events_on_new_options(const or_options_t *options);
void reschedule_per_second_timer(void);
extern time_t time_of_process_start;
extern int quiet_level;

View File

@ -1599,15 +1599,24 @@ router_perform_bandwidth_test(int num_circs, time_t now)
}
}
/** Return true iff our network is in some sense disabled: either we're
* hibernating, entering hibernation, or the network is turned off with
* DisableNetwork. */
/** Return true iff our network is in some sense disabled or shutting down:
* either we're hibernating, entering hibernation, or the network is turned
* off with DisableNetwork. */
int
net_is_disabled(void)
{
return get_options()->DisableNetwork || we_are_hibernating();
}
/** Return true iff our network is in some sense "completely disabled" either
* we're fully hibernating or the network is turned off with
* DisableNetwork. */
int
net_is_completely_disabled(void)
{
return get_options()->DisableNetwork || we_are_fully_hibernating();
}
/** Return true iff we believe ourselves to be an authoritative
* directory server.
*/
@ -2268,6 +2277,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* and compute ri->bandwidthburst similarly */
ri->bandwidthburst = get_effective_bwburst(options);
/* Report bandwidth, unless we're hibernating or shutting down */
ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess();
if (dns_seems_to_be_broken() || has_dns_init_failed()) {
@ -2538,6 +2548,8 @@ check_descriptor_bandwidth_changed(time_t now)
return;
prev = router_get_my_routerinfo()->bandwidthcapacity;
/* Consider ourselves to have zero bandwidth if we're hibernating or
* shutting down. */
cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess();
if ((prev != cur && (!prev || !cur)) ||
cur > prev*2 ||

View File

@ -53,6 +53,7 @@ void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
int net_is_disabled(void);
int net_is_completely_disabled(void);
int authdir_mode(const or_options_t *options);
int authdir_mode_handles_descs(const or_options_t *options, int purpose);