Prechádzať zdrojové kódy

Favor quiet circuits when choosing which order to relay cells in.

Each circuit is ranked in terms of how many cells from it have been
relayed recently, using a time-weighted average.

This patch has been tested this on a private Tor network on PlanetLab,
and gotten improvements of 12-35% in time it takes to fetch a small
web page while there's a simultaneous large data transfer going on
simultaneously.

[Commit msg by nickm based on mail from Ian Goldberg.]
Can Tang 16 rokov pred
rodič
commit
d3be00e0f4
5 zmenil súbory, kde vykonal 191 pridanie a 6 odobranie
  1. 12 0
      src/or/circuitlist.c
  2. 5 0
      src/or/config.c
  3. 24 4
      src/or/connection.c
  4. 32 0
      src/or/or.h
  5. 118 2
      src/or/relay.c

+ 12 - 0
src/or/circuitlist.c

@@ -385,6 +385,10 @@ init_circuit_base(circuit_t *circ)
   circ->package_window = circuit_initial_package_window();
   circ->deliver_window = CIRCWINDOW_START;
 
+  /* Initialize the cell_ewma_t structure */
+  circ->n_cell_ewma.last_cell_time = circ->highres_created;
+  circ->n_cell_ewma.cell_count = 0.0;
+
   circuit_add(circ);
 }
 
@@ -432,6 +436,14 @@ or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn)
 
   init_circuit_base(TO_CIRCUIT(circ));
 
+  /* Initialize the cell_ewma_t structure */
+
+  /* Fetch the timeval that init_circuit_base filled in. */
+  circ->p_cell_ewma.last_cell_time = TO_CIRCUIT(circ)->highres_created;
+
+  /* Initialize the cell counts to 0 */
+  circ->p_cell_ewma.cell_count = 0.0;
+
   return circ;
 }
 

+ 5 - 0
src/or/config.c

@@ -355,6 +355,11 @@ static config_var_t _option_vars[] = {
   VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
       NULL),
   V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
+
+  /* Options for EWMA selection of circuit to write from */
+  VAR("EWMASignificance", DOUBLE, EWMASignificance, "-1.0"),
+  VAR("EWMAInterval", DOUBLE, EWMAInterval, "-1.0"),
+
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
 

+ 24 - 4
src/or/connection.c

@@ -2275,8 +2275,8 @@ connection_read_bucket_should_increase(or_connection_t *conn)
  * Mark the connection and return -1 if you want to close it, else
  * return 0.
  */
-int
-connection_handle_read(connection_t *conn)
+static int
+connection_handle_read_impl(connection_t *conn)
 {
   int max_to_read=-1, try_to_read;
   size_t before, n_read = 0;
@@ -2371,6 +2371,17 @@ loop_again:
   return 0;
 }
 
+int
+connection_handle_read(connection_t *conn)
+{
+  int res;
+
+  tor_gettimeofday_cache_clear();
+  res = connection_handle_read_impl(conn);
+  return res;
+  
+}
+
 /** Pull in new bytes from conn-\>s or conn-\>linked_conn onto conn-\>inbuf,
  * either directly or via TLS. Reduce the token buckets by the number of bytes
  * read.
@@ -2572,8 +2583,8 @@ connection_outbuf_too_full(connection_t *conn)
  * Mark the connection and return -1 if you want to close it, else
  * return 0.
  */
-int
-connection_handle_write(connection_t *conn, int force)
+static int
+connection_handle_write_impl(connection_t *conn, int force)
 {
   int e;
   socklen_t len=(socklen_t)sizeof(e);
@@ -2740,6 +2751,15 @@ connection_handle_write(connection_t *conn, int force)
   return 0;
 }
 
+int
+connection_handle_write(connection_t *conn, int force)
+{
+    int res;
+    tor_gettimeofday_cache_clear();
+    res = connection_handle_write_impl(conn, force);
+    return res;
+}
+
 /** OpenSSL TLS record size is 16383; this is close. The goal here is to
  * push data out as soon as we know there's enough for a TLS record, so
  * during periods of high load we won't read entire megabytes from

+ 32 - 0
src/or/or.h

@@ -1992,6 +1992,20 @@ typedef struct {
   time_t expiry_time;
 } cpath_build_state_t;
 
+/**
+ * The cell_ewma_t structure keeps track of how many cells a circuit has
+ * transferred recently.  It keeps an EWMA (exponentially weighted
+ * moving average) of the number of cells flushed in
+ * connection_or_flush_from_first_active_circuit().
+ */
+
+typedef struct {
+    /** The last time a cell was flushed from this circuit. */
+    struct timeval last_cell_time;
+    /** The EWMA of the cell count. */
+    double cell_count;
+} cell_ewma_t;
+
 #define ORIGIN_CIRCUIT_MAGIC 0x35315243u
 #define OR_CIRCUIT_MAGIC 0x98ABC04Fu
 
