1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use FourTuple;
use io;
use SocketAddr;
pub
pub
pub
/// Plain single-datagram UDP receive buffer size (no GRO coalescing).
pub const UDP_RECV_BUF_LEN: usize = 2000;
/// Upper bound on the number of datagrams the kernel may coalesce into one UDP GRO
/// receive (`UDP_SEGMENT`/GRO cap is 64 per buffer).
pub const MAX_GRO_SEGMENTS: usize = 64;
/// Upper bound on datagrams coalesced into one UDP GSO send. The kernel caps
/// `UDP_SEGMENT` at 64 segments per `sendmsg`; a socket may report fewer.
pub const MAX_GSO_SEGMENTS: usize = 64;
/// Upper bound on the total bytes of one UDP GSO batch. Kept at the single-datagram
/// UDP payload limit (65535) so a batch never trips the kernel's aggregate-size
/// checks and disables GSO — at ~1.25 KB datagrams this still coalesces ~50 per call.
pub const MAX_GSO_BATCH_BYTES: usize = 65535;
/// Minimum datagrams in a run before it is worth a single GSO `sendmsg` instead of
/// individual `send_to`s. GSO trades N cheap `sendto` syscalls for one heavier
/// `sendmsg` (control-message construction + kernel GSO setup) plus one buffer
/// concatenation, so it only pays off once the run is large. Below this the batching
/// machinery is pure overhead — exactly the paced single-connection case, where the
/// watermark dribbles a few datagrams per flush. A too-low threshold there GSOs the
/// occasional large drain and thrashes the tiny working set (measured on loopback:
/// threshold 2 → wall +58%, threshold 8 → +21%, threshold 16 → −15% i.e. back to a
/// win). Throughput-bound bursts (bulk/flood/many-connection) run far larger (50+),
/// so 16 keeps their full win (N=10 wall −34%, flood +77%) while erasing the
/// single-connection regression.
pub const MIN_GSO_RUN: usize = 16;
/// Per-datagram size assumed when sizing a GRO receive buffer, at the standard
/// Ethernet MTU. GRO coalesces up to `max_gro_segments()` datagrams into one buffer,
/// each at most one wire MTU, so the buffer must be `max_gro_segments() *
/// GRO_RECV_SEGMENT_LEN` — the kernel truncates (silently drops the tail datagrams)
/// if the coalesced super-datagram overflows the buffer. WebRTC keeps its own
/// datagrams well under this (DTLS/SCTP MTU ~1200); the 1500 headroom covers a peer
/// sending up to standard-MTU-sized datagrams. Jumbo-frame paths (MTU > 1500) are not
/// supported for GRO and would truncate.
pub const GRO_RECV_SEGMENT_LEN: usize = 1500;
/// Size a UDP receive buffer for a socket that may coalesce `max_gro` datagrams via
/// GRO. Falls back to the plain single-datagram size when GRO is unavailable.
///
/// NOTE: with GRO enabled this returns ~96 KB (64 * 1500) per socket vs the ~2 KB
/// non-GRO size — a real per-connection RSS cost that scales with socket count
/// (relevant at SFU scale). It cannot be shrunk without risking truncation (see
/// [`GRO_RECV_SEGMENT_LEN`]); the buffers are zero-initialized so pages stay unmapped
/// until actually written. Measured net effect is still an RSS *reduction* under load
/// because batching cuts per-packet allocator churn far more than the buffers cost.
pub
pub
pub
pub