|
@@ -10,6 +10,7 @@
|
|
|
#include "channel.h"
|
|
|
#include "circuitlist.h"
|
|
|
#include "circuitmux.h"
|
|
|
+#include "relay.h"
|
|
|
|
|
|
/*
|
|
|
* Private typedefs for circuitmux.c
|
|
@@ -115,6 +116,18 @@ struct circuitmux_s {
|
|
|
*/
|
|
|
struct circuit_t *active_circuits_head, *active_circuits_tail;
|
|
|
|
|
|
+ /** List of queued destroy cells */
|
|
|
+ cell_queue_t destroy_cell_queue;
|
|
|
+ /** Boolean: True iff the last cell to circuitmux_get_first_active_circuit
|
|
|
+ * returned the destroy queue. Used to force alternation between
|
|
|
+ * destroy/non-destroy cells.
|
|
|
+ *
|
|
|
+ * XXXX There is no reason to think that alternating is a particularly good
|
|
|
+ * approach -- it's just designed to prevent destroys from starving other
|
|
|
+ * cells completely.
|
|
|
+ */
|
|
|
+ unsigned int last_cell_was_destroy : 1;
|
|
|
+
|
|
|
/*
|
|
|
* Circuitmux policy; if this is non-NULL, it can override the built-
|
|
|
* in round-robin active circuits behavior. This is how EWMA works in
|
|
@@ -508,6 +521,8 @@ circuitmux_free(circuitmux_t *cmux)
|
|
|
tor_free(cmux->chanid_circid_map);
|
|
|
}
|
|
|
|
|
|
+ cell_queue_clear(&cmux->destroy_cell_queue);
|
|
|
+
|
|
|
tor_free(cmux);
|
|
|
}
|
|
|
|
|
@@ -816,7 +831,7 @@ circuitmux_num_cells(circuitmux_t *cmux)
|
|
|
{
|
|
|
tor_assert(cmux);
|
|
|
|
|
|
- return cmux->n_cells;
|
|
|
+ return cmux->n_cells + cmux->destroy_cell_queue.n;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1368,16 +1383,36 @@ circuitmux_set_num_cells(circuitmux_t *cmux, circuit_t *circ,
|
|
|
/**
|
|
|
* Pick a circuit to send from, using the active circuits list or a
|
|
|
* circuitmux policy if one is available. This is called from channel.c.
|
|
|
+ *
|
|
|
+ * If we would rather send a destroy cell, return NULL and set
|
|
|
+ * *<b>destroy_queue_out</b> to the destroy queue.
|
|
|
+ *
|
|
|
+ * If we have nothing to send, set *<b>destroy_queue_out</b> to NULL and
|
|
|
+ * return NULL.
|
|
|
*/
|
|
|
|
|
|
circuit_t *
|
|
|
-circuitmux_get_first_active_circuit(circuitmux_t *cmux)
|
|
|
+circuitmux_get_first_active_circuit(circuitmux_t *cmux,
|
|
|
+ cell_queue_t **destroy_queue_out)
|
|
|
{
|
|
|
circuit_t *circ = NULL;
|
|
|
|
|
|
tor_assert(cmux);
|
|
|
+ tor_assert(destroy_queue_out);
|
|
|
+
|
|
|
+ *destroy_queue_out = NULL;
|
|
|
+
|
|
|
+ if (cmux->destroy_cell_queue.n &&
|
|
|
+ (!cmux->last_cell_was_destroy || cmux->n_active_circuits == 0)) {
|
|
|
+ /* We have destroy cells to send, and either we just sent a relay cell,
|
|
|
+ * or we have no relay cells to send. */
|
|
|
|
|
|
- if (cmux->n_active_circuits > 0) {
|
|
|
+ /* XXXX We should let the cmux policy have some say in this eventually. */
|
|
|
+ /* XXXX Alternating is not a terribly brilliant approach here. */
|
|
|
+ *destroy_queue_out = &cmux->destroy_cell_queue;
|
|
|
+
|
|
|
+ cmux->last_cell_was_destroy = 1;
|
|
|
+ } else if (cmux->n_active_circuits > 0) {
|
|
|
/* We also must have a cell available for this to be the case */
|
|
|
tor_assert(cmux->n_cells > 0);
|
|
|
/* Do we have a policy-provided circuit selector? */
|
|
@@ -1389,7 +1424,11 @@ circuitmux_get_first_active_circuit(circuitmux_t *cmux)
|
|
|
tor_assert(cmux->active_circuits_head);
|
|
|
circ = cmux->active_circuits_head;
|
|
|
}
|
|
|
- } else tor_assert(cmux->n_cells == 0);
|
|
|
+ cmux->last_cell_was_destroy = 0;
|
|
|
+ } else {
|
|
|
+ tor_assert(cmux->n_cells == 0);
|
|
|
+ tor_assert(cmux->destroy_cell_queue.n == 0);
|
|
|
+ }
|
|
|
|
|
|
return circ;
|
|
|
}
|
|
@@ -1743,3 +1782,29 @@ circuitmux_assert_okay_pass_three(circuitmux_t *cmux)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*DOCDOC */
|
|
|
+void
|
|
|
+circuitmux_append_destroy_cell(channel_t *chan,
|
|
|
+ circuitmux_t *cmux,
|
|
|
+ circid_t circ_id,
|
|
|
+ uint8_t reason)
|
|
|
+{
|
|
|
+ cell_t cell;
|
|
|
+ memset(&cell, 0, sizeof(cell_t));
|
|
|
+ cell.circ_id = circ_id;
|
|
|
+ cell.command = CELL_DESTROY;
|
|
|
+ cell.payload[0] = (uint8_t) reason;
|
|
|
+
|
|
|
+ cell_queue_append_packed_copy(&cmux->destroy_cell_queue, &cell,
|
|
|
+ chan->wide_circ_ids, 0);
|
|
|
+
|
|
|
+ /* XXXX Duplicate code from append_cell_to_circuit_queue */
|
|
|
+ if (!channel_has_queued_writes(chan)) {
|
|
|
+ /* There is no data at all waiting to be sent on the outbuf. Add a
|
|
|
+ * cell, so that we can notice when it gets flushed, flushed_some can
|
|
|
+ * get called, and we can start putting more data onto the buffer then.
|
|
|
+ */
|
|
|
+ log_debug(LD_GENERAL, "Primed a buffer.");
|
|
|
+ channel_flush_from_first_active_circuit(chan, 1);
|
|
|
+ }
|
|
|
+}
|