@@ -2081,6 +2095,10 @@ typedef struct circuit_t {
 
   /** Unique ID for measuring tunneled network status requests. */
   uint64_t dirreq_id;
+
+  /** The EWMA count for the number of cells flushed from the
+   * n_conn_cells queue. */
+  cell_ewma_t n_cell_ewma;
 } circuit_t;
 
 /** Largest number of relay_early cells that we can send on a given
@@ -2212,6 +2230,10 @@ typedef struct or_circuit_t {
    * exit-ward queues of this circuit; reset every time when writing
    * buffer stats to disk. */
   uint64_t total_cell_waiting_time;
+
+  /** The EWMA count for the number of cells flushed from the
+   * p_conn_cells queue. */
+  cell_ewma_t p_cell_ewma;
 } or_circuit_t;
 
 /** Convert a circuit subtype to a circuit_t.*/
@@ -2740,6 +2762,14 @@ typedef struct {
    * to make this false. */
   int ReloadTorrcOnSIGHUP;
 
+  /* The EWMA parameters for circuit selection within a connection.
+   * The most recent EWMAInterval seconds will account for an
+   * EWMASignificance (between 0 and 1) portion of the weight.
+   * If these values are negative, use the global defaults (soon to be
+   * set in the consensus). */
+  double EWMASignificance;
+  double EWMAInterval;
+
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */
@@ -5122,5 +5152,7 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
                                    size_t intro_points_encoded_size);
 int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
 
+void tor_gettimeofday_cache_clear(void);
+
 #endif
 

+ 118 - 2
src/or/relay.c

@@ -10,6 +10,7 @@
  *    receiving from circuits, plus queuing on circuits.
  **/
 
+#include <math.h>
 #include "or.h"
 #include "mempool.h"
 
@@ -35,6 +36,26 @@ circuit_resume_edge_reading_helper(edge_connection_t *conn,
 static int
 circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint);
 
+/** Cache the current hi-res time; the cache gets reset when libevent
+ * calls us. */
+
+static struct timeval cached_time_hires = {0, 0};
+
+static void
+tor_gettimeofday_cached(struct timeval *tv)
+{
+  if (cached_time_hires.tv_sec == 0) {
+    tor_gettimeofday(&cached_time_hires);
+  }
+  *tv = cached_time_hires;
+}
+
+void
+tor_gettimeofday_cache_clear(void)
+{
+    cached_time_hires.tv_sec = 0;
+}
+
 /** Stats: how many relay cells have originated at this hop, or have
  * been relayed onward (not recognized at this hop)?
  */
@@ -1633,7 +1654,7 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell)
     insertion_time_queue_t *it_queue = queue->insertion_times;
     if (!it_pool)
       it_pool = mp_pool_new(sizeof(insertion_time_elem_t), 1024);
-    tor_gettimeofday(&now);
+    tor_gettimeofday_cached(&now);
 #define SECONDS_IN_A_DAY 86400L
     added = (uint32_t)(((now.tv_sec % SECONDS_IN_A_DAY) * 100L)
             + ((uint32_t)now.tv_usec / (uint32_t)10000L));
@@ -1857,9 +1878,101 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
   cell_queue_t *queue;
   circuit_t *circ;
   int streams_blocked;
