Merge pull request #133 from greatest-ape/work-2023-03-09

udp: improve docs, uring code and uring performance; update README; update dependencies
This commit is contained in:
Joakim Frostegård 2023-03-09 23:34:33 +01:00 committed by GitHub
commit a0c0e85122
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 191 additions and 156 deletions

164
Cargo.lock generated
View file

@ -122,7 +122,7 @@ dependencies = [
"serde",
"signal-hook",
"slab",
"socket2 0.4.7",
"socket2 0.4.9",
]
[[package]]
@ -166,7 +166,7 @@ dependencies = [
"rustls",
"serde",
"signal-hook",
"socket2 0.4.7",
"socket2 0.4.9",
"sqlx",
"tokio",
"tokio-rustls",
@ -223,7 +223,7 @@ dependencies = [
"aquatic_udp_protocol",
"blake3",
"cfg-if",
"constant_time_eq",
"constant_time_eq 0.2.4",
"crossbeam-channel",
"getrandom",
"hashbrown 0.13.2",
@ -243,7 +243,7 @@ dependencies = [
"serde",
"signal-hook",
"slab",
"socket2 0.4.7",
"socket2 0.4.9",
"time",
"tinytemplate",
]
@ -282,7 +282,7 @@ dependencies = [
"rand",
"rand_distr",
"serde",
"socket2 0.4.7",
"socket2 0.4.9",
]
[[package]]
@ -324,7 +324,7 @@ dependencies = [
"serde",
"signal-hook",
"slab",
"socket2 0.4.7",
"socket2 0.4.9",
"tungstenite",
]
@ -387,9 +387,9 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "async-trait"
version = "0.1.64"
version = "0.1.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
dependencies = [
"proc-macro2",
"quote",
@ -510,9 +510,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "base64ct"
version = "1.5.3"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bendy"
@ -546,32 +546,32 @@ checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403"
[[package]]
name = "blake3"
version = "1.3.3"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
checksum = "526c210b4520e416420759af363083471656e819a75e831b8d2c9d5a584f2413"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"digest",
"constant_time_eq 0.1.5",
"digest 0.9.0",
]
[[package]]
name = "block-buffer"
version = "0.10.3"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "buddy-alloc"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff9f338986406db85e2b5deb40a9255b796ca03a194c7457403d215173f3fd5"
checksum = "3240a4cb09cf0da6a51641bd40ce90e96ea6065e3a1adc46434029254bcc2d09"
[[package]]
name = "bumpalo"
@ -688,6 +688,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "constant_time_eq"
version = "0.2.4"
@ -779,9 +785,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
dependencies = [
"cfg-if",
"crossbeam-utils",
@ -789,9 +795,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
@ -800,14 +806,14 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset 0.7.1",
"memoffset 0.8.0",
"scopeguard",
]
@ -823,9 +829,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
@ -852,9 +858,9 @@ dependencies = [
[[package]]
name = "csv"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af91f40b7355f82b0a891f50e70399475945bb0b0da4f1700ce60761c9d3e359"
checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
dependencies = [
"csv-core",
"itoa",
@ -884,13 +890,21 @@ dependencies = [
[[package]]
name = "digest"
version = "0.10.6"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -1433,7 +1447,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
"socket2 0.4.7",
"socket2 0.4.9",
"tokio",
"tower-service",
"tracing",
@ -1517,9 +1531,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
@ -1551,9 +1565,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.139"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "libm"
@ -1997,9 +2011,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pem-rfc7468"
@ -2248,18 +2262,18 @@ dependencies = [
[[package]]
name = "raw-cpuid"
version = "10.6.1"
version = "10.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307f7aacdbab3f0adee67d52739a1d71112cc068d6fab169ddeb18e48877fad"
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "rayon"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
@ -2267,9 +2281,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.10.2"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@ -2334,7 +2348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b"
dependencies = [
"byteorder",
"digest",
"digest 0.10.5",
"num-bigint-dig",
"num-integer",
"num-iter",
@ -2376,15 +2390,15 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
[[package]]
name = "ryu"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
@ -2419,9 +2433,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.152"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e"
dependencies = [
"serde_derive",
]
@ -2457,9 +2471,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.152"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217"
dependencies = [
"proc-macro2",
"quote",
@ -2468,9 +2482,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.93"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
dependencies = [
"itoa",
"ryu",
@ -2485,7 +2499,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.5",
]
[[package]]
@ -2496,7 +2510,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.5",
]
[[package]]
@ -2612,9 +2626,9 @@ dependencies = [
[[package]]
name = "socket2"
version = "0.4.7"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi 0.3.9",
@ -2679,7 +2693,7 @@ dependencies = [
"bytes",
"crc",
"crossbeam-queue",
"digest",
"digest 0.10.5",
"dotenvy",
"either",
"event-listener",
@ -2763,9 +2777,9 @@ dependencies = [
[[package]]
name = "subtle"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
@ -2795,18 +2809,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.38"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
@ -2869,9 +2883,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.25.0"
version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
dependencies = [
"autocfg",
"bytes",
@ -2882,9 +2896,9 @@ dependencies = [
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
"socket2 0.4.7",
"socket2 0.4.9",
"tokio-macros",
"windows-sys 0.42.0",
"windows-sys 0.45.0",
]
[[package]]
@ -3042,15 +3056,15 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-bidi"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"

View file

@ -13,11 +13,11 @@ of sub-implementations for different protocols:
[mio]: https://github.com/tokio-rs/mio
[glommio]: https://github.com/DataDog/glommio
| Name | Protocol | OS requirements |
|--------------|----------------------------------------------|------------------------------|
| aquatic_udp | [BitTorrent over UDP] | Unix-like (using [mio]) |
| aquatic_http | [BitTorrent over HTTP] over TLS ([rustls]) | Linux 5.8+ (using [glommio]) |
| aquatic_ws | [WebTorrent], optionally over TLS ([rustls]) | Linux 5.8+ (using [glommio]) |
| Name | Protocol | OS requirements |
|--------------|-----------------------------------|--------------------------------------|
| aquatic_udp | [BitTorrent over UDP] | Unix-like / Linux 6.0+ with io_uring |
| aquatic_http | [BitTorrent over HTTP] over TLS | Linux 5.8+ |
| aquatic_ws | [WebTorrent], optionally over TLS | Linux 5.8+ |
Features at a glance:
@ -27,6 +27,7 @@ Features at a glance:
- Supports forbidding/allowing info hashes
- Built-in TLS support (no reverse proxy needed)
- Automated CI testing of full file transfers
- Prometheus metrics
Known users:
@ -190,22 +191,24 @@ Implements:
This is the most mature of the implementations. I consider it ready for production use.
#### Performance
![UDP BitTorrent tracker throughput comparison](./documents/aquatic-udp-load-test-illustration-2023-01-11.png)
More details are available [here](./documents/aquatic-udp-load-test-2023-01-11.pdf).
#### io_uring
An experimental io_uring backend can be compiled in by passing the `io-uring`
feature. Currently, Linux 6.0 or later is required. The application will
attempt to fall back to the mio backend if your kernel is not supported.
An experimental io_uring backend is available. It currently requires Linux
6.0 or later and will attempt to fall back to the [mio] backend if run with
older kernels. To enable it, pass the `io-uring` feature when compiling:
```sh
cargo build --release -p aquatic_udp --features "io-uring"
```
#### Performance
![UDP BitTorrent tracker throughput comparison](./documents/aquatic-udp-load-test-illustration-2023-01-11.png)
The mio backend was used. More details are available [here](./documents/aquatic-udp-load-test-2023-01-11.pdf).
---
### aquatic_http: HTTP BitTorrent tracker
[BEP 003]: https://www.bittorrent.org/beps/bep_0003.html
@ -236,6 +239,8 @@ without knowing the exact setup.
More details are available [here](./documents/aquatic-http-load-test-2023-01-25.pdf).
---
### aquatic_ws: WebTorrent tracker
Aims for compatibility with [WebTorrent](https://github.com/webtorrent)

View file

@ -3,9 +3,7 @@
## High priority
* udp uring
* should queues be synced?
* miri
* uneven performance?
* thiserror?
* CI
* uring load test?

View file

@ -11,23 +11,30 @@ use aquatic_toml_config::TomlConfig;
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct Config {
/// Socket workers receive requests from the socket, parse them and send
/// them on to the swarm workers. They then receive responses from the
/// swarm workers, encode them and send them back over the socket.
/// Number of socket workers. Increase with core count
///
/// Socket workers receive requests from clients and parse them.
/// Responses to connect requests are sent back immediately. Announce and
/// scrape requests are passed on to swarm workers, which generate
/// responses and send them back to the socket worker, which sends them
/// to the client.
pub socket_workers: usize,
/// Swarm workers receive a number of requests from socket workers,
/// generate responses and send them back to the socket workers.
/// Number of swarm workers. One is enough in almost all cases
///
/// Swarm workers receive parsed announce and scrape requests from socket
/// workers, generate responses and send them back to the socket workers.
pub swarm_workers: usize,
pub log_level: LogLevel,
/// Maximum number of items in each channel passing requests/responses
/// between workers. A value of zero means that the channel will be of
/// between workers. A value of zero means that the channels will be of
/// unbounded size.
pub worker_channel_size: usize,
/// How long to block waiting for requests in swarm workers. Higher
/// values means that with zero traffic, the worker will not unnecessarily
/// cause the CPU to wake up as often. However, high values (something like
/// larger than 1000) combined with very low traffic can cause delays
/// in torrent cleaning.
/// How long to block waiting for requests in swarm workers.
///
/// Higher values means that with zero traffic, the worker will not
/// unnecessarily cause the CPU to wake up as often. However, high values
/// (something like larger than 1000) combined with very low traffic can
/// cause delays in torrent cleaning.
pub request_channel_recv_timeout_ms: u64,
pub network: NetworkConfig,
pub protocol: ProtocolConfig,
@ -85,16 +92,18 @@ pub struct NetworkConfig {
/// $ sudo sysctl -w net.core.rmem_max=104857600
/// $ sudo sysctl -w net.core.rmem_default=104857600
pub socket_recv_buffer_size: usize,
/// Poll event capacity (mio backend)
/// Poll event capacity (mio backend only)
pub poll_event_capacity: usize,
/// Poll timeout in milliseconds (mio backend)
/// Poll timeout in milliseconds (mio backend only)
pub poll_timeout_ms: u64,
/// Number of ring entries (io_uring backend)
/// Number of ring entries (io_uring backend only)
///
/// Will be rounded to next power of two if not already one
/// Will be rounded to next power of two if not already one. Increasing
/// this value can help throughput up to a certain point.
#[cfg(feature = "io-uring")]
pub ring_entries: u16,
pub ring_size: u16,
/// Store this many responses at most for retrying (once) on send failure
/// (mio backend only)
///
/// Useful on operating systems that do not provide an udp send buffer,
/// such as FreeBSD. Setting the value to zero disables resending
@ -120,7 +129,7 @@ impl Default for NetworkConfig {
poll_event_capacity: 4096,
poll_timeout_ms: 50,
#[cfg(feature = "io-uring")]
ring_entries: 1024,
ring_size: 1024,
resend_buffer_max_len: 0,
}
}
@ -129,7 +138,7 @@ impl Default for NetworkConfig {
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct ProtocolConfig {
/// Maximum number of torrents to accept in scrape request
/// Maximum number of torrents to allow in scrape request
pub max_scrape_torrents: u8,
/// Maximum number of peers to return in announce response
pub max_response_peers: usize,
@ -210,8 +219,7 @@ pub struct CleaningConfig {
/// Clean pending scrape responses this often (seconds)
///
/// In regular operation, there should be no pending scrape responses
/// lingering for a long time. However, the cleaning also returns unused
/// allocated memory to the OS, so the interval can be configured here.
/// lingering for long enough to have to be cleaned up this way.
pub pending_scrape_cleaning_interval: u64,
/// Allow clients to use a connection token for this long (seconds)
pub max_connection_age: u32,

View file

@ -34,7 +34,8 @@ use super::storage::PendingScrapeResponseSlab;
use super::validator::ConnectionValidator;
use super::{create_socket, EXTRA_PACKET_SIZE_IPV4, EXTRA_PACKET_SIZE_IPV6};
const BUF_LEN: usize = 8192;
const RESPONSE_BUF_LEN: usize = 8192;
const REQUEST_BUF_LEN: usize = 4096;
const USER_DATA_RECV: u64 = u64::MAX;
const USER_DATA_PULSE_TIMEOUT: u64 = u64::MAX - 1;
@ -92,9 +93,9 @@ impl SocketWorker {
response_receiver: Receiver<(ConnectedResponse, CanonicalSocketAddr)>,
priv_dropper: PrivilegeDropper,
) {
let ring_entries = config.network.ring_entries.next_power_of_two();
// Bias ring towards sending to prevent build-up of unsent responses
let send_buffer_entries = ring_entries - (ring_entries / 4);
let ring_entries = config.network.ring_size.next_power_of_two();
// Try to fill up the ring with send requests
let send_buffer_entries = ring_entries;
let socket = create_socket(&config, priv_dropper).expect("create socket");
let access_list_cache = create_access_list_cache(&shared_state.access_list);
@ -119,7 +120,7 @@ impl SocketWorker {
let buf_ring = buf_ring::Builder::new(0)
.ring_entries(ring_entries)
.buf_len(BUF_LEN)
.buf_len(REQUEST_BUF_LEN)
.build()
.unwrap();
@ -153,8 +154,9 @@ impl SocketWorker {
let recv_entry = self
.recv_helper
.create_entry(self.buf_ring.bgid().try_into().unwrap());
// This timeout makes it possible to avoid busy-polling and enables
// regular updates of pending_scrape_valid_until
// This timeout enables regular updates of pending_scrape_valid_until
// and wakes the main loop to send any pending responses in the case
// of no incoming requests
let pulse_timeout_entry = Timeout::new(&self.pulse_timeout as *const _)
.build()
.user_data(USER_DATA_PULSE_TIMEOUT);
@ -250,11 +252,11 @@ impl SocketWorker {
}
}
// Wait for all sendmsg entries to complete, as well as at least
// one recvmsg or timeout, in order to avoid busy-polling if there
// is no incoming data.
// Wait for all sendmsg entries to complete. If none were added,
// wait for at least one recvmsg or timeout in order to avoid
// busy-polling if there is no incoming data.
ring.submitter()
.submit_and_wait(num_send_added + 1)
.submit_and_wait(num_send_added.max(1))
.unwrap();
for cqe in ring.completion() {
@ -320,8 +322,13 @@ impl SocketWorker {
response_counter.fetch_add(1, Ordering::Relaxed);
}
self.send_buffers
.mark_index_as_free(send_buffer_index as usize);
// Safety: OK because cqe using buffer has been
// returned and contents will no longer be accessed
// by kernel
unsafe {
self.send_buffers
.mark_buffer_as_free(send_buffer_index as usize);
}
}
}
}
@ -339,7 +346,7 @@ impl SocketWorker {
if result < 0 {
if -result == libc::ENOBUFS {
::log::warn!("recv failed due to lack of buffers, try increasing ring size");
::log::info!("recv failed due to lack of buffers. If increasing ring size doesn't help, get faster hardware");
} else {
::log::warn!(
"recv failed: {:#}",

View file

@ -6,7 +6,7 @@ use io_uring::opcode::SendMsg;
use crate::config::Config;
use super::{BUF_LEN, SOCKET_IDENTIFIER};
use super::{RESPONSE_BUF_LEN, SOCKET_IDENTIFIER};
pub enum Error {
NoBuffers,
@ -35,13 +35,13 @@ impl ResponseType {
struct SendBuffer {
name_v4: UnsafeCell<libc::sockaddr_in>,
name_v6: UnsafeCell<libc::sockaddr_in6>,
bytes: UnsafeCell<[u8; BUF_LEN]>,
bytes: UnsafeCell<[u8; RESPONSE_BUF_LEN]>,
iovec: UnsafeCell<libc::iovec>,
msghdr: UnsafeCell<libc::msghdr>,
free: bool,
// Only used for statistics
/// Only used for statistics
receiver_is_ipv4: bool,
// Only used for statistics
/// Only used for statistics
response_type: ResponseType,
}
@ -61,7 +61,7 @@ impl SendBuffer {
sin6_addr: libc::in6_addr { s6_addr: [0; 16] },
sin6_scope_id: 0,
}),
bytes: UnsafeCell::new([0; BUF_LEN]),
bytes: UnsafeCell::new([0; RESPONSE_BUF_LEN]),
iovec: UnsafeCell::new(libc::iovec {
iov_base: null_mut(),
iov_len: 0,
@ -81,32 +81,34 @@ impl SendBuffer {
}
}
/// # Safety
///
/// - SendBuffer must be stored at a fixed location in memory
unsafe fn setup_pointers(&mut self, socket_is_ipv4: bool) {
let iovec = &mut *self.iovec.get();
fn setup_pointers(&mut self, socket_is_ipv4: bool) {
unsafe {
let iovec = &mut *self.iovec.get();
iovec.iov_base = self.bytes.get() as *mut libc::c_void;
iovec.iov_len = (&*self.bytes.get()).len();
iovec.iov_base = self.bytes.get() as *mut libc::c_void;
iovec.iov_len = (&*self.bytes.get()).len();
let msghdr = &mut *self.msghdr.get();
let msghdr = &mut *self.msghdr.get();
msghdr.msg_iov = self.iovec.get();
msghdr.msg_iov = self.iovec.get();
if socket_is_ipv4 {
msghdr.msg_name = self.name_v4.get() as *mut libc::c_void;
msghdr.msg_namelen = core::mem::size_of::<libc::sockaddr_in>() as u32;
} else {
msghdr.msg_name = self.name_v6.get() as *mut libc::c_void;
msghdr.msg_namelen = core::mem::size_of::<libc::sockaddr_in6>() as u32;
if socket_is_ipv4 {
msghdr.msg_name = self.name_v4.get() as *mut libc::c_void;
msghdr.msg_namelen = core::mem::size_of::<libc::sockaddr_in>() as u32;
} else {
msghdr.msg_name = self.name_v6.get() as *mut libc::c_void;
msghdr.msg_namelen = core::mem::size_of::<libc::sockaddr_in6>() as u32;
}
}
}
/// # Safety
///
/// - SendBuffer must be stored at a fixed location in memory
/// - SendBuffer.setup_pointers must have been called previously
/// - SendBuffer.setup_pointers must have been called while stored at that
/// fixed location
/// - Contents of struct fields wrapped in UnsafeCell can NOT be accessed
/// simultaneously to this function call
unsafe fn prepare_entry(
&mut self,
response: &Response,
@ -128,6 +130,7 @@ impl SendBuffer {
name.sin_port = addr.port().to_be();
name.sin_addr.s_addr = u32::from(*addr.ip()).to_be();
} else {
// Set receiver protocol type before calling addr.get_ipv6_mapped()
self.receiver_is_ipv4 = addr.is_ipv4();
let addr = if let SocketAddr::V6(addr) = addr.get_ipv6_mapped() {
@ -153,9 +156,7 @@ impl SendBuffer {
self.response_type = ResponseType::from_response(response);
self.free = false;
let sqe = SendMsg::new(SOCKET_IDENTIFIER, self.msghdr.get()).build();
Ok(sqe)
Ok(SendMsg::new(SOCKET_IDENTIFIER, self.msghdr.get()).build())
}
Err(err) => Err(Error::SerializationFailed(err)),
}
@ -178,10 +179,7 @@ impl SendBuffers {
.into_boxed_slice();
for buffer in buffers.iter_mut() {
// Safety: OK because buffers are stored in fixed memory location
unsafe {
buffer.setup_pointers(socket_is_ipv4);
}
buffer.setup_pointers(socket_is_ipv4);
}
Self {
@ -197,7 +195,11 @@ impl SendBuffers {
(buffer.response_type, buffer.receiver_is_ipv4)
}
pub fn mark_index_as_free(&mut self, index: usize) {
/// # Safety
///
/// Only safe to call once buffer is no longer referenced by in-flight
/// io_uring queue entries
pub unsafe fn mark_buffer_as_free(&mut self, index: usize) {
self.buffers[index].free = true;
}
@ -215,8 +217,9 @@ impl SendBuffers {
let buffer = self.buffers.index_mut(index);
// Safety: OK because buffers are stored in fixed memory location
// and buffer pointers were set up in SendBuffers::new()
// Safety: OK because buffers are stored in fixed memory location,
// buffer pointers were set up in SendBuffers::new() and pointers to
// SendBuffer UnsafeCell contents are not accessed elsewhere
unsafe {
match buffer.prepare_entry(response, addr, self.socket_is_ipv4) {
Ok(entry) => {