Skip to content

feat: Remove bindgen dependency for MTU crate#3337

Merged
larseggert merged 20 commits into
mozilla:mainfrom
larseggert:feat-mtu-no-bindgen
Apr 15, 2026
Merged

feat: Remove bindgen dependency for MTU crate#3337
larseggert merged 20 commits into
mozilla:mainfrom
larseggert:feat-mtu-no-bindgen

Conversation

@larseggert

Copy link
Copy Markdown
Collaborator

@codecov

codecov Bot commented Jan 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.41%. Comparing base (53d49ba) to head (fdc1d99).
⚠️ Report is 27 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3337      +/-   ##
==========================================
+ Coverage   94.30%   94.41%   +0.11%     
==========================================
  Files         127      133       +6     
  Lines       38837    40081    +1244     
  Branches    38837    40081    +1244     
==========================================
+ Hits        36626    37844    +1218     
- Misses       1376     1391      +15     
- Partials      835      846      +11     
Flag Coverage Δ
freebsd 93.47% <ø> (+0.15%) ⬆️
linux 94.52% <ø> (+0.25%) ⬆️
macos 94.46% <ø> (+0.25%) ⬆️
windows 94.52% <ø> (+0.25%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
neqo-common 98.61% <ø> (+0.11%) ⬆️
neqo-crypto 87.08% <ø> (+0.17%) ⬆️
neqo-http3 93.92% <ø> (ø)
neqo-qpack 95.14% <ø> (+0.10%) ⬆️
neqo-transport 95.60% <ø> (+0.39%) ⬆️
neqo-udp 84.90% <ø> (+1.37%) ⬆️
mtu 86.61% <ø> (ø)

@codspeed-hq

codspeed-hq Bot commented Jan 21, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 24 untouched benchmarks
⏩ 27 skipped benchmarks1


Comparing larseggert:feat-mtu-no-bindgen (fdc1d99) with main (53527a2)

Open in CodSpeed

Footnotes

  1. 27 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@martinthomson martinthomson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I'm following the CI parts, but don't you want CI to (mostly) just check that what is generated matches what is in the tree? But then you might also have CI propose a PR to update what is in-tree on the affected branch.

Comment thread .github/workflows/check-mtu.yml Outdated
@larseggert

larseggert commented Jan 21, 2026

Copy link
Copy Markdown
Collaborator Author

But then you might also have CI propose a PR to update what is in-tree on the affected branch.

That is probably overkill. I don't really expect these bindings to ever change, since OSs have ABI promises.

Edit: But Rust version changes may cause bindgen to generate slightly different bindings. Let me look into the PR thing.

@larseggert larseggert marked this pull request as ready for review January 21, 2026 09:21
Copilot AI review requested due to automatic review settings January 21, 2026 09:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the bindgen build dependency from the MTU crate by replacing runtime binding generation with pre-generated platform-specific binding files. Instead of using bindgen during the build process, the PR checks in generated binding files for each supported platform (Linux, macOS, FreeBSD, NetBSD, OpenBSD, Solaris) and uses conditional compilation to select the appropriate bindings for the target platform.

Changes:

  • Removed bindgen from build dependencies and simplified the build script
  • Added pre-generated binding files for all supported platforms
  • Added CI workflow steps to regenerate bindings and verify they remain up-to-date
  • Updated lint attributes to use #[expect] instead of #[allow] where appropriate

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mtu/Cargo.toml Removed bindgen dependency and updated cargo-machete ignore list
mtu/build.rs Removed bindgen-related code, keeping only platform alias configuration
mtu/src/linux.rs Updated lint attributes and changed to use static binding module path
mtu/src/bsd.rs Updated lint attributes and changed to use static binding module paths per platform
mtu/src/bindings/*.rs Added pre-generated binding files for all supported platforms
mtu/src/bindings/*.h Added header files used as input for binding generation
.github/workflows/check-mtu.yml Added CI jobs to generate bindings and verify they match committed versions
.github/actions/check-vm/action.yml Added bindgen installation and binding generation steps to VM-based tests

Comment thread mtu/src/linux.rs Outdated
Comment thread mtu/src/bsd.rs
Comment thread .github/workflows/check-mtu.yml Outdated
@larseggert larseggert force-pushed the feat-mtu-no-bindgen branch 2 times, most recently from 401f12e to da05690 Compare February 12, 2026 13:18
@larseggert larseggert force-pushed the feat-mtu-no-bindgen branch from da05690 to bd4c008 Compare March 27, 2026 07:53
Comment thread .github/actions/check-vm/action.yml Outdated
Comment thread .github/actions/check-vm/action.yml
Comment thread .github/workflows/check-mtu.yml
Comment thread mtu/src/bsd.rs
Comment thread mtu/src/linux.rs Outdated
Comment thread .github/workflows/check-mtu.yml Outdated
Comment thread mtu/src/bindings/bsd.h Outdated
Comment thread mtu/build.rs

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Good approach — replacing runtime bindgen with pre-generated, CI-verified bindings eliminates the libclang build-time dependency for downstream consumers while keeping bindings provably in sync via the check-bindings job. The centralized LINUX_BINDGEN_ARGS / BSD_BINDGEN_ARGS env vars in the workflow address earlier DRY concerns, and the auto-PR mechanism for push events is a nice touch.

One blocking issue: the gecko feature and mozbuild build-dependency are now orphaned — build.rs no longer references mozbuild at all. This should be cleaned up to avoid confusing Firefox integration builds (see inline comment on Cargo.toml).

CI observability gaps: the check-bindings job can silently skip platforms whose upstream VM jobs failed (no artifact = no comparison), and the VM jobs report success even when all tests were skipped due to binding drift. Neither issue causes incorrect results, but both reduce the signal CI provides. Details in inline comments.

The Rust code changes (lint attributes, #[cfg_attr] path selection for per-platform binding modules) are clean and correct. The unsafe blocks are pre-existing and unchanged. The #[expect] vs #[allow] usage for dead_code is deliberate and well-considered across platforms.

Comment thread mtu/Cargo.toml Outdated
Comment thread .github/actions/check-vm/action.yml
Comment thread .github/workflows/check-mtu.yml
Comment thread mtu/src/linux.rs
Comment thread .github/workflows/check-mtu.yml Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Good approach — replacing runtime bindgen with pre-generated, CI-verified bindings eliminates the libclang build-time dependency for downstream consumers while keeping bindings provably in sync via the check-bindings job. The Rust code changes are clean and correct.

Previous review items now resolved in the latest commit:

  • The gecko feature and mozbuild build-dependency have been properly cleaned up from both mtu/Cargo.toml and neqo-transport/Cargo.toml.
  • The DRY concern about duplicated bindgen arguments is addressed: args are now centralized as LINUX_BINDGEN_ARGS / BSD_BINDGEN_ARGS env vars in check-mtu.yml and passed to the VM action via the bindgen-args input.

One should-fix issue (see inline): the auto-PR dedup check in check-bindings uses gh pr list --head "chore-update-mtu-bindings-" which does exact branch-name matching. Since branches are created with a SHA suffix (chore-update-mtu-bindings-<sha>), this will never match, resulting in duplicate PRs on every push to main with binding changes.

Previous review items still present (not re-raised inline):

  • check-vm/action.yml:92-93: exit 0 on binding drift causes the VM job to report success even though all compile/test/clippy checks were skipped. The ::warning:: annotation provides some signal, but CI status checks won't surface the skip.
  • check-mtu.yml:123: if: always() && !cancelled() on check-bindings means it runs even if upstream jobs failed without producing artifacts. The ::warning:: on line 142 mitigates this partially.

Comment thread .github/workflows/check-mtu.yml Outdated
Comment thread mtu/src/bindings/openbsd.rs
Comment thread .github/actions/check-vm/action.yml Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Good approach — replacing runtime bindgen with pre-generated, CI-verified bindings eliminates the libclang build-time dependency for downstream consumers while keeping bindings provably in sync. The centralized LINUX_BINDGEN_ARGS / BSD_BINDGEN_ARGS env vars and the auto-PR mechanism for push events are well designed.

Rust code is clean: the #[cfg_attr(..., path = "...")] pattern for per-platform module selection is idiomatic, and the compile-time size/alignment/offset assertions in the generated bindings provide a strong safety net against ABI drift. The gecko / mozbuild cleanup (flagged in a previous review) is now properly resolved across both mtu/Cargo.toml and neqo-transport/Cargo.toml.

Two issues flagged inline (one new, one reinforcing an existing thread):

  1. bsd.rs expect(dead_code) will error on Solaris — the exclusion must also cover Solaris, not just NetBSD, because all generated items are transitively reachable on both platforms. See inline for details and a suggestion.
  2. --head dedup in check-bindings never matches — reinforcing the existing observation with a concrete fix using a fixed branch name.

Previously raised issues I reviewed and agree with (not re-raised inline):

  • exit 0 on binding drift causes misleading green CI for VM jobs — worth addressing but not blocking.
  • if: always() && !cancelled() on check-bindings can mask missing artifacts from failed upstream jobs — the ::warning:: annotations partially mitigate this.
  • OpenBSD bindings generated with bindgen 0.72.0 vs. 0.72.1 elsewhere — cosmetic; CI will catch real drift.

Comment thread mtu/src/bsd.rs
Comment thread .github/workflows/check-mtu.yml Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Clean, well-motivated change that eliminates the bindgen build-time dependency for the mtu crate by checking in pre-generated platform-specific binding files. This removes the libclang requirement for downstream consumers — a significant ergonomic improvement, especially for the quinn integration.

Approach: The include!(env!("BINDINGS")) pattern is replaced with #[cfg_attr(target_os = "...", path = "bindings/....rs")] module path selection. Bindgen-generated .rs files are committed for all 6 platforms, with CI infrastructure (check-mtu.yml + check-vm) to detect and auto-update drifted bindings. The compile-time size/alignment assertions in each binding file serve as effective safety nets against ABI mismatches.

What looks good:

  • The conditional expect(dead_code) on bsd.rs (excluding NetBSD/Solaris where RTA_IFP is used) is well-reasoned.
  • Version bump to 0.4.0 correctly signals the semver-breaking removal of the gecko feature.
  • The check-bindings CI job with its auto-PR-on-push / fail-on-PR design is a sound workflow for keeping bindings in sync.
  • Header files (bsd.h, linux.h) are retained as documentation of bindgen inputs — good for reproducibility.

Key items from prior reviews that appear addressed in the latest revision:

  • gecko/mozbuild dead code in Cargo.toml — removed.
  • allow vs expect inconsistency — both bsd.rs and linux.rs now use #[expect].
  • PR dedup logic — now uses a fixed branch name (chore/update-mtu-bindings) with force-push.

Remaining open concerns from prior reviews (not re-raised inline):

  • openbsd.rs was generated with bindgen 0.72.0 while all others use 0.72.1 — worth aligning.
  • The exit 0 on binding drift in check-vm means the VM job reports success even when compile/test/clippy are skipped. The check-bindings job catches the drift, but the green VM job status can be misleading. Consider setting a job output or using a distinct exit code.

New observations are in the inline comments.

Comment thread .github/actions/check-vm/action.yml
Comment thread .github/workflows/check-mtu.yml
Comment thread neqo-transport/Cargo.toml Outdated
Comment thread .github/workflows/check-mtu.yml
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark results

Significant performance differences relative to 53527a2.

streams/walltime/1000-streams/each-1-bytes: 💚 Performance has improved by -1.5415%.
       time:   [12.267 ms 12.293 ms 12.322 ms]
       change: [-1.7926% -1.5415% -1.2911] (p = 0.00 < 0.05)
       Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
All results
transfer/1-conn/1-100mb-resp (aka. Download): Change within noise threshold.
       time:   [203.69 ms 204.01 ms 204.40 ms]
       thrpt:  [489.23 MiB/s 490.17 MiB/s 490.95 MiB/s]
change:
       time:   [-0.6173% -0.3254% -0.0539] (p = 0.03 < 0.05)
       thrpt:  [+0.0539% +0.3264% +0.6212]
       Change within noise threshold.
Found 2 outliers among 100 measurements (2.00%)
1 (1.00%) high mild
1 (1.00%) high severe
transfer/1-conn/10_000-parallel-1b-resp (aka. RPS): Change within noise threshold.
       time:   [288.73 ms 290.56 ms 292.41 ms]
       thrpt:  [34.198 Kelem/s 34.416 Kelem/s 34.634 Kelem/s]
change:
       time:   [+0.6852% +1.5116% +2.4324] (p = 0.00 < 0.05)
       thrpt:  [-2.3747% -1.4891% -0.6805]
       Change within noise threshold.
Found 3 outliers among 100 measurements (3.00%)
3 (3.00%) high mild
transfer/1-conn/1-1b-resp (aka. HPS): No change in performance detected.
       time:   [38.800 ms 38.980 ms 39.178 ms]
       thrpt:  [25.525   B/s 25.654   B/s 25.773   B/s]
change:
       time:   [-0.4034% +0.1929% +0.8372] (p = 0.56 > 0.05)
       thrpt:  [-0.8303% -0.1925% +0.4050]
       No change in performance detected.
Found 11 outliers among 100 measurements (11.00%)
2 (2.00%) high mild
9 (9.00%) high severe
transfer/1-conn/1-100mb-req (aka. Upload): No change in performance detected.
       time:   [204.82 ms 205.15 ms 205.50 ms]
       thrpt:  [486.61 MiB/s 487.44 MiB/s 488.24 MiB/s]
change:
       time:   [-0.2780% -0.0054% +0.2553] (p = 0.97 > 0.05)
       thrpt:  [-0.2546% +0.0054% +0.2787]
       No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
1 (1.00%) high mild
1 (1.00%) high severe
streams/walltime/1-streams/each-1000-bytes: Change within noise threshold.
       time:   [586.19 µs 588.79 µs 591.69 µs]
       change: [-1.5296% -0.8228% -0.1029] (p = 0.02 < 0.05)
       Change within noise threshold.
Found 14 outliers among 100 measurements (14.00%)
14 (14.00%) high severe
streams/walltime/1000-streams/each-1-bytes: 💚 Performance has improved by -1.5415%.
       time:   [12.267 ms 12.293 ms 12.322 ms]
       change: [-1.7926% -1.5415% -1.2911] (p = 0.00 < 0.05)
       Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
streams/walltime/1000-streams/each-1000-bytes: Change within noise threshold.
       time:   [44.772 ms 44.818 ms 44.864 ms]
       change: [-1.0873% -0.9301% -0.7734] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
transfer/walltime/pacing-false/varying-seeds: No change in performance detected.
       time:   [81.981 ms 82.034 ms 82.089 ms]
       change: [-0.0764% +0.0354% +0.1543] (p = 0.56 > 0.05)
       No change in performance detected.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
transfer/walltime/pacing-true/varying-seeds: Change within noise threshold.
       time:   [81.604 ms 81.710 ms 81.868 ms]
       change: [+0.1745% +0.3366% +0.5277] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 3 outliers among 100 measurements (3.00%)
1 (1.00%) low mild
1 (1.00%) high mild
1 (1.00%) high severe
transfer/walltime/pacing-false/same-seed: Change within noise threshold.
       time:   [81.072 ms 81.254 ms 81.429 ms]
       change: [-2.0628% -1.8193% -1.5531] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
transfer/walltime/pacing-true/same-seed: Change within noise threshold.
       time:   [81.854 ms 81.950 ms 82.060 ms]
       change: [-1.0100% -0.8811% -0.7328] (p = 0.00 < 0.05)
       Change within noise threshold.
Found 4 outliers among 100 measurements (4.00%)
4 (4.00%) high severe

Download data for profiler.firefox.com or download performance comparison data.

@github-actions

Copy link
Copy Markdown
Contributor

Failed Interop Tests

QUIC Interop Runner, client vs. server, differences relative to main at 7bce87a.

neqo-pr as clientneqo-pr as server
neqo-pr vs. go-x-net: BP BA
neqo-pr vs. haproxy: BP BA
neqo-pr vs. kwik: S L1 C1 BP BA
neqo-pr vs. linuxquic: L1 🚀C1
neqo-pr vs. lsquic: L1 C1
neqo-pr vs. msquic: A L1 C1
neqo-pr vs. mvfst: A BP BA
neqo-pr vs. neqo: Z A
neqo-pr vs. nginx: BP BA
neqo-pr vs. ngtcp2: CM
neqo-pr vs. picoquic: A
neqo-pr vs. quic-go: A
neqo-pr vs. quiche: BP BA
neqo-pr vs. s2n-quic: ⚠️BP BA CM
neqo-pr vs. tquic: S BP BA
neqo-pr vs. xquic: A ⚠️L1 C1
aioquic vs. neqo-pr: Z 🚀L1 CM
go-x-net vs. neqo-pr: CM
kwik vs. neqo-pr: Z BP BA CM
lsquic vs. neqo-pr: Z
msquic vs. neqo-pr: Z CM
mvfst vs. neqo-pr: Z A L1 C1 CM
neqo vs. neqo-pr: Z A
openssl vs. neqo-pr: LR M A CM
picoquic vs. neqo-pr: Z
quic-go vs. neqo-pr: ⚠️Z CM
quic-zig vs. neqo-pr: DC 🚀CM
quiche vs. neqo-pr: Z CM
quinn vs. neqo-pr: Z 🚀C1 V2 CM
s2n-quic vs. neqo-pr: 🚀C1 CM
tquic vs. neqo-pr: Z CM
xquic vs. neqo-pr: M CM
All results

Succeeded Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

Unsupported Interop Tests

QUIC Interop Runner, client vs. server

neqo-pr as client

neqo-pr as server

@github-actions

Copy link
Copy Markdown
Contributor

Client/server transfer results

Performance differences relative to 53527a2.

Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.

Client vs. server (params) Mean ± σ Min Max MiB/s ± σ Δ baseline Δ baseline
neqo-msquic-cubic 149.8 ± 1.9 146.7 155.8 213.6 ± 16.8 💔 0.5 0.3%
neqo-neqo-newreno-nopacing 91.2 ± 2.9 85.9 99.1 350.9 ± 11.0 💔 1.0 1.1%

Table above only shows statistically significant changes. See all results below.

All results

Transfer of 33554432 bytes over loopback, min. 100 runs. All unit-less numbers are in milliseconds.

Client vs. server (params) Mean ± σ Min Max MiB/s ± σ Δ baseline Δ baseline
google-google-nopacing 452.7 ± 3.1 448.1 474.0 70.7 ± 10.3
google-neqo-cubic 266.0 ± 2.8 261.6 278.5 120.3 ± 11.4 0.1 0.0%
msquic-msquic-nopacing 153.7 ± 66.0 112.5 496.0 208.2 ± 0.5
msquic-neqo-cubic 170.7 ± 59.3 119.4 374.8 187.5 ± 0.5 -1.0 -0.6%
neqo-google-cubic 753.9 ± 3.5 748.8 769.4 42.4 ± 9.1 0.2 0.0%
neqo-msquic-cubic 149.8 ± 1.9 146.7 155.8 213.6 ± 16.8 💔 0.5 0.3%
neqo-neqo-cubic 91.0 ± 4.3 85.6 114.4 351.8 ± 7.4 0.4 0.5%
neqo-neqo-cubic-nopacing 90.1 ± 4.2 83.9 122.5 355.3 ± 7.6 0.5 0.5%
neqo-neqo-newreno 90.2 ± 2.3 85.9 96.1 354.9 ± 13.9 -0.5 -0.6%
neqo-neqo-newreno-nopacing 91.2 ± 2.9 85.9 99.1 350.9 ± 11.0 💔 1.0 1.1%
neqo-quiche-cubic 188.8 ± 2.7 184.9 196.5 169.5 ± 11.9 -0.7 -0.3%
neqo-s2n-cubic 215.7 ± 2.5 210.7 227.5 148.3 ± 12.8 0.2 0.1%
quiche-neqo-cubic 173.5 ± 4.3 168.4 204.8 184.4 ± 7.4 -0.6 -0.3%
quiche-quiche-nopacing 138.6 ± 2.4 133.4 149.3 230.9 ± 13.3
s2n-neqo-cubic 213.9 ± 3.3 207.4 224.8 149.6 ± 9.7 0.3 0.1%
s2n-s2n-nopacing 290.1 ± 19.6 279.6 389.4 110.3 ± 1.6

Download data for profiler.firefox.com or download performance comparison data.

@larseggert larseggert merged commit 824b15d into mozilla:main Apr 15, 2026
191 checks passed
@larseggert larseggert deleted the feat-mtu-no-bindgen branch April 16, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants