[tor-commits] [tor/master] Faster circuit_get_by_rend_token_and_purpose()

nickm at torproject.org nickm at torproject.org
Tue Apr 8 16:07:49 UTC 2014


commit 8b9a2cb68b290e550695124d7ef0511225b451d5
Author: Nick Mathewson <nickm at torproject.org>
Date:   Fri Sep 27 11:54:36 2013 -0400

    Faster circuit_get_by_rend_token_and_purpose()
    
    On busy servers, this function takes up something like 3-7% in
    different profiles, and gets invoked every time we need to participate
    as the midpoint in a hidden service.
    
    So maybe walking through a linked list of all the circuits here wasn't
    a good idea.
---
 changes/bug9841      |    7 +++
 src/or/circuitlist.c |  148 +++++++++++++++++++++++++++++++++++++++++++++-----
 src/or/circuitlist.h |    2 +
 src/or/or.h          |   34 +++++++-----
 src/or/rendmid.c     |    6 +-
 5 files changed, 166 insertions(+), 31 deletions(-)

diff --git a/changes/bug9841 b/changes/bug9841
new file mode 100644
index 0000000..8b40dda0
--- /dev/null
+++ b/changes/bug9841
@@ -0,0 +1,7 @@
+  o Minor features (performance):
+
+    - Faster server-side lookups of rendezvous and introduction point
+      circuits by using hashtables instead of linear searches over all
+      the circuits. These functions previously accounted between 3 and
+      7% of CPU usage on some busy relays.
+
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index b0e24a5..8ab673c 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -31,6 +31,7 @@
 #include "rephist.h"
 #include "routerlist.h"
 #include "routerset.h"
+
 #include "ht.h"
 
 /********* START VARIABLES **********/
@@ -45,6 +46,9 @@ static void circuit_free(circuit_t *circ);
 static void circuit_free_cpath(crypt_path_t *cpath);
 static void circuit_free_cpath_node(crypt_path_t *victim);
 static void cpath_ref_decref(crypt_path_reference_t *cpath_ref);
+//static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
+//                                   const uint8_t *token);
+static void circuit_clear_rend_token(or_circuit_t *circ);
 
 /********* END VARIABLES ************/
 
@@ -672,6 +676,8 @@ circuit_free(circuit_t *circ)
     crypto_cipher_free(ocirc->n_crypto);
     crypto_digest_free(ocirc->n_digest);
 
+    circuit_clear_rend_token(ocirc);
+
     if (ocirc->rend_splice) {
       or_circuit_t *other = ocirc->rend_splice;
       tor_assert(other->base_.magic == OR_CIRCUIT_MAGIC);
@@ -1129,21 +1135,118 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
   return NULL;
 }
 
-/** Return the first OR circuit in the global list whose purpose is
- * <b>purpose</b>, and whose rend_token is the <b>len</b>-byte
- * <b>token</b>. */
+/** Map from rendezvous cookie to or_circuit_t */
+static digestmap_t *rend_cookie_map = NULL;
+
+/** Map from introduction point digest to or_circuit_t */
+static digestmap_t *intro_digest_map = NULL;
+
+/** Return the OR circuit whose purpose is <b>purpose</b>, and whose
+ * rend_token is the REND_TOKEN_LEN-byte <b>token</b>. If <b>is_rend_circ</b>,
+ * look for rendezvous point circuits; otherwise look for introduction point
+ * circuits. */
 static or_circuit_t *
