]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
Implement AIMD effort estimation.
authorMike Perry <mikeperry-git@torproject.org>
Wed, 13 Jul 2022 23:33:07 +0000 (23:33 +0000)
committerMicah Elizabeth Scott <beth@torproject.org>
Wed, 10 May 2023 14:37:11 +0000 (07:37 -0700)
Now, pow should auto-enable and auto-disable itself.

src/feature/hs/hs_circuit.c
src/feature/hs/hs_circuit.h
src/feature/hs/hs_pow.h
src/feature/hs/hs_service.c

index acb33bbf8815b81614e1699172ccf60dfeae1c36..3f17373b6900651ecf0f0d523124cfcb536aa38a 100644 (file)
@@ -678,6 +678,10 @@ trim_rend_pqueue(hs_pow_service_state_t *pow_state, time_t now)
     if (queued_rend_request_is_too_old(req, now)) {
       log_info(LD_REND, "While trimming, rend request has been pending "
                         "for too long; discarding.");
+
+      if (req->rdv_data.pow_effort > pow_state->max_trimmed_effort)
+        pow_state->max_trimmed_effort = req->rdv_data.pow_effort;
+
       free_pending_rend(req);
     } else {
       smartlist_pqueue_add(new_pqueue,
@@ -689,6 +693,9 @@ trim_rend_pqueue(hs_pow_service_state_t *pow_state, time_t now)
   /* Ok, we have rescued all the entries we want to keep. The rest are
    * all excess. */
   SMARTLIST_FOREACH_BEGIN(old_pqueue, pending_rend_t *, req) {
+    if (req->rdv_data.pow_effort > pow_state->max_trimmed_effort)
+      pow_state->max_trimmed_effort = req->rdv_data.pow_effort;
+
     free_pending_rend(req);
   } SMARTLIST_FOREACH_END(req);
   smartlist_free(old_pqueue);
@@ -719,7 +726,7 @@ count_service_rp_circuits_pending(hs_service_t *service)
  * effort is at least what we're suggesting for that service right now,
  * return 1, else return 0.
  */
-static int
+int
 top_of_rend_pqueue_is_worthwhile(hs_pow_service_state_t *pow_state)
 {
   tor_assert(pow_state->rend_request_pqueue);
@@ -815,6 +822,15 @@ handle_rend_pqueue_cb(mainloop_event_t *ev, void *arg)
    * reschedule the event in order to continue handling them. */
   if (smartlist_len(pow_state->rend_request_pqueue) > 0) {
     mainloop_event_activate(pow_state->pop_pqueue_ev);
+
+    // XXX: Is this a good threshhold to decide that we have a significant
+    // queue? I just made it up.
+    if (smartlist_len(pow_state->rend_request_pqueue) >
+        2*MAX_REND_REQUEST_PER_MAINLOOP) {
+      /* Note the fact that we had multiple eventloops worth of queue
+       * to service, for effort estimation */
+      pow_state->had_queue = 1;
+    }
   }
 }
 
@@ -1334,8 +1350,10 @@ hs_circ_handle_introduce2(const hs_service_t *service,
       goto done;
     }
 
-    /* Increase the total effort in valid requests received this period. */
-    service->state.pow_state->total_effort += data.rdv_data.pow_effort;
+    /* Increase the total effort in valid requests received this period,
+     * but count 0-effort as min-effort, for estimation purposes. */
+    service->state.pow_state->total_effort += MAX(data.rdv_data.pow_effort,
+                                      service->state.pow_state->min_effort);
 
     /* Successfully added rend circuit to priority queue. */
     ret = 0;
index d61ddcede8ec782197b601ad5db94ce31b930d65..da4eb9aa0b8529e1abb133f226977408a100c8e1 100644 (file)
@@ -32,6 +32,8 @@ typedef struct pending_rend_t {
   time_t enqueued_ts;
 } pending_rend_t;
 
+int top_of_rend_pqueue_is_worthwhile(hs_pow_service_state_t *pow_state);
+
 /* Cleanup function when the circuit is closed or freed. */
 void hs_circ_cleanup_on_close(circuit_t *circ);
 void hs_circ_cleanup_on_free(circuit_t *circ);
index 019fea400e27b0c52c98aaff941f4ef98e9dd75c..bc0b823fd99af1c07c30301557a4187cbddae5b3 100644 (file)
@@ -87,6 +87,9 @@ typedef struct hs_pow_service_state_t {
    * be serviced in a timely manner. */
   uint32_t suggested_effort;
 
+  /* The maximum effort of a request we've had to trim, this update period */
+  uint32_t max_trimmed_effort;
+
   /* The following values are used when calculating and updating the suggested
    * effort every HS_UPDATE_PERIOD seconds. */
 
@@ -96,6 +99,8 @@ typedef struct hs_pow_service_state_t {
   time_t next_effort_update;
   /* Sum of effort of all valid requests received since the last update. */
   uint64_t total_effort;
+  /* Did we have elements waiting in the queue during this period? */
+  bool had_queue;
 } hs_pow_service_state_t;
 
 /* Struct to store a solution to the PoW challenge. */
index 045022f182e01a6d3e92ae0f3ca0a0ff86da86e6..4f8a689290e798987b88097ed92d950665501815 100644 (file)
@@ -2678,21 +2678,42 @@ update_suggested_effort(hs_service_t *service, time_t now)
   /* Make life easier */
   hs_pow_service_state_t *pow_state = service->state.pow_state;
 
-  /* Calculate the new suggested effort. */
-  /* TODO Check for overflow? */
-  pow_state->suggested_effort = (uint32_t)(pow_state->total_effort / pow_state->rend_handled);
+  /* Calculate the new suggested effort, using an additive-increase
+   * multiplicative-decrease estimation scheme. */
+  if (pow_state->max_trimmed_effort > pow_state->suggested_effort) {
+    /* If we trimmed a request above our suggested effort, re-estimate the
+     * effort */
+    pow_state->suggested_effort = (uint32_t)(pow_state->total_effort /
+                                             pow_state->rend_handled);
+  } else if (pow_state->had_queue) {
+    /* If we had a queue during this period, and the current top of queue
+     * is at or above the suggested effort, we should re-estimate the effort.
+     * Otherwise, it can stay the same (no change to effort). */
+    if (top_of_rend_pqueue_is_worthwhile(pow_state)) {
+      pow_state->suggested_effort = (uint32_t)(pow_state->total_effort /
+                                               pow_state->rend_handled);
+    }
+  } else {
+    /* If we were able to keep the queue drained the entire update period,
+     * multiplicative decrease the pow by 2/3. */
+    pow_state->suggested_effort = 2*pow_state->suggested_effort/3;
+  }
 
   log_debug(LD_REND, "Recalculated suggested effort: %u",
             pow_state->suggested_effort);
 
-  /* Set suggested effort to max(min_effort, suggested_effort) */
+  /* If the suggested effort has been decreased below the minimum, set it
+   * to zero: no pow needed again until we queue or trim */
   if (pow_state->suggested_effort < pow_state->min_effort) {
-    pow_state->suggested_effort = pow_state->min_effort;
+    // XXX: Verify this disables pow being done at all.
+    pow_state->suggested_effort = 0;
   }
 
   /* Reset the total effort sum and number of rends for this update period. */
   pow_state->total_effort = 0;
   pow_state->rend_handled = 0;
+  pow_state->max_trimmed_effort = 0;
+  pow_state->had_queue = 0;
   pow_state->next_effort_update = now + HS_UPDATE_PERIOD;
 }