diff options
| -rw-r--r-- | DESIGN | 38 | ||||
| -rw-r--r-- | src/trackerlogic.c | 12 |
2 files changed, 44 insertions, 6 deletions
| @@ -108,6 +108,32 @@ single-family torrent pays for the absent family's vector header. On jemalloc | |||
| 108 | the bins differ, so the per-shape figures do too, but the empty-torrent and | 108 | the bins differ, so the per-shape figures do too, but the empty-torrent and |
| 109 | single-allocation gains hold on both. | 109 | single-allocation gains hold on both. |
| 110 | 110 | ||
| 111 | Reading those structs for one announce is a walk down cold memory. With the | ||
| 112 | hash in hand, the steady-state path to an existing torrent reads, in order: | ||
| 113 | |||
| 114 | step reads load | ||
| 115 | lock one bucket mutex cold | ||
| 116 | bucket header that bucket's torrent vector cold | ||
| 117 | torrent search a hash per probe, log2 of the bucket cold | ||
| 118 | peerlist the torrent's peerlist cold | ||
| 119 | peer search a peer per probe, log2 of the swarm cold | ||
| 120 | reply counts the peerlist's sizes and seeder count hot | ||
| 121 | |||
| 122 | Each cold line is a separate allocation, and on a large table each is a miss. | ||
| 123 | The hot line rode in on the peerlist step before it. The torrent search | ||
| 124 | dominates, one miss per probe into a bucket of hundreds of thousands, with the | ||
| 125 | peerlist pointer chase and the peer search behind it. Aging the torrent adds | ||
| 126 | nothing in steady state: one touched this minute has a zero span and the | ||
| 127 | cleaner returns at once. The reply counts are hot because the two family | ||
| 128 | sizes and the seeder count share the peerlist's line. A split swarm is the | ||
| 129 | exception, since summing its size walks the bucket headers, but those are the | ||
| 130 | largest torrents, where handing out peers already walks the swarm. | ||
| 131 | |||
| 132 | A request waits on two system calls around this, a receive and a send, each | ||
| 133 | on the order of a handful of misses. Memory latency and kernel time are | ||
| 134 | co-dominant. Neither hides behind the other, and no term in the table is one | ||
| 135 | a cache would hold still. | ||
| 136 | |||
| 111 | A refused announce is answered over UDP with a well formed announce for an | 137 | A refused announce is answered over UDP with a well formed announce for an |
| 112 | empty swarm rather than with the error action. BEP 15 binds a client to the | 138 | empty swarm rather than with the error action. BEP 15 binds a client to the |
| 113 | interval it is handed and says nothing about what a client should do with an | 139 | interval it is handed and says nothing about what a client should do with an |
| @@ -140,7 +166,7 @@ Build hardening the shipped binary | |||
| 140 | ---------------------------------- | 166 | ---------------------------------- |
| 141 | 167 | ||
| 142 | The production and debug binaries carry -fstack-protector-strong, -fPIE and | 168 | The production and debug binaries carry -fstack-protector-strong, -fPIE and |
| 143 | _FORTIFY_SOURCE=2 on every platform; the GNU-style linkers also get -pie and | 169 | _FORTIFY_SOURCE=2 on every platform. The GNU-style linkers also get -pie and |
| 144 | full RELRO (-z relro,-z now). Measured cost on | 170 | full RELRO (-z relro,-z now). Measured cost on |
| 145 | the announce hot path is 300 to 305 nanoseconds, under two per cent, and the | 171 | the announce hot path is 300 to 305 nanoseconds, under two per cent, and the |
| 146 | individual flags sit inside the run-to-run noise: add_peer has few stack | 172 | individual flags sit inside the run-to-run noise: add_peer has few stack |
| @@ -152,7 +178,7 @@ The link flags -pie and RELRO are gated on the operating system: Apple's | |||
| 152 | linker rejects -z, and flags a bare -pie in the combined compile-and-link as | 178 | linker rejects -z, and flags a bare -pie in the combined compile-and-link as |
| 153 | unused, while macOS links executables PIE by default, so leaving both off | 179 | unused, while macOS links executables PIE by default, so leaving both off |
| 154 | there costs no hardening. The branch runs in the shell through a != assignment, | 180 | there costs no hardening. The branch runs in the shell through a != assignment, |
| 155 | the one form both bmake and GNU make 4 accept; $(shell) is GNU only and GNU | 181 | the one form both bmake and GNU make 4 accept. $(shell) is GNU only and GNU |
| 156 | make 3.81, which stock macOS ships, has neither. On that old make the != line | 182 | make 3.81, which stock macOS ships, has neither. On that old make the != line |
| 157 | is inert and leaves the flags empty, which is the right answer for macOS | 183 | is inert and leaves the flags empty, which is the right answer for macOS |
| 158 | anyway, so every platform resolves correctly without a make conditional. | 184 | anyway, so every platform resolves correctly without a make conditional. |
| @@ -225,13 +251,19 @@ A directory for object files. bmake has no pattern rules and the Makefile | |||
| 225 | builds under FreeBSD's make today. Building each binary in one invocation | 251 | builds under FreeBSD's make today. Building each binary in one invocation |
| 226 | clears the objects out of the tree without giving that up. | 252 | clears the objects out of the tree without giving that up. |
| 227 | 253 | ||
| 254 | A cached peer count on the torrent or the peerlist. The count is a vector's | ||
| 255 | own size, a field in a line the peer insert has already loaded, so a cache | ||
| 256 | would hold a second copy of a number already in reach and have to be kept in | ||
| 257 | step with the vector it copies. Only a split swarm pays, to sum its buckets, | ||
| 258 | and there the sum is lost against walking the swarm to answer at all. | ||
| 259 | |||
| 228 | 260 | ||
| 229 | 261 | ||
| 230 | Platform differences that cost us code | 262 | Platform differences that cost us code |
| 231 | -------------------------------------- | 263 | -------------------------------------- |
| 232 | 264 | ||
| 233 | SO_REUSEPORT means three different things. Linux spreads datagrams over the | 265 | SO_REUSEPORT means three different things. Linux spreads datagrams over the |
| 234 | group; FreeBSD permits the bind and needs SO_REUSEPORT_LB to spread. Darwin | 266 | group. FreeBSD permits the bind and needs SO_REUSEPORT_LB to spread. Darwin |
| 235 | hands every datagram to whichever socket bound last, so a group there starves | 267 | hands every datagram to whichever socket bound last, so a group there starves |
| 236 | all but one reader. | 268 | all but one reader. |
| 237 | 269 | ||
diff --git a/src/trackerlogic.c b/src/trackerlogic.c index ddbc430..1f56a2c 100644 --- a/src/trackerlogic.c +++ b/src/trackerlogic.c | |||
| @@ -78,7 +78,7 @@ void add_torrent_from_saved_state(ot_hash const hash, ot_time age, size_t down_c | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) { | 80 | size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstruct *ws, size_t amount) { |
| 81 | int exactmatch, delta_torrentcount = 0; | 81 | int exactmatch, delta_torrentcount = 0, completed = 0; |
| 82 | ot_torrent *torrent; | 82 | ot_torrent *torrent; |
| 83 | ot_peer *peer_dest; | 83 | ot_peer *peer_dest; |
| 84 | ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash); | 84 | ot_vector *torrents_list = mutex_bucket_lock_by_hash(*ws->hash); |
| @@ -153,7 +153,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 153 | 153 | ||
| 154 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { | 154 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED) { |
| 155 | torrent->down_count++; | 155 | torrent->down_count++; |
| 156 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | 156 | completed = 1; /* the event, which may syslog, is issued after unlock */ |
| 157 | } | 157 | } |
| 158 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) | 158 | if (OT_PEERFLAG(ws->peer) & PEER_FLAG_SEEDING) |
| 159 | peer_list->seed_count++; | 159 | peer_list->seed_count++; |
| @@ -180,7 +180,7 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 180 | peer_list->seed_count++; | 180 | peer_list->seed_count++; |
| 181 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { | 181 | if (!(OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) && (OT_PEERFLAG(ws->peer) & PEER_FLAG_COMPLETED)) { |
| 182 | torrent->down_count++; | 182 | torrent->down_count++; |
| 183 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | 183 | completed = 1; /* the event, which may syslog, is issued after unlock */ |
| 184 | } | 184 | } |
| 185 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) | 185 | if (OT_PEERFLAG_D(peer_dest, peer_size) & PEER_FLAG_COMPLETED) |
| 186 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED; | 186 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_COMPLETED; |
| @@ -190,12 +190,18 @@ size_t add_peer_to_torrent_and_return_peers(PROTO_FLAG proto, struct ot_workstru | |||
| 190 | #ifdef WANT_SYNC | 190 | #ifdef WANT_SYNC |
| 191 | if (proto == FLAG_MCA) { | 191 | if (proto == FLAG_MCA) { |
| 192 | mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount); | 192 | mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount); |
| 193 | if (completed) | ||
| 194 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | ||
| 193 | return 0; | 195 | return 0; |
| 194 | } | 196 | } |
| 195 | #endif | 197 | #endif |
| 196 | 198 | ||
| 197 | ws->reply_size = return_peers_for_torrent(ws, torrent, amount, ws->reply, proto); | 199 | ws->reply_size = return_peers_for_torrent(ws, torrent, amount, ws->reply, proto); |
| 198 | mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount); | 200 | mutex_bucket_unlock_by_hash(*ws->hash, delta_torrentcount); |
| 201 | |||
| 202 | /* Issued after bucket unlock, so a slow syslog cannot stall the bucket */ | ||
| 203 | if (completed) | ||
| 204 | stats_issue_event(ws, EVENT_COMPLETED, 0, 0); | ||
| 199 | return ws->reply_size; | 205 | return ws->reply_size; |
| 200 | } | 206 | } |
| 201 | 207 | ||