-circuit_get_by_rend_token_and_purpose(uint8_t purpose, const char *token,
-                                      size_t len)
+circuit_get_by_rend_token_and_purpose(uint8_t purpose, int is_rend_circ,
+                                      const char *token)
 {
-  circuit_t *circ;
-  for (circ = global_circuitlist; circ; circ = circ->next) {
-    if (! circ->marked_for_close &&
-        circ->purpose == purpose &&
-        tor_memeq(TO_OR_CIRCUIT(circ)->rend_token, token, len))
-      return TO_OR_CIRCUIT(circ);
+  or_circuit_t *circ;
+  digestmap_t *map = is_rend_circ ? rend_cookie_map : intro_digest_map;
+
+  if (!map)
+    return NULL;
+
+  circ = digestmap_get(map, token);
+  if (!circ ||
+      circ->base_.purpose != purpose ||
+      circ->base_.marked_for_close)
+    return NULL;
+
+  if (!circ->rendinfo ||
+      ! bool_eq(circ->rendinfo->is_rend_circ, is_rend_circ) ||
+      tor_memneq(circ->rendinfo->rend_token, token, REND_TOKEN_LEN)) {
+    char *t = tor_strdup(hex_str(token, REND_TOKEN_LEN));
+    log_warn(LD_BUG, "Wanted a circuit with %s:%d, but lookup returned %s:%d",
+             safe_str(t), is_rend_circ,
+             safe_str(hex_str(circ->rendinfo->rend_token, REND_TOKEN_LEN)),
+             (int)circ->rendinfo->is_rend_circ);
+    tor_free(t);
+    return NULL;
   }
-  return NULL;
+
+  return circ;
+}
+
+/** Clear the rendezvous cookie or introduction point key digest that's
+ * configured on <b>circ</b>, if any, and remove it from any such maps. */
+static void
+circuit_clear_rend_token(or_circuit_t *circ)
+{
+  or_circuit_t *found_circ;
+  digestmap_t *map;
+
+  if (!circ || !circ->rendinfo)
+    return;
+
+  map = circ->rendinfo->is_rend_circ ? rend_cookie_map : intro_digest_map;
+
+  if (!map) {
+    log_warn(LD_BUG, "Tried to clear rend token on circuit, but found no map");
+    return;
+  }
+
+  found_circ = digestmap_get(map, circ->rendinfo->rend_token);
+  if (found_circ == circ) {
+    /* Great, this is the right one. */
+    digestmap_remove(map, circ->rendinfo->rend_token);
+  } else if (found_circ) {
+    log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
+             "it was already replaced in the map.");
+  } else {
+    log_warn(LD_BUG, "Tried to clear rend token on circuit, but "
+             "it not in the map at all.");
+  }
+
+  tor_free(circ->rendinfo); /* Sets it to NULL too */
+}
+
+/** Set the rendezvous cookie (if is_rend_circ), or the introduction point
+ * digest (if ! is_rend_circ) of <b>circ</b> to the REND_TOKEN_LEN-byte value
+ * in <b>token</b>, and add it to the appropriate map.  If it previously had a
+ * token, clear it.  If another circuit previously had the same
+ * cookie/intro-digest, mark that circuit and remove it from the map. */
+static void
+circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ,
+                       const uint8_t *token)
+{
+  digestmap_t **map_p, *map;
+  or_circuit_t *found_circ;
+
+  /* Find the right map, creating it as needed */
+  map_p = is_rend_circ ? &rend_cookie_map : &intro_digest_map;
+
+  if (!*map_p)
+    *map_p = digestmap_new();
+
+  map = *map_p;
+
+  /* If this circuit already has a token, we need to remove that. */
+  if (circ->rendinfo)
+    circuit_clear_rend_token(circ);
+
+  found_circ = digestmap_get(map, (const char *)token);
+  if (found_circ) {
+    tor_assert(found_circ != circ);
+    circuit_clear_rend_token(found_circ);
+    if (! found_circ->base_.marked_for_close)
+      circuit_mark_for_close(TO_CIRCUIT(found_circ), END_CIRC_REASON_FINISHED);
+  }
+
+  /* Now set up the rendinfo */
+  circ->rendinfo = tor_malloc(sizeof(*circ->rendinfo));
+  memcpy(circ->rendinfo->rend_token, token, REND_TOKEN_LEN);
+  circ->rendinfo->is_rend_circ = is_rend_circ ? 1 : 0;
+
+  digestmap_set(map, (const char *)token, circ);
 }
 
 /** Return the circuit waiting for a rendezvous with the provided cookie.
@@ -1154,7 +1257,7 @@ circuit_get_rendezvous(const char *cookie)
 {
   return circuit_get_by_rend_token_and_purpose(
                                      CIRCUIT_PURPOSE_REND_POINT_WAITING,
-                                     cookie, REND_COOKIE_LEN);
+                                     1, cookie);
 }
 
 /** Return the circuit waiting for intro cells of the given digest.
@@ -1164,8 +1267,23 @@ or_circuit_t *
 circuit_get_intro_point(const char *digest)
 {
   return circuit_get_by_rend_token_and_purpose(
-                                     CIRCUIT_PURPOSE_INTRO_POINT, digest,
-                                     DIGEST_LEN);
+                                     CIRCUIT_PURPOSE_INTRO_POINT, 0, digest);
+}
+
+/** Set the rendezvous cookie of <b>circ</b> to <b>cookie</b>.  If another
+ * circuit previously had that cookie, mark it. */
+void
+circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie)
+{
+  circuit_set_rend_token(circ, 1, cookie);
+}
+
+/** Set the intro point key digest of <b>circ</b> to <b>cookie</b>.  If another
+ * circuit previously had that intro point digest, mark it. */
+void
+circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest)
+{
+  circuit_set_rend_token(circ, 0, digest);
 }
 
 /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index 874f68c..d8c107f 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -43,6 +43,8 @@ origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
                                          const char *digest, uint8_t purpose);
 or_circuit_t *circuit_get_rendezvous(const char *cookie);
 or_circuit_t *circuit_get_intro_point(const char *digest);
