Files
James Tucker f55fd4e9e4 util/cpucaps, tsweb/varz: extract and extend GOAMD64 and CPU capability detection
Move the goamd64_capable/goamd64_compiled logic out of tsweb/varz
into a new util/cpucaps package so that a future hostinfo extension
can share it, and extend it to all supported platforms.

The host GOAMD64 level is now computed from CPUID feature bits via
x/sys/cpu rather than by parsing /proc/cpuinfo, which removes the
linux/amd64 restriction and all file I/O. CPUID is also what the Go
runtime checks at startup, so it directly answers the question the
metric asks: could this machine run a binary built at a higher level.

Four flags required by the runtime checks have no x/sys/cpu field:
LAHF/SAHF (v2) and MOVBE, LZCNT and F16C (v3). We report a level when
all of its other flags are present. That is exact on real hardware:
LAHF/SAHF exists on every 64-bit CPU that also has SSE4.2, and
MOVBE/LZCNT/F16C exist on every CPU that also has AVX2, BMI2 and FMA
(Haswell and Zen onwards). A linux/amd64 test cross-checks the CPUID
result against the exact /proc/cpuinfo flag lists, so a test run on
any machine where the assumption fails, or where the kernel's view
diverges from CPUID (e.g. clearcpuid=), reports the difference.

Add a Caps bitmask of the CPU capabilities that Go's runtime and
standard library dispatch on, normalized across amd64 and arm64: AES,
CLMUL/PMULL, SHA2, SHA512, CRC32, ADX and LSE, plus the vector
capabilities not implied by the GOAMD64 level: AVX, AVX2, AVX512 (the
v4 F+BW+CD+DQ+VL subset), VAES, VPCLMULQDQ and GFNI. Detection is
lazy and reads only x/sys/cpu globals and build info; there is no
init-time work and no new dependency.

Coverage includes all key client platforms: linux, windows, darwin,
freebsd, android (via the linux auxv/MRS path in x/sys/cpu) and ios
(via the darwin sysctl path). x/sys/cpu has no arm64 detection on
FreeBSD, so read ID_AA64ISAR0_EL1 directly; the FreeBSD kernel
emulates EL0 reads of the ID registers, and Go's runtime already
depends on that emulation on this platform.

The whole feature is behind a new cpucaps feature tag. Building with
ts_omit_cpucaps removes the varz metrics and all detection code
following the tka stub pattern: the detection functions compile to
stubs returning zero inside the package itself, so future importers
cannot reintroduce the code by forgetting a build tag, and the Caps
type stays available for decoding recorded values. A tailscaled
dependency test enforces the omission, and the generated
buildfeatures.HasCPUCaps const is available for the later hostinfo
extension.

tsweb/varz metric names are unchanged. gauge_goamd64_capable values
only change on machines where /proc/cpuinfo disagrees with CPUID.

Updates #21002

Signed-off-by: James Tucker <james@tailscale.com>
2026-08-26 18:31:31 -07:00
..
2026-07-10 17:39:16 -07:00