[PATCH 4/9] rust: xxhash: add a safe xxh64 wrapper

From: Mike Lothian

Date: Wed Aug 26 2026 - 12:33:41 EST


Expose xxh64() through a slice-based Rust API for non-cryptographic
hashing such as framebuffer damage detection.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <mike@xxxxxxxxxxxxxx>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/lib.rs | 1 +
rust/kernel/xxhash.rs | 68 +++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)
create mode 100644 rust/kernel/xxhash.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..2d079f278a04 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -93,6 +93,7 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
+#include <linux/xxhash.h>
#include <trace/events/rust_sample.h>

/*
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f39648246271..d7ced2a4c11f 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -141,6 +141,7 @@
pub mod usb;
pub mod workqueue;
pub mod xarray;
+pub mod xxhash;

#[doc(hidden)]
pub use bindings;
diff --git a/rust/kernel/xxhash.rs b/rust/kernel/xxhash.rs
new file mode 100644
index 000000000000..fca24cddef46
--- /dev/null
+++ b/rust/kernel/xxhash.rs
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! xxHash, a fast non-cryptographic hash function.
+//!
+//! C header: [`include/linux/xxhash.h`](srctree/include/linux/xxhash.h)
+
+use core::mem::MaybeUninit;
+
+use crate::{bindings, error::to_result, prelude::*};
+
+/// Incremental xxHash64 state.
+///
+/// Use this when the input is split across several buffers. Calling
+/// [`update`](Self::update) for each buffer produces the same digest as hashing
+/// their concatenation with [`xxh64`].
+pub struct Xxh64(bindings::xxh64_state);
+
+impl Xxh64 {
+ /// Start a new hash with `seed`.
+ pub fn new(seed: u64) -> Self {
+ let mut state = MaybeUninit::uninit();
+ // SAFETY: `xxh64_reset()` initializes every byte of the state before
+ // returning.
+ unsafe {
+ bindings::xxh64_reset(state.as_mut_ptr(), seed);
+ Self(state.assume_init())
+ }
+ }
+
+ /// Add `data` to the hash.
+ pub fn update(&mut self, data: &[u8]) -> Result {
+ // SAFETY: `self.0` is initialized and exclusively borrowed; `data` is
+ // valid for reads of `data.len()` bytes.
+ to_result(unsafe { bindings::xxh64_update(&mut self.0, data.as_ptr().cast(), data.len()) })
+ }
+
+ /// Return the current digest without consuming the state.
+ pub fn digest(&self) -> u64 {
+ // SAFETY: `self.0` was initialized by `xxh64_reset()` and remains live.
+ unsafe { bindings::xxh64_digest(&self.0) }
+ }
+}
+
+/// Returns the 64-bit xxHash of `data`, starting from `seed`.
+///
+/// This is a non-cryptographic hash: use it for change detection, bucketing and similar, never for
+/// anything security-relevant.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::xxhash::xxh64;
+///
+/// // The same input and seed always produce the same hash.
+/// assert_eq!(xxh64(b"hello", 0), xxh64(b"hello", 0));
+///
+/// // Different seeds produce different hashes.
+/// assert_ne!(xxh64(b"hello", 0), xxh64(b"hello", 1));
+///
+/// // Hashing the empty slice is well-defined.
+/// let _ = xxh64(&[], 0);
+/// ```
+#[inline]
+pub fn xxh64(data: &[u8], seed: u64) -> u64 {
+ // SAFETY: `data` is a valid slice, so its pointer is valid for reads of `data.len()` bytes,
+ // and `xxh64()` only reads from it for the duration of the call.
+ unsafe { bindings::xxh64(data.as_ptr().cast(), data.len(), seed) }
+}