+void circuit_set_rendezvous_cookie(or_circuit_t *circ, const uint8_t *cookie);
+void circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest);
 origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
                                               extend_info_t *info, int flags);
 void circuit_mark_all_unused_circs(void);
diff --git a/src/or/or.h b/src/or/or.h
index 5318b0f..510b8ea 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -99,6 +99,7 @@
 #include "ht.h"
 #include "replaycache.h"
 #include "crypto_curve25519.h"
+#include "tor_queue.h"
 
 /* These signals are defined to help handle_control_signal work.
  */
@@ -3152,20 +3153,8 @@ typedef struct or_circuit_t {
    * is not marked for close. */
   struct or_circuit_t *rend_splice;
 
-#if REND_COOKIE_LEN >= DIGEST_LEN
-#define REND_TOKEN_LEN REND_COOKIE_LEN
-#else
-#define REND_TOKEN_LEN DIGEST_LEN
-#endif
+  struct or_circuit_rendinfo_s *rendinfo;
 
-  /** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
-   * rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
-   * otherwise.
-   * ???? move to a subtype or adjunct structure? Wastes 20 bytes. -NM
-   */
-  char rend_token[REND_TOKEN_LEN];
-
-  /* ???? move to a subtype or adjunct structure? Wastes 20 bytes -NM */
   /** Stores KH for the handshake. */
   char rend_circ_nonce[DIGEST_LEN];/* KH in tor-spec.txt */
 
@@ -3186,6 +3175,25 @@ typedef struct or_circuit_t {
   uint64_t total_cell_waiting_time;
 } or_circuit_t;
 
+typedef struct or_circuit_rendinfo_s {
+
+#if REND_COOKIE_LEN != DIGEST_LEN
+#error "The REND_TOKEN_LEN macro assumes REND_COOKIE_LEN == DIGEST_LEN"
+#endif
+#define REND_TOKEN_LEN DIGEST_LEN
+
+  /** A hash of location-hidden service's PK if purpose is INTRO_POINT, or a
+   * rendezvous cookie if purpose is REND_POINT_WAITING. Filled with zeroes
+   * otherwise.
+   */
+  char rend_token[REND_TOKEN_LEN];
+
+  /** True if this is a rendezvous point circuit; false if this is an
+   * introduction point. */
+  unsigned is_rend_circ;
+
+} or_circuit_rendinfo_t;
+
 /** Convert a circuit subtype to a circuit_t. */
 #define TO_CIRCUIT(x)  (&((x)->base_))
 
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index 1bd11f6..8090bf2 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -111,7 +111,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request,
 
   /* Now, set up this circuit. */
   circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
-  memcpy(circ->rend_token, pk_digest, DIGEST_LEN);
+  circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest);
 
   log_info(LD_REND,
            "Established introduction point on circuit %u for service %s",
@@ -251,7 +251,7 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
   }
 
   circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING);
-  memcpy(circ->rend_token, request, REND_COOKIE_LEN);
+  circuit_set_rendezvous_cookie(circ, request);
 
   base16_encode(hexid,9,(char*)request,4);
 
@@ -327,7 +327,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
   circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_ESTABLISHED);
   circuit_change_purpose(TO_CIRCUIT(rend_circ),
                          CIRCUIT_PURPOSE_REND_ESTABLISHED);
-  memset(circ->rend_token, 0, REND_COOKIE_LEN);
+  circuit_set_rendezvous_cookie(circ, NULL);
 
   rend_circ->rend_splice = circ;
   circ->rend_splice = rend_circ;





More information about the tor-commits mailing list