diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/TODO | 2 | ||||
-rw-r--r-- | src/or/connection.c | 44 | ||||
-rw-r--r-- | src/or/connection_edge.c | 3 | ||||
-rw-r--r-- | src/or/connection_or.c | 49 | ||||
-rw-r--r-- | src/or/dirserv.c | 100 | ||||
-rw-r--r-- | src/or/or.h | 30 | ||||
-rw-r--r-- | src/or/relay.c | 57 |
8 files changed, 78 insertions, 212 deletions
@@ -6,6 +6,8 @@ Changes in version 0.2.0.1-alpha - 2007-??-?? queue for each circuit. This lets us use less slack memory, and will eventually let us be smarter about prioritizing different kinds of traffic. + - Stop reading on edge connections when their corresponding circuit + buffers are full; start again as the circuits empty out. o Security fixes: - Directory authorities now call routers stable if they have an @@ -67,6 +69,9 @@ Changes in version 0.2.0.1-alpha - 2007-??-?? o Code simplifications and refactoring - Stop passing around circuit_t and crypt_path_t pointers that are implicit in other procedure arguments. + - Drop the old code to choke directory connections when the corresponding + or connections got full: thanks to the cell queue feature, or conns + don't get full any more. Changes in version 0.1.2.12-rc - 2007-03-16 @@ -74,7 +74,7 @@ Things we'd like to do in 0.2.0.x: is full, not when the orconn is "too full". - Do we switch to arena-allocation for cells? - Can we stop doing so many memcpys on cells? - - Also, only package data from exitconns when there is space on the + o Also, only package data from exitconns when there is space on the target OR conn's outbuf? or when the circuit is not too full. - MAYBE kill stalled circuits rather than stalled connections; consider anonymity implications. diff --git a/src/or/connection.c b/src/or/connection.c index 0d21ee759..2163c8234 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -416,10 +416,6 @@ connection_about_to_close_connection(connection_t *conn) } if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC) rend_client_desc_here(dir_conn->rend_query); /* give it a try */ - /* If this is from BEGIN_DIR, unlink it from the edge_conn and - * the or_conn. */ - if (dir_conn->bridge_conn) - connection_dirserv_unlink_from_bridge(dir_conn); break; case CONN_TYPE_OR: or_conn = TO_OR_CONN(conn); @@ -446,13 +442,6 @@ connection_about_to_close_connection(connection_t *conn) control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED, control_tls_error_to_reason(or_conn->tls_error)); } - /* Remove any dir_conns that are blocked on this one. Non-blocked - * ones will die when the circuits do. */ - while (or_conn->blocked_dir_connections) { - dir_connection_t *dir_conn = or_conn->blocked_dir_connections; - connection_dirserv_unlink_from_bridge(dir_conn); - tor_assert(or_conn->blocked_dir_connections != dir_conn); - } /* Now close all the attached circuits on it. */ circuit_unlink_all_from_or_conn(TO_OR_CONN(conn), END_CIRC_REASON_OR_CONN_CLOSED); @@ -485,9 +474,6 @@ connection_about_to_close_connection(connection_t *conn) if (conn->state == EXIT_CONN_STATE_RESOLVING) { connection_dns_remove(edge_conn); } - /* If we're relaying a dirserv connection, clean up any pointers */ - if (edge_conn->bridge_for_conn) - connection_dirserv_unlink_from_bridge(edge_conn->bridge_for_conn); break; } } @@ -2431,8 +2417,7 @@ assert_connection_ok(connection_t *conn, time_t now) if (conn->outbuf_flushlen > 0) { tor_assert(connection_is_writing(conn) || conn->wants_to_write || - (conn->type == CONN_TYPE_DIR && - TO_DIR_CONN(conn)->is_blocked_on_or_conn)); + conn->edge_blocked_on_circ); } if (conn->hold_open_until_flushed) @@ -2496,34 +2481,7 @@ assert_connection_ok(connection_t *conn, time_t now) tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT || conn->purpose == EXIT_PURPOSE_RESOLVE); } - if (edge_conn->bridge_for_conn) { - tor_assert(conn->type == CONN_TYPE_EXIT); - tor_assert(edge_conn->bridge_for_conn->bridge_conn == edge_conn); - } } else if (conn->type == CONN_TYPE_DIR) { - dir_connection_t *dir_conn = TO_DIR_CONN(conn); - - if (dir_conn->bridge_conn) { - tor_assert(DIR_CONN_IS_SERVER(conn)); - tor_assert(dir_conn->bridge_conn->bridge_for_conn == dir_conn); - if (dir_conn->bridge_conn->on_circuit) { - dir_connection_t *d; - or_connection_t *or_conn; - tor_assert(!CIRCUIT_IS_ORIGIN(dir_conn->bridge_conn->on_circuit)); - or_conn = TO_OR_CIRCUIT(dir_conn->bridge_conn->on_circuit)->p_conn; - if (dir_conn->is_blocked_on_or_conn) - tor_assert(or_conn); - for (d = or_conn->blocked_dir_connections; d; - d = d->next_blocked_on_same_or_conn) { - if (d == dir_conn) { - tor_assert(dir_conn->is_blocked_on_or_conn == 1); - break; - } - } - if (!d) - tor_assert(!dir_conn->is_blocked_on_or_conn); - } - } } else { /* Purpose is only used for dir and exit types currently */ tor_assert(!conn->purpose); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index de7dc3654..79192b594 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -2469,9 +2469,6 @@ connection_exit_connect_dir(edge_connection_t *exit_conn) return 0; } - dir_conn->bridge_conn = exit_conn; - exit_conn->bridge_for_conn = dir_conn; - connection_start_reading(TO_CONN(dir_conn)); connection_start_reading(TO_CONN(exit_conn)); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 8ebb6b475..ab73d3ea4 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -16,7 +16,6 @@ const char connection_or_c_id[] = static int connection_tls_finish_handshake(or_connection_t *conn); static int connection_or_process_cells_from_inbuf(or_connection_t *conn); -static int connection_or_empty_enough_for_dirserv_data(or_connection_t *conn); /**************************************************************/ @@ -232,21 +231,22 @@ connection_or_process_inbuf(or_connection_t *conn) } } +/**DOCDOC*/ +#define OR_CONN_HIGHWATER (32*1024) + +/**DOCDOC*/ +#define OR_CONN_LOWWATER (16*1024) + /** Called whenever we have flushed some data on an or_conn: add more data * from active circuits. */ int connection_or_flushed_some(or_connection_t *conn) { - if (conn->blocked_dir_connections && - connection_or_empty_enough_for_dirserv_data(conn)) { - connection_dirserv_stop_blocking_all_on_or_conn(conn); - } - if (buf_datalen(conn->_base.outbuf) < 16*1024) { - int n = (32*1024 - buf_datalen(conn->_base.outbuf)) / CELL_NETWORK_SIZE; + size_t datalen = buf_datalen(conn->_base.outbuf); + if (datalen < OR_CONN_LOWWATER) { + int n = (OR_CONN_HIGHWATER - datalen) / CELL_NETWORK_SIZE; while (conn->active_circuits && n > 0) { - int flushed; - log_info(LD_GENERAL, "Loop, n==%d",n); - flushed = connection_or_flush_from_first_active_circuit(conn, 1); + int flushed = connection_or_flush_from_first_active_circuit(conn, 1); n -= flushed; } } @@ -799,32 +799,3 @@ connection_or_send_destroy(uint16_t circ_id, or_connection_t *conn, int reason) return 0; } -/** A high waterlevel for whether to refill this OR connection - * with more directory information, if any is pending. */ -#define DIR_BUF_FULLNESS_THRESHOLD (128*1024) -/** A bottom waterlevel for whether to refill this OR connection - * with more directory information, if any is pending. We don't want - * to make this too low, since we already run the risk of starving - * the pending dir connections if the OR conn is frequently busy with - * other things. */ -#define DIR_BUF_EMPTINESS_THRESHOLD (96*1024) - -/** Return true iff there is so much data waiting to be flushed on <b>conn</b> - * that we should stop writing directory data to it. */ -int -connection_or_too_full_for_dirserv_data(or_connection_t *conn) -{ - return buf_datalen(conn->_base.outbuf) > DIR_BUF_FULLNESS_THRESHOLD; -} - -/** Return true iff there is no longer so much data waiting to be flushed on - * <b>conn</b> that we should not write directory data to it. */ -static int -connection_or_empty_enough_for_dirserv_data(or_connection_t *conn) -{ - /* Note that the threshold to stop writing is a bit higher than the - * threshold to start again: this should (with any luck) keep us from - * flapping about indefinitely. */ - return buf_datalen(conn->_base.outbuf) < DIR_BUF_EMPTINESS_THRESHOLD; -} - diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 6f11cfd7e..fa6ecef31 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -2012,99 +2012,6 @@ dirserv_test_reachability(int try_all) ctr = (ctr + 1) % 128; } -/** If <b>conn</b> is a dirserv connection tunneled over an or_connection, - * return that connection. Otherwise, return NULL. */ -static INLINE or_connection_t * -connection_dirserv_get_target_or_conn(dir_connection_t *conn) -{ - if (conn->bridge_conn && - conn->bridge_conn->on_circuit && - !CIRCUIT_IS_ORIGIN(conn->bridge_conn->on_circuit)) { - or_circuit_t *circ = TO_OR_CIRCUIT(conn->bridge_conn->on_circuit); - return circ->p_conn; - } else { - return NULL; - } -} - -/** Remove <b>dir_conn</b> from the list of bridged dirserv connections - * blocking on <b>or_conn</b>, and set its status to nonblocked. */ -static INLINE void -connection_dirserv_remove_from_blocked_list(or_connection_t *or_conn, - dir_connection_t *dir_conn) -{ - dir_connection_t **c; - for (c = &or_conn->blocked_dir_connections; *c; - c = &(*c)->next_blocked_on_same_or_conn) { - if (*c == dir_conn) { - tor_assert(dir_conn->is_blocked_on_or_conn == 1); - *c = dir_conn->next_blocked_on_same_or_conn; - dir_conn->next_blocked_on_same_or_conn = NULL; - dir_conn->is_blocked_on_or_conn = 0; - return; - } - } - tor_assert(!dir_conn->is_blocked_on_or_conn); -} - -/** If <b>dir_conn</b> is a dirserv connection that's bridged over an edge_conn - * onto an or_conn, remove it from the blocked list (if it's blocked) and - * unlink it and the edge_conn from one another. */ -void -connection_dirserv_unlink_from_bridge(dir_connection_t *dir_conn) -{ - edge_connection_t *edge_conn; - or_connection_t *or_conn; - tor_assert(dir_conn); - edge_conn = dir_conn->bridge_conn; - or_conn = connection_dirserv_get_target_or_conn(dir_conn); - if (or_conn) { - /* XXXX Really, this is only necessary if dir_conn->is_blocked_on_or_conn. - * But for now, let's leave it in, so the assert can catch */ - connection_dirserv_remove_from_blocked_list(or_conn, dir_conn); - } - dir_conn->is_blocked_on_or_conn = 0; /* Probably redundant. */ - edge_conn->bridge_for_conn = NULL; - dir_conn->bridge_conn = NULL; -} - -/** Stop writing on a bridged dir_conn, and remember that it's blocked because - * its or_conn was too full. */ -static void -connection_dirserv_mark_as_blocked(dir_connection_t *dir_conn) -{ - or_connection_t *or_conn; - if (dir_conn->is_blocked_on_or_conn) - return; - tor_assert(! dir_conn->next_blocked_on_same_or_conn); - or_conn = connection_dirserv_get_target_or_conn(dir_conn); - if (!or_conn) - return; - dir_conn->next_blocked_on_same_or_conn = or_conn->blocked_dir_connections; - or_conn->blocked_dir_connections = dir_conn; - dir_conn->is_blocked_on_or_conn = 1; - connection_stop_writing(TO_CONN(dir_conn)); -} - -/** Tell all bridged dir_conns that were blocked because or_conn's outbuf was - * too full that they can write again. */ -void -connection_dirserv_stop_blocking_all_on_or_conn(or_connection_t *or_conn) -{ - dir_connection_t *dir_conn, *next; - - dir_conn = or_conn->blocked_dir_connections; - while (dir_conn) { - next = dir_conn->next_blocked_on_same_or_conn; - - dir_conn->is_blocked_on_or_conn = 0; - dir_conn->next_blocked_on_same_or_conn = NULL; - connection_start_writing(TO_CONN(dir_conn)); - dir_conn = next; - } - or_conn->blocked_dir_connections = NULL; -} - /** Return an approximate estimate of the number of bytes that will * be needed to transmit the server descriptors (if is_serverdescs -- * they can be either d/ or fp/ queries) or networkstatus objects (if @@ -2309,18 +2216,11 @@ connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn) int connection_dirserv_flushed_some(dir_connection_t *conn) { - or_connection_t *or_conn; tor_assert(conn->_base.state == DIR_CONN_STATE_SERVER_WRITING); if (buf_datalen(conn->_base.outbuf) >= DIRSERV_BUFFER_MIN) return 0; - if ((or_conn = connection_dirserv_get_target_or_conn(conn)) && - connection_or_too_full_for_dirserv_data(or_conn)) { - connection_dirserv_mark_as_blocked(conn); - return 0; - } - switch (conn->dir_spool_src) { case DIR_SPOOL_SERVER_BY_DIGEST: case DIR_SPOOL_SERVER_BY_FP: diff --git a/src/or/or.h b/src/or/or.h index a3a10cf98..416ffb0b4 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -750,10 +750,11 @@ typedef struct connection_t { * before closing it? */ unsigned int inbuf_reached_eof:1; /**< Boolean: did read() return 0 on this * conn? */ - unsigned edge_has_sent_end:1; /**< For debugging; only used on edge + unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge * connections. Set once we've set the stream end, * and check in connection_about_to_close_connection(). */ + unsigned int edge_blocked_on_circ:1; /**< DOCDOC */ /** Used for OR conns that shouldn't get any new circs attached to them. */ unsigned int or_is_obsolete:1; /** For AP connections only. If 1, and we fail to reach the chosen exit, @@ -818,9 +819,7 @@ typedef struct or_connection_t { struct circuit_t *active_circuits; /**< DOCDOC */ struct or_connection_t *next_with_same_id; /**< Next connection with same * identity digest as this one. */ - /** Linked list of bridged dirserver connections that can't write until - * this connection's outbuf is less full. */ - struct dir_connection_t *blocked_dir_connections; + circ_id_type_t circ_id_type:2; /**< When we send CREATE cells along this * connection, which half of the space should * we use? */ @@ -869,10 +868,6 @@ typedef struct edge_connection_t { /** Bytes written since last call to control_event_stream_bandwidth_used() */ uint32_t n_written; - /** Exit only: a dirserv connection that is tunneled over this connection - * using a socketpair. */ - struct dir_connection_t *bridge_for_conn; - char rend_query[REND_SERVICE_ID_LEN+1]; /**< What rendezvous service are we * querying for? (AP only) */ @@ -891,10 +886,6 @@ typedef struct dir_connection_t { char *requested_resource; /**< Which 'resource' did we ask the directory * for? */ unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */ - /** True iff this is a dirserv conn, and it's tunneled over an or_conn, - * and we've stopped writing because the or_conn had too much pending - * data to write */ - unsigned int is_blocked_on_or_conn : 1; /* Used only for server sides of some dir connections, to implement * "spooling" of directory material to the outbuf. Otherwise, we'd have @@ -919,15 +910,6 @@ typedef struct dir_connection_t { char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for * the directory server's signing key. */ - /** If this is a dirserv conn created with a BEGIN_DIR (a "bridged" dirserv - * connection), a pointer to the edge_conn at the other end of its - * socketpair. */ - edge_connection_t *bridge_conn; - /** Next connection in linked list of dirserv connections blocked until - * the or_conns over which they're bridged have enough space in their - * outbufs. */ - struct dir_connection_t *next_blocked_on_same_or_conn; - } dir_connection_t; /** Subtype of connection_t for an connection to a controller. */ @@ -1396,6 +1378,11 @@ typedef struct circuit_t { uint16_t n_port; /** The IPv4 address of the OR that is next in this circuit. */ uint32_t n_addr; + + /** DOCDOC */ + unsigned int streams_blocked_on_n_conn : 1; + unsigned int streams_blocked_on_p_conn : 1; + /** How many relay data cells can we package (read from edge streams) * on this circuit before we receive a circuit-level sendme cell asking * for more? */ @@ -2196,7 +2183,6 @@ char *alloc_http_authenticator(const char *authenticator); void assert_connection_ok(connection_t *conn, time_t now); int connection_or_nonopen_was_started_here(or_connection_t *conn); -int connection_or_too_full_for_dirserv_data(or_connection_t *conn); /********************************* connection_edge.c *************************/ diff --git a/src/or/relay.c b/src/or/relay.c index f3063ddd4..4be5d603b 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1463,6 +1463,11 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) } /** DOCDOC */ +#define CELL_QUEUE_HIGHWATER_SIZE 256 + /** DOCDOC */ +#define CELL_QUEUE_LOWWATER_SIZE 64 + +/** DOCDOC */ static INLINE void cell_free(cell_t *cell) { @@ -1532,6 +1537,7 @@ cell_queue_pop(cell_queue_t *queue) return cell; } +/** DOCDOC */ static INLINE circuit_t ** next_circ_on_conn_p(circuit_t *circ, or_connection_t *conn) { @@ -1612,28 +1618,65 @@ connection_or_unlink_all_active_circs(or_connection_t *orconn) } /** DOCDOC */ +static void +block_streams_on_circ(circuit_t *circ, or_connection_t *orconn, int block) +{ + edge_connection_t *edge = NULL; + if (circ->n_conn == orconn) { + circ->streams_blocked_on_n_conn = block; + if (CIRCUIT_IS_ORIGIN(circ)) + edge = TO_ORIGIN_CIRCUIT(circ)->p_streams; + } else { + circ->streams_blocked_on_p_conn = block; + tor_assert(!CIRCUIT_IS_ORIGIN(circ)); + edge = TO_OR_CIRCUIT(circ)->n_streams; + } + + for (; edge; edge = edge->next_stream) { + connection_t *conn = TO_CONN(edge); + conn->edge_blocked_on_circ = block; + + if (block) { + if (connection_is_reading(conn)) + connection_stop_reading(conn); + } else { + /* Is this right? */ + if (!connection_is_reading(conn)) + connection_start_reading(conn); + } + } +} + +/** DOCDOC */ int connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max) { int n_flushed; cell_queue_t *queue; circuit_t *circ; + int streams_blocked; circ = conn->active_circuits; if (!circ) return 0; - if (circ->n_conn == conn) + if (circ->n_conn == conn) { queue = &circ->n_conn_cells; - else + streams_blocked = circ->streams_blocked_on_n_conn; + } else { queue = &TO_OR_CIRCUIT(circ)->p_conn_cells; + streams_blocked = circ->streams_blocked_on_p_conn; + } for (n_flushed = 0; n_flushed < max && queue->head; ++n_flushed) { cell_t *cell = cell_queue_pop(queue); connection_or_write_cell_to_buf(cell, conn); cell_free(cell); - log_info(LD_GENERAL, "flushed a cell; n ==%d", queue->n); ++n_flushed; } conn->active_circuits = *next_circ_on_conn_p(circ, conn); + + if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE) + block_streams_on_circ(circ, conn, 0); /* unblock streams */ + if (queue->n == 0) { log_info(LD_GENERAL, "Made a circuit inactive."); make_circuit_inactive_on_conn(circ, conn); @@ -1647,16 +1690,20 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, cell_t *cell, int direction) { cell_queue_t *queue; + int streams_blocked; if (direction == CELL_DIRECTION_OUT) { queue = &circ->n_conn_cells; + streams_blocked = circ->streams_blocked_on_n_conn; } else { or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); queue = &orcirc->p_conn_cells; + streams_blocked = circ->streams_blocked_on_p_conn; } cell_queue_append_copy(queue, cell); - log_info(LD_GENERAL, "Added a cell; n ==%d", queue->n); + if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE) + block_streams_on_circ(circ, orconn, 1); /* block streams */ if (queue->n == 1) { /* This was the first cell added to the queue. We need to make this @@ -1676,6 +1723,7 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn, } } +/** DOCDOC */ void assert_active_circuits_ok(or_connection_t *orconn) { @@ -1693,3 +1741,4 @@ assert_active_circuits_ok(or_connection_t *orconn) cur = next; } while (cur != head); } + |