summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-27 15:53:05 +0100
committerMark Brown <broonie@kernel.org>2026-07-27 15:53:05 +0100
commit7a4d8f4733f8bf98677c8562a7477f32fb507ec7 (patch)
treebfdf0d64102778875fda5eb51b889fb9e6dc9e4f /rust
parent58a63fb3b3e074080cd9c29a3b7cb55ebfa5ab3e (diff)
parent0957fbab972a9499626ac457fd924b24491c6315 (diff)
downloadlinux-next-7a4d8f4733f8bf98677c8562a7477f32fb507ec7.tar.gz
linux-next-7a4d8f4733f8bf98677c8562a7477f32fb507ec7.zip
Merge branch 'char-misc-next' of https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
# Conflicts: # rust/kernel/task.rs
Diffstat (limited to 'rust')
-rw-r--r--rust/bindings/bindings_helper.h3
-rw-r--r--rust/helpers/helpers.c1
-rw-r--r--rust/helpers/net/genetlink.c46
-rw-r--r--rust/kernel/miscdevice.rs2
-rw-r--r--rust/kernel/net/mod.rs (renamed from rust/kernel/net.rs)2
-rw-r--r--rust/kernel/net/netlink.rs337
-rw-r--r--rust/kernel/sync/poll.rs73
-rw-r--r--rust/kernel/task.rs7
-rw-r--r--rust/uapi/uapi_helper.h1
9 files changed, 470 insertions, 2 deletions
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..ec96e811610d 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -93,6 +93,8 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
+#include <net/genetlink.h>
+#include <net/netlink.h>
#include <trace/events/rust_sample.h>
/*
@@ -110,6 +112,7 @@
const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN = ARCH_KMALLOC_MINALIGN;
const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE;
+const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE = GENLMSG_DEFAULT_SIZE;
const gfp_t RUST_CONST_HELPER_GFP_ATOMIC = GFP_ATOMIC;
const gfp_t RUST_CONST_HELPER_GFP_KERNEL = GFP_KERNEL;
const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT = GFP_KERNEL_ACCOUNT;
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 998e31052e66..1d4ee51f576b 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -72,6 +72,7 @@
#include "maple_tree.c"
#include "mm.c"
#include "mutex.c"
+#include "net/genetlink.c"
#include "of.c"
#include "page.c"
#include "pci.c"
diff --git a/rust/helpers/net/genetlink.c b/rust/helpers/net/genetlink.c
new file mode 100644
index 000000000000..3530b69f6cf7
--- /dev/null
+++ b/rust/helpers/net/genetlink.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2026 Google LLC.
+ */
+
+#include <net/genetlink.h>
+
+#ifdef CONFIG_NET
+
+__rust_helper struct sk_buff *rust_helper_genlmsg_new(size_t payload, gfp_t flags)
+{
+ return genlmsg_new(payload, flags);
+}
+
+__rust_helper
+int rust_helper_genlmsg_multicast(const struct genl_family *family,
+ struct sk_buff *skb, u32 portid,
+ unsigned int group, gfp_t flags)
+{
+ return genlmsg_multicast(family, skb, portid, group, flags);
+}
+
+__rust_helper void rust_helper_genlmsg_cancel(struct sk_buff *skb, void *hdr)
+{
+ genlmsg_cancel(skb, hdr);
+}
+
+__rust_helper void rust_helper_genlmsg_end(struct sk_buff *skb, void *hdr)
+{
+ genlmsg_end(skb, hdr);
+}
+
+__rust_helper void rust_helper_nlmsg_free(struct sk_buff *skb)
+{
+ nlmsg_free(skb);
+}
+
+__rust_helper
+int rust_helper_genl_has_listeners(const struct genl_family *family,
+ struct net *net, unsigned int group)
+{
+ return genl_has_listeners(family, net, group);
+}
+
+#endif
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 83ce50def5ac..3abef1b8543d 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -289,7 +289,7 @@ impl<T: MiscDevice> MiscdeviceVTable<T> {
/// # Safety
///
/// `kiocb` must be correspond to a valid file that is associated with a
- /// `MiscDeviceRegistration<T>`. `iter` must be a valid `struct iov_iter` for writing.
+ /// `MiscDeviceRegistration<T>`. `iter` must be a valid `struct iov_iter` for reading.
unsafe extern "C" fn write_iter(
kiocb: *mut bindings::kiocb,
iter: *mut bindings::iov_iter,
diff --git a/rust/kernel/net.rs b/rust/kernel/net/mod.rs
index fe415cb369d3..8ecae7577ed2 100644
--- a/rust/kernel/net.rs
+++ b/rust/kernel/net/mod.rs
@@ -4,3 +4,5 @@
#[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)]
pub mod phy;
+
+pub mod netlink;
diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs
new file mode 100644
index 000000000000..22ef3dde36fa
--- /dev/null
+++ b/rust/kernel/net/netlink.rs
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2026 Google LLC.
+
+//! Rust support for generic netlink.
+//!
+//! Currently only supports exposing multicast groups.
+//!
+//! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h)
+
+use kernel::{
+ alloc::{self, AllocError},
+ error::to_result,
+ prelude::*,
+ transmute::AsBytes,
+ types::Opaque,
+ ThisModule,
+};
+
+use core::{
+ mem::ManuallyDrop,
+ ptr::NonNull, //
+};
+
+/// The default netlink message size.
+pub const GENLMSG_DEFAULT_SIZE: usize = bindings::GENLMSG_DEFAULT_SIZE;
+
+/// A wrapper around `struct sk_buff` for generic netlink messages.
+///
+/// This type is intended to be specific for buffers used with netlink only, and other usecases for
+/// `struct sk_buff` are out-of-scope for this abstraction.
+///
+/// # Invariants
+///
+/// The pointer has ownership over a valid `sk_buff`.
+pub struct NetlinkSkBuff {
+ skb: NonNull<kernel::bindings::sk_buff>,
+}
+
+impl NetlinkSkBuff {
+ /// Creates a new `NetlinkSkBuff` with the given size.
+ pub fn new(size: usize, flags: alloc::Flags) -> Result<NetlinkSkBuff, AllocError> {
+ // SAFETY: `genlmsg_new` only requires its arguments to be valid integers.
+ let skb = unsafe { bindings::genlmsg_new(size, flags.as_raw()) };
+ let skb = NonNull::new(skb).ok_or(AllocError)?;
+ Ok(NetlinkSkBuff { skb })
+ }
+
+ /// Puts a generic netlink header into the `NetlinkSkBuff`.
+ pub fn genlmsg_put(
+ self,
+ portid: u32,
+ seq: u32,
+ family: &'static Family,
+ cmd: u8,
+ ) -> Result<GenlMsg, AllocError> {
+ let skb = self.skb.as_ptr();
+ // SAFETY: The skb and family pointers are valid.
+ let hdr = unsafe { bindings::genlmsg_put(skb, portid, seq, family.as_raw(), 0, cmd) };
+ let hdr = NonNull::new(hdr).ok_or(AllocError)?;
+ Ok(GenlMsg { skb: self, hdr })
+ }
+}
+
+impl Drop for NetlinkSkBuff {
+ fn drop(&mut self) {
+ // SAFETY: We have ownership over the `sk_buff`, so we may free it.
+ unsafe { bindings::nlmsg_free(self.skb.as_ptr()) }
+ }
+}
+
+/// A generic netlink message being constructed.
+///
+/// # Invariants
+///
+/// `hdr` references the header in this netlink message.
+pub struct GenlMsg {
+ skb: NetlinkSkBuff,
+ hdr: NonNull<c_void>,
+}
+
+impl GenlMsg {
+ /// Puts an attribute into the message.
+ #[inline]
+ fn put<T>(&mut self, attrtype: c_int, value: &T) -> Result
+ where
+ T: ?Sized + AsBytes,
+ {
+ let skb = self.skb.skb.as_ptr();
+ let len = size_of_val(value);
+ let ptr = core::ptr::from_ref(value).cast::<c_void>();
+ // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is
+ // readable and initialized for its `size_of` bytes.
+ to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) })
+ }
+
+ /// Puts a `u32` attribute into the message.
+ #[inline]
+ pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result {
+ self.put(attrtype, &value)
+ }
+
+ /// Puts a string attribute into the message.
+ #[inline]
+ pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result {
+ self.put(attrtype, value.to_bytes_with_nul())
+ }
+
+ /// Puts a flag attribute into the message.
+ #[inline]
+ pub fn put_flag(&mut self, attrtype: c_int) -> Result {
+ let skb = self.skb.skb.as_ptr();
+ // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and a null pointer is valid
+ // when the length is zero.
+ to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::null()) })
+ }
+
+ /// Sends the generic netlink message as a multicast message.
+ #[inline]
+ pub fn multicast(
+ self,
+ family: &'static Family,
+ portid: u32,
+ group: u32,
+ flags: alloc::Flags,
+ ) -> Result {
+ let me = ManuallyDrop::new(self);
+ // SAFETY: The `skb` and `family` pointers are valid. We pass ownership of the `skb` to
+ // `genlmsg_multicast` by not dropping `self`.
+ unsafe {
+ bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr());
+ to_result(bindings::genlmsg_multicast(
+ family.as_raw(),
+ me.skb.skb.as_ptr(),
+ portid,
+ group,
+ flags.as_raw(),
+ ))
+ }
+ }
+}
+impl Drop for GenlMsg {
+ fn drop(&mut self) {
+ // SAFETY: The `hdr` pointer references the header of this generic netlink message.
+ unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.as_ptr()) };
+ }
+}
+
+/// Flags for a generic netlink family.
+struct FamilyFlags {
+ /// Whether the family supports network namespaces.
+ netnsok: bool,
+ /// Whether the family supports parallel operations.
+ parallel_ops: bool,
+}
+
+impl FamilyFlags {
+ /// Converts the flags to the bitfield representation used by `genl_family`.
+ const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1]> {
+ // The below shifts are verified correct by test_family_flags_bitfield() below.
+ //
+ // Although bindgen generates helpers to change bitfields based on the C headers, these
+ // helpers unfortunately can't be used in const context. Since `Family` needs to be filled
+ // out at build-time, we use this helper instead.
+ let mut bits = 0;
+ if self.netnsok {
+ bits |= 1 << 0;
+ }
+ if self.parallel_ops {
+ bits |= 1 << 1;
+ }
+ // Convert from little endian to the target's endianness.
+ bits = u8::from_le(bits);
+ // SAFETY: This bitfield is represented as an u8.
+ unsafe { core::mem::transmute::<u8, bindings::__BindgenBitfieldUnit<[u8; 1]>>(bits) }
+ }
+}
+
+/// A generic netlink family.
+#[repr(transparent)]
+pub struct Family {
+ inner: Opaque<bindings::genl_family>,
+}
+
+// SAFETY: The `Family` type is thread safe.
+unsafe impl Sync for Family {}
+
+impl Family {
+ /// Creates a new `Family` instance.
+ ///
+ /// Intended to be used from const context only. Will panic if provided with invalid arguments.
+ ///
+ /// The name must be a nul-terminated string, but it is taken as `&[u8]` so that it can be used
+ /// more conveniently with the strings generated by bindgen.
+ pub const fn const_new(
+ module: &ThisModule,
+ name: &[u8],
+ version: u32,
+ mcgrps: &'static [MulticastGroup],
+ ) -> Family {
+ let n_mcgrps = mcgrps.len() as u8;
+ if n_mcgrps as usize != mcgrps.len() {
+ panic!("too many mcgrps");
+ }
+ let mut genl_family = bindings::genl_family {
+ version,
+ _bitfield_1: FamilyFlags {
+ netnsok: true,
+ parallel_ops: true,
+ }
+ .into_bitfield(),
+ module: module.as_ptr(),
+ mcgrps: mcgrps.as_ptr().cast(),
+ n_mcgrps,
+ ..pin_init::zeroed()
+ };
+ if CStr::from_bytes_with_nul(name).is_err() {
+ panic!("genl_family name not nul-terminated");
+ }
+ if genl_family.name.len() < name.len() {
+ panic!("genl_family name too long");
+ }
+ let mut i = 0;
+ while i < name.len() {
+ genl_family.name[i] = name[i];
+ i += 1;
+ }
+ Family {
+ inner: Opaque::new(genl_family),
+ }
+ }
+
+ /// Checks if there are any listeners for the given multicast group.
+ pub fn has_listeners(&self, group: u32) -> bool {
+ // SAFETY: The family and init_net pointers are valid.
+ unsafe {
+ bindings::genl_has_listeners(self.as_raw(), &raw mut bindings::init_net, group) != 0
+ }
+ }
+
+ /// Returns a raw pointer to the underlying `genl_family` structure.
+ pub fn as_raw(&self) -> *mut bindings::genl_family {
+ self.inner.get()
+ }
+}
+
+/// A generic netlink multicast group.
+#[repr(transparent)]
+pub struct MulticastGroup {
+ // No Opaque because fully immutable
+ group: bindings::genl_multicast_group,
+}
+
+// SAFETY: Pure data so thread safe.
+unsafe impl Sync for MulticastGroup {}
+
+impl MulticastGroup {
+ /// Creates a new `MulticastGroup` instance.
+ ///
+ /// Intended to be used from const context only. Will panic if provided with invalid arguments.
+ pub const fn const_new(name: &CStr) -> MulticastGroup {
+ let mut group: bindings::genl_multicast_group = pin_init::zeroed();
+
+ let name = name.to_bytes_with_nul();
+ if group.name.len() < name.len() {
+ panic!("genl_multicast_group name too long");
+ }
+ let mut i = 0;
+ while i < name.len() {
+ group.name[i] = name[i];
+ i += 1;
+ }
+
+ MulticastGroup { group }
+ }
+}
+
+/// A registration of a generic netlink family.
+///
+/// This type represents the registration of a [`Family`]. When an instance of this type is
+/// dropped, its respective generic netlink family will be unregistered from the system.
+///
+/// # Invariants
+///
+/// `self.family` always holds a valid reference to an initialized and registered [`Family`].
+pub struct Registration {
+ family: &'static Family,
+}
+
+impl Family {
+ /// Registers the generic netlink family with the kernel.
+ pub fn register(&'static self) -> Result<Registration> {
+ // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` struct.
+ // The `genl_family` struct is static, so it will outlive the registration.
+ to_result(unsafe { bindings::genl_register_family(self.as_raw()) })?;
+ Ok(Registration { family: self })
+ }
+}
+
+impl Drop for Registration {
+ fn drop(&mut self) {
+ // SAFETY: `self.family.as_raw()` is a valid pointer to a registered `genl_family` struct.
+ // The `Registration` struct ensures that `genl_unregister_family` is called exactly once
+ // for this family when it goes out of scope.
+ unsafe { bindings::genl_unregister_family(self.family.as_raw()) };
+ }
+}
+
+#[macros::kunit_tests(rust_netlink)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_family_flags_bitfield() {
+ for netnsok in [false, true] {
+ for parallel_ops in [false, true] {
+ let mut b_fam = bindings::genl_family {
+ ..Default::default()
+ };
+ b_fam.set_netnsok(if netnsok { 1 } else { 0 });
+ b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 });
+
+ let c_bitfield = FamilyFlags {
+ netnsok,
+ parallel_ops,
+ }
+ .into_bitfield();
+
+ // SAFETY: The bit field is stored as u8.
+ let b_val: u8 = unsafe { core::mem::transmute(b_fam._bitfield_1) };
+ // SAFETY: The bit field is stored as u8.
+ let c_val: u8 = unsafe { core::mem::transmute(c_bitfield) };
+ assert_eq!(b_val, c_val);
+ }
+ }
+ }
+}
diff --git a/rust/kernel/sync/poll.rs b/rust/kernel/sync/poll.rs
index 0ec985d560c8..684dfa242b1a 100644
--- a/rust/kernel/sync/poll.rs
+++ b/rust/kernel/sync/poll.rs
@@ -5,12 +5,18 @@
//! Utilities for working with `struct poll_table`.
use crate::{
+ alloc::AllocError,
bindings,
fs::File,
prelude::*,
sync::{CondVar, LockClassKey},
+ types::Opaque, //
+};
+use core::{
+ marker::PhantomData,
+ mem::ManuallyDrop,
+ ops::Deref, //
};
-use core::{marker::PhantomData, ops::Deref};
/// Creates a [`PollCondVar`] initialiser with the given name and a newly-created lock class.
#[macro_export]
@@ -66,6 +72,7 @@ impl<'a> PollTable<'a> {
///
/// [`CondVar`]: crate::sync::CondVar
#[pin_data(PinnedDrop)]
+#[repr(transparent)]
pub struct PollCondVar {
#[pin]
inner: CondVar,
@@ -104,3 +111,67 @@ impl PinnedDrop for PollCondVar {
unsafe { bindings::synchronize_rcu() };
}
}
+
+/// A [`KBox<PollCondVar>`] that uses `kfree_rcu`.
+///
+/// [`KBox<PollCondVar>`]: PollCondVar
+pub struct PollCondVarBox {
+ inner: ManuallyDrop<Pin<KBox<PollCondVarBoxInner>>>,
+}
+
+#[pin_data]
+#[repr(C)]
+struct PollCondVarBoxInner {
+ #[pin]
+ inner: PollCondVar,
+ rcu: Opaque<bindings::callback_head>,
+}
+
+// SAFETY: PollCondVar is Send
+unsafe impl Send for PollCondVarBoxInner {}
+// SAFETY: PollCondVar is Sync
+unsafe impl Sync for PollCondVarBoxInner {}
+
+impl PollCondVarBox {
+ /// Constructs a new boxed [`PollCondVar`].
+ pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> Result<Self, AllocError> {
+ let b = KBox::pin_init(
+ pin_init!(PollCondVarBoxInner {
+ inner <- PollCondVar::new(name, key),
+ rcu: Opaque::uninit(),
+ }),
+ GFP_KERNEL,
+ )
+ .map_err(|_| AllocError)?;
+
+ Ok(PollCondVarBox {
+ inner: ManuallyDrop::new(b),
+ })
+ }
+}
+
+impl Deref for PollCondVarBox {
+ type Target = PollCondVar;
+ fn deref(&self) -> &PollCondVar {
+ &self.inner.inner
+ }
+}
+
+impl Drop for PollCondVarBox {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: ManuallyDrop::take ok because not already taken.
+ let boxed = unsafe { ManuallyDrop::take(&mut self.inner) };
+
+ // SAFETY: The code below frees the box without calling the actual destructor of the type,
+ // but it's okay because it re-implements the destructor using `kfree_rcu()` in place of
+ // `synchronize_rcu()`.
+ let ptr = KBox::into_raw(unsafe { Pin::into_inner_unchecked(boxed) });
+
+ // SAFETY: The pointer points at a valid `wait_queue_head`.
+ unsafe { bindings::__wake_up_pollfree((*ptr).inner.inner.wait_queue_head.get()) };
+
+ // SAFETY: This was allocated using `KBox::pin_init`, so it can be freed with `kvfree`.
+ unsafe { bindings::kvfree_call_rcu((*ptr).rcu.get(), ptr.cast::<ffi::c_void>()) };
+ }
+}
diff --git a/rust/kernel/task.rs b/rust/kernel/task.rs
index c2b3457b700c..3336df493dec 100644
--- a/rust/kernel/task.rs
+++ b/rust/kernel/task.rs
@@ -210,6 +210,13 @@ impl Task {
unsafe { *ptr::addr_of!((*self.as_ptr()).pid) }
}
+ /// Returns the TGID (Thread Group ID / Process ID) of the given task.
+ pub fn tgid(&self) -> Pid {
+ // SAFETY: The tgid of a task never changes after initialization, so reading this field is
+ // not a data race.
+ unsafe { *ptr::addr_of!((*self.as_ptr()).tgid) }
+ }
+
/// Returns the objective real UID of the given task.
#[inline]
pub fn uid(&self) -> Kuid {
diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h
index 06d7d1a2e8da..86c7b6b284b0 100644
--- a/rust/uapi/uapi_helper.h
+++ b/rust/uapi/uapi_helper.h
@@ -11,6 +11,7 @@
#include <uapi/drm/nova_drm.h>
#include <uapi/drm/panthor_drm.h>
#include <uapi/linux/android/binder.h>
+#include <uapi/linux/android/binder_netlink.h>
#include <uapi/linux/mdio.h>
#include <uapi/linux/mii.h>
#include <uapi/linux/ethtool.h>