|
@@ -1499,6 +1499,108 @@ circuit_mark_for_close_(circuit_t *circ, int reason, int line,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * recover memory. */
|
|
|
+static void
|
|
|
+marked_circuit_free_cells(circuit_t *circ)
|
|
|
+{
|
|
|
+ if (!circ->marked_for_close) {
|
|
|
+ log_warn(LD_BUG, "Called on non-marked circuit");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ cell_queue_clear(&circ->n_conn_cells);
|
|
|
+ if (! CIRCUIT_IS_ORIGIN(circ))
|
|
|
+ cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_conn_cells);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static size_t
|
|
|
+n_cells_in_circ_queues(const circuit_t *c)
|
|
|
+{
|
|
|
+ size_t n = c->n_conn_cells.n;
|
|
|
+ if (! CIRCUIT_IS_ORIGIN(c))
|
|
|
+ n += TO_OR_CIRCUIT((circuit_t*)c)->p_conn_cells.n;
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * order. */
|
|
|
+static int
|
|
|
+circuits_compare_by_queue_len_(const void **a_, const void **b_)
|
|
|
+{
|
|
|
+ const circuit_t *a = *a_;
|
|
|
+ const circuit_t *b = *b_;
|
|
|
+ size_t a_n = n_cells_in_circ_queues(a);
|
|
|
+ size_t b_n = n_cells_in_circ_queues(b);
|
|
|
+
|
|
|
+ if (a_n < b_n)
|
|
|
+ return 1;
|
|
|
+ else if (a_n == b_n)
|
|
|
+ return 0;
|
|
|
+ else
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+#define FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM 0.90
|
|
|
+
|
|
|
+
|
|
|
+ * bytes' worth. Kill the 'worst' circuits until we're under
|
|
|
+ * FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM of our maximum usage. */
|
|
|
+void
|
|
|
+circuits_handle_oom(size_t current_allocation)
|
|
|
+{
|
|
|
+
|
|
|
+ smartlist_t *circlist = smartlist_new();
|
|
|
+ circuit_t *circ;
|
|
|
+ size_t n_cells_removed=0, n_cells_to_remove;
|
|
|
+ int n_circuits_killed=0;
|
|
|
+ log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
|
|
|
+ "over-long queues. (This behavior is controlled by "
|
|
|
+ "MaxMemInCellQueues.)");
|
|
|
+
|
|
|
+ {
|
|
|
+ size_t mem_target = (size_t)(get_options()->MaxMemInCellQueues *
|
|
|
+ FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM);
|
|
|
+ size_t mem_to_recover;
|
|
|
+ if (current_allocation <= mem_target)
|
|
|
+ return;
|
|
|
+ mem_to_recover = current_allocation - mem_target;
|
|
|
+ n_cells_to_remove = CEIL_DIV(mem_to_recover, packed_cell_mem_cost());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * to actually run it. */
|
|
|
+ for (circ = global_circuitlist; circ; circ = circ->next)
|
|
|
+ smartlist_add(circlist, circ);
|
|
|
+
|
|
|
+
|
|
|
+ * Let's hope this doesn't happen enough to be in the critical path. */
|
|
|
+ smartlist_sort(circlist, circuits_compare_by_queue_len_);
|
|
|
+
|
|
|
+
|
|
|
+ * them, and reclaim their storage aggressively. */
|
|
|
+ SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {
|
|
|
+ size_t n = n_cells_in_circ_queues(circ);
|
|
|
+ if (! circ->marked_for_close) {
|
|
|
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
|
|
|
+ }
|
|
|
+ marked_circuit_free_cells(circ);
|
|
|
+
|
|
|
+ ++n_circuits_killed;
|
|
|
+ n_cells_removed += n;
|
|
|
+ if (n_cells_removed >= n_cells_to_remove)
|
|
|
+ break;
|
|
|
+ } SMARTLIST_FOREACH_END(circ);
|
|
|
+
|
|
|
+ clean_cell_pool();
|
|
|
+
|
|
|
+ log_notice(LD_GENERAL, "Removed "U64_FORMAT" bytes by killing %d circuits.",
|
|
|
+ U64_PRINTF_ARG(n_cells_removed * packed_cell_mem_cost()),
|
|
|
+ n_circuits_killed);
|
|
|
+
|
|
|
+ smartlist_free(circlist);
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* correct. Trigger an assert if anything is invalid.
|
|
|
*/
|