fix(server): measure bandwidth across a single large graphics write - #2021
Greg Lamberson (glamberson) wants to merge 2 commits into
Conversation
|
This pull request may overlap with #2031. Both bracket large EGFX server writes with Bandwidth Measure Start/Stop for auto-detect per MS-RDPBCGR: this PR brackets message-channel writes, while This notice is advisory only. Automated review continues as usual, and how these pull requests relate is for maintainers and authors to decide. Note LLM-assisted content (no human feedback). |
There was a problem hiding this comment.
The PR brackets large EGFX writes with Bandwidth Measure Start/Stop so the client times a real burst, retires the tick-window measurement after the first bracket, floors zero time deltas at one millisecond, and rejects zero byte counts. Independent inspection confirms the bracketing is protocol-conforming, the zero-delta floor only under-reports, and the in-tree Egfx caller pairs begin/end and aborts on write errors. Published: a one-way latch retires the tick window after a single large write, leaving one-large-write-then-quiet sessions without any bandwidth re-measurement; the new public bracket API can wedge the state machine with no timeout or cancel; and three no-behavior-change compression nits in the new code.
There was a problem hiding this comment.
PR 2021 replaces the fixed tick-window bandwidth measurement (which times idle desktop stretches and reports implausibly low figures) with a Start/data/Stop bracket around EGFX writes of at least 10 KiB, paced to one per second, with the tick window kept as a fallback after eight bracket-free ticks, and adds 30 s expiry for abandoned bandwidth transactions. Independent inspection confirms the mechanism is correct: the bracketed continuous Start/Stop go out back-to-back with matching sequence numbers on the message channel, brackets never overlap and hold off the tick window, the tick counter reset keeps the fallback closed only while brackets recur, expiry covers both a Stop that is never sent and an unanswered measurement without cutting short an open window, and sequence allocation is centralized. The wire-level integration in server.rs matches the existing auto-detect path. Remaining concerns are the unverified zero-timeDelta semantic inversion (which also deviates from the spec-ma…
- [protocol + skeptical] Zero timeDelta clamped to 1 ms without client-behavior evidence, deviating from the spec-mandated calculation — medium 🟠 ❓ — crates/ironrdp-server/src/autodetect.rs
measured_bandwidth_kbps inverts the previously tested semantic (zero timeDelta made the result unusable and cleared the stored figure) into clamp-to-1-ms-and-report. The timer-resolution argument is physically plausible and the old rule would discard most bracketed measurements on fast links, but nothing in the repository shows clients never use timeDelta 0 as a no-measurement sentinel, and the PR cites no evidence that gnome-remote-desktop clamps zero deltas. A client that does mean 'invalid' receives a fabricated byte_count*8 kbps figure (capped at u32::MAX, ~4.3 Tbps) advertised in RDP_NETCHAR_RESULTS. The handling also intentionally deviates from the MS-RDPBCGR-mandated (byteCount*8)/timeDelta calculation at both degenerate inputs (zero bytes yields None rather than 0 kbps; zero delta floors to 1 ms where the formula is undefined); the deviation is defensible and partly documented, but the divergence from the MUST clause should be documented against the spec. Missing client-behavior context prevents a firm conclusion. - [skeptical] measured_bandwidth_kbps duplicates the PDU crate's computed_bandwidth_kbps, which now has no production callers — low 🟡 — crates/ironrdp-server/src/autodetect.rs
After this change the server computes bandwidth from the raw Bandwidth Measure Results fields with a private helper, and a search confirms AutoDetectResponse::computed_bandwidth_kbps (ironrdp-pdu/src/rdp/autodetect.rs:613) is referenced only by its own unit tests; the server was its sole production caller. The same wire formula now lives in two crates with deliberately divergent zero handling, so any future correction (rounding, overflow, units) must be found and mirrored by hand, and the two implementations will silently disagree on what a client's result means. The stated goal of avoiding the impossible branch did not require duplication: the special case could wrap a call to the PDU helper or the helper could take the clamped delta.
The continuous measurement opened a fixed window of RTT ticks, and the client counts only the traffic that happens to pass between Start and Stop, so a window over a quiet desktop timed idle time and reported a fast link as a few hundred kbps. Bracket one EGFX write of at least 10 KiB with Start and Stop instead, at most once a second, so the client times a burst the link carried. Sessions without EGFX keep the tick window until the first bracketed measurement. A zero time delta now counts as one millisecond: the client's timer has millisecond resolution and a burst on a fast link completes within one. A result with no bytes counted still clears the stored figure.
The first bracketed measurement no longer turns the tick window off for good: each bracketed Start resets the tick count, so the window takes over again once eight ticks pass without one. A bracketed measurement, or one waiting for the client's results, is now dropped by expire_stale_probes once it is older than the RTT probe age, so a Stop that is never sent or a client that never answers no longer stops measurement for the rest of the session. A tick window that is still open is not timed, since it sends its own Stop. Also folds the Start and Stop writes into let chains, passes the measured fields to measured_bandwidth_kbps, and allocates sequence numbers through one helper.
95b0633 to
5cdf387
Compare
Summary
expire_stale_probes), instead of stopping bandwidth measurement for the rest of the session. This also covers the tick window, which could already get stuck that way.AutoDetectManager::begin_bandwidth_measureandAutoDetectManager::end_bandwidth_measure. Additive;build_bandwidth_measureis unchanged.Validation
cargo xtask check fmt/lints/tests/typos/locksall pass. Nine new tests inironrdp-testsuite-corecover the bracket, the size threshold, the pacing, the tick window staying closed while brackets recur and resuming when they stop, the expiry of an unended bracket and of an unanswered measurement, a slow tick window still reaching its Stop, and the zero-delta rule. The existing test that treated a zero time delta as unusable now checks a zero byte count instead.