+
+  /* The current (hi-res) time */
+  struct timeval now_hires;
+
+  /* The EWMA cell counter for the circuit we're flushing. */
+  cell_ewma_t *cell_ewma = NULL;
+
+  /* The global cell EWMA parameters.  The algorithm is parameterized by
+   * two values (type double):
+   *
+   * "significance" (between 0 and 1) and "interval"
+   *
+   * The cell count is weighted so that the most recent "interval"
+   * seconds will account for "significance" of the weight.
+   *
+   * If "interval" is set to 0, it disables the algorithm, and the old
+   * algorithm (round-robin) is used.
+   *
+   * These parameters should really be set by the consensus, but can be
+   * overridden by the torrc (in which case the options values will be
+   * >= 0.0).
+   */
+  static double cell_ewma_significance = 0.9;
+  static double cell_ewma_interval = 10.0;
+
+  double significance_override = get_options()->EWMASignificance;
+  double interval_override = get_options()->EWMAInterval;
+  if (significance_override >= 0.0) {
+    cell_ewma_significance = significance_override;
+  }
+  if (interval_override >= 0.0) {
+    cell_ewma_interval = interval_override;
+  }
+
   circ = conn->active_circuits;
   if (!circ) return 0;
   assert_active_circuits_ok_paranoid(conn);
+
+  /* See if we're doing the ewma circuit selection algorithm. */
+  if (cell_ewma_interval > 0.0) {
+    /* Is there another circuit we might like better? */
+    circuit_t *circ_iter, *circ_start;
+    circuit_t *circ_min_cell_count = NULL;
+    double min_cell_count = 0.0;
+    tor_gettimeofday_cached(&now_hires);
+
+    /* Start with circ, and go around the circular linked list */
+    circ_start = circ_iter = circ;
+    do {
+      double delta_t;
+
+      /* Find the appropriate EWMA cell counter to use. */
+      if (circ_iter->n_conn == conn) {
+        cell_ewma = &(circ_iter->n_cell_ewma);
+      } else {
+        cell_ewma = &(TO_OR_CIRCUIT(circ_iter)->p_cell_ewma);
+      }
+
+      /* Update the EWMA cell counter to account for the passage of time. */
+      delta_t = (double)(now_hires.tv_sec -
+			    cell_ewma->last_cell_time.tv_sec);
+      delta_t += ((double)(now_hires.tv_usec -
+			    cell_ewma->last_cell_time.tv_usec)) / 1000000.0;
+
+      if (delta_t > 0.0) {
+        cell_ewma->cell_count *=
+	    pow(cell_ewma_significance, delta_t / cell_ewma_interval);
+		//printf("cc: %f ", cell_ewma->cell_count);
+      }
+      cell_ewma->last_cell_time = now_hires;
+
+      /* Now keep track of the lowest cell count we've seen. */
+      if (circ_min_cell_count == NULL ||
+	    cell_ewma->cell_count < min_cell_count) {
+		min_cell_count = cell_ewma->cell_count;
+		circ_min_cell_count = circ_iter;
+      }
+      
+      circ_iter = *next_circ_on_conn_p(circ_iter, conn);
+    } while (circ_iter != circ_start);
+
+    /* OK, we've gone all the way around.  Let's use the circ with the
+     * lowest (recent) cell count. */
+    circ = circ_min_cell_count;
+
+    /* Now set the appropriate EWMA cell counter to use below to add the
+     * cells we actually send. */
+    if (circ_min_cell_count->n_conn == conn) {
+      cell_ewma = &(circ_min_cell_count->n_cell_ewma);
+    } else {
+      cell_ewma = &(TO_OR_CIRCUIT(circ_min_cell_count)->p_cell_ewma);
+    }
+  }
+  
+
   if (circ->n_conn == conn) {
     queue = &circ->n_conn_cells;
     streams_blocked = circ->streams_blocked_on_n_conn;
@@ -1879,7 +1992,7 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
       uint32_t flushed;
       uint32_t cell_waiting_time;
       insertion_time_queue_t *it_queue = queue->insertion_times;
-      tor_gettimeofday(&now);
+      tor_gettimeofday_cached(&now);
       flushed = (uint32_t)((now.tv_sec % SECONDS_IN_A_DAY) * 100L +
                  (uint32_t)now.tv_usec / (uint32_t)10000L);
       if (!it_queue || !it_queue->first) {
@@ -1915,6 +2028,9 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
 
     packed_cell_free_unchecked(cell);
     ++n_flushed;
+    if (cell_ewma) {
+      cell_ewma->cell_count += 1.0;
+    }
     if (circ != conn->active_circuits) {
       /* If this happens, the current circuit just got made inactive by
        * a call in connection_write_to_buf().  That's nothing to worry about: