From nobody Mon May 4 13:04:19 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 63074356A08 for ; Mon, 4 May 2026 09:05:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885511; cv=none; b=p4Q+HZWPUHE7wJk/Bhl4bZl3hBSPWxrrlWI+WGPJMD9D1Ibe1fU36N69JGr91rH5mSk5HVUsLM9eGiqeeTXigrY/aKQo2/6PE3B3d/cRtFu7kX0xpn4FMc6ksDKCatFW1R4W5Rw+gEVkhaXoVXOZjDtG+x6yuwaUuAzDhFv73pE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885511; c=relaxed/simple; bh=aNab5duyaOPqhLDWZ8/VUW/u5s/n1TryMWaLJDhduV0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=fq21tXSTUdwzyNzuSLF6D3rYj54SMAVqpP4EfG2qREYo24P/mw6Hkzibu8FGpS86b86o3wFHY4pd1PTr6UCg/Yqrucsfmecpx/EF6avgpOuAeBnEFz2CVfQkNflpznmVf/Z7JfSqTqmQcABEzepJQ9/vPhlGNSUuCBNdSswgbRg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=LLYqteSE; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="LLYqteSE" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-488d1b5bca0so19691475e9.2 for ; Mon, 04 May 2026 02:05:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777885508; x=1778490308; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Gv8OdnGsAYLQJgROgPIvQlwmVjXR+mbfDvwidHE+2tU=; b=LLYqteSEuTTSNuVjPesxBV5ttGVqcl6WyR3nQ/jAV7rhQkj+6deFM1n1Jyp3k4qBeU 2aJYNT9cqB9a2hRZn+3NfS6t/RU8wkY5I21N3DWW/wqsfaGMA1qyCjDUFpOTMxOaG6ha HgUs8atouH6Q/pS1ysy04XyrKKTRssAr8hKCy5/LRB1b9+ov1UOtuY5vJBuPwMCC7bRL xC2xtjGbVqEZY57JxfBj4EGOJpBtlZzptZflOyul5mRVEj2C9ZOC2lw9Mzc1enuJHbpi kuCz019slVEP/4ePPpnkAfJ/jHxZIRf+paCE1B35ek0OZp4pnJHLhCdgqTomys7c4p0d vf7g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777885508; x=1778490308; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Gv8OdnGsAYLQJgROgPIvQlwmVjXR+mbfDvwidHE+2tU=; b=OkLONZojqvjX8vRXODPIVSg6tlEDBVvcms4vzevbl3MRJ+bBzR1TTzfj9Uepymsgoi 808PGsGxznyjuLrPreH+jXcbdivYllhu0kcJI0Ta5ouN0nU0wquYZt2uUQCf9zFaI7Ge mcxNFt5l882Ij7DiPYAYbGQkhIflWmaBs2jv5qZSg6F6P2PwaPKvCeXX7CDgekMoe2hU v+U0Z5CHTwaHR/dT4wtFsuBzqyHTshAEWXxPCaC55cOM5YkDPrtNS9twoPLgn330R8XP wF6oyBb3HIyyAOjElzL3NLCaYn8+8H4BU13WMLp0zUQ0dS7qTl+6fSPKeqzhGI8ciyer BX4A== X-Forwarded-Encrypted: i=1; AFNElJ9tU8cFAWrVAsNo5d9eKi47AYJd4KQkcgk3moLQz3J9xtuvUYHxakFy5wcYnI0mdU18dOPskmCehYkCkoU=@vger.kernel.org X-Gm-Message-State: AOJu0YzCWPmhUhzUqYf5/wN+GbFlWrEKwdnxNWH7xRh5s6VYxCg0xhgx jaRIMCS0sWg1U8wgzNtqikq5VUF6/roZhWXwTPlz0pFnAvIkM/g4v6M8BIH8MtfENyFDaKFN/zK o/b/LBnTGkmwjywRGJg== X-Received: from wmog7.prod.google.com ([2002:a05:600c:3107:b0:488:a71c:cf48]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600d:8408:b0:48a:56de:d63c with SMTP id 5b1f17b1804b1-48a988bdd98mr112245025e9.27.1777885507550; Mon, 04 May 2026 02:05:07 -0700 (PDT) Date: Mon, 04 May 2026 09:04:54 +0000 In-Reply-To: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=15720; i=aliceryhl@google.com; h=from:subject:message-id; bh=aNab5duyaOPqhLDWZ8/VUW/u5s/n1TryMWaLJDhduV0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBp+GFArgBvHeCoG0CT5V5kqdaMSYj9i9yhkXSY4 FWpfhfT+LiJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCafhhQAAKCRAEWL7uWMY5 RsSFD/9kAHCxW0POTk4wO56oYHpkbeISckGvEPme2/AZOqgQwcUUiCvHCp0rvnjffJ2HKKNwMyc Mc81yHY1cuY83rpskXyH14ooYHQRM7KD2D2+NUbFcXcA5yfrmg7tiLJg46m984cUGilhZcCPGRk ElN65d4pGydvTv7xt90opOvsmcUq8TGEsuVbKm4mBPyrteWBODwv1rQv1u63l0+ly1fcyO4qhLX e5zj4mvfkgO63/BajNgsuB1SohqtKJmaKbUHjJ+AOfGwyy8rVgNE/63hbOWSV0PaNXVndtHzsm3 28C+aK8Jn8t3Fe6uslSpNchAN6v2tEDXbZ0LMImjlXBdIJMVPgoVLM/dcV0gqBrVesNjDb5qqzb qG2EUydqXuq0CINalGmsVcz8Xw1i8H+AVMYkxhI30STVvl4Xgyp8EmmG/fhE5utgPln7C4x4LMf 745mX5RK+4PeeTBPxgf6XPVB3FjF0h2WxUhsPVxLjUOYezkbkLHLkLSJANQMpi6ogjI6JeRaBph ehBSPs18hd6F/Pkm8K5LkAvIiBxTeC/krL1NTATGl2DwSy/RIWBdn0BRSM2EwFN4qWyeLoVmuVX 9Vluj52DAAPlMy2vLOnd5TFelsQ5tA48qK0/qB1a7oYoJ5oq8SWMk4XPo9FOjm7iO/qS2rjHdf2 K8bImVJ177M0RSQ== X-Mailer: b4 0.14.3 Message-ID: <20260504-binder-netlink-v4-1-601b41cd25b2@google.com> Subject: [PATCH v4 1/4] rust: netlink: add raw netlink abstraction From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This implements a safe and relatively simple API over the netlink API, that allows you to add different attributes to a netlink message and broadcast it. As the first user of this API only makes use of broadcast, only broadcast messages are supported here. This API is intended to be safe and to be easy to use in *generated* code. This is because netlink is generally used with yaml files that describe the underlying API, and the python generator outputs C code (or, soon, Rust code) that lets you use the API more easily. So for example, if there is a string field, the code generator will output a method that internall calls `put_string()` with the right attr type. Reviewed-by: Matthew Maurer Reviewed-by: Andrew Lunn Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 3 + rust/helpers/genetlink.c | 46 ++++++ rust/helpers/helpers.c | 1 + rust/kernel/lib.rs | 1 + rust/kernel/netlink.rs | 336 ++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 387 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 446dbeaf0866..612fa5388b7d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -92,6 +92,8 @@ #include #include #include +#include +#include #include =20 /* @@ -109,6 +111,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN =3D ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN =3D ARCH_KMALLOC_MINA= LIGN; const size_t RUST_CONST_HELPER_PAGE_SIZE =3D PAGE_SIZE; +const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE =3D GENLMSG_DEFAULT_SI= ZE; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC =3D GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL =3D GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT =3D GFP_KERNEL_ACCOUNT; diff --git a/rust/helpers/genetlink.c b/rust/helpers/genetlink.c new file mode 100644 index 000000000000..3530b69f6cf7 --- /dev/null +++ b/rust/helpers/genetlink.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2026 Google LLC. + */ + +#include + +#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 *h= dr) +{ + 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/helpers/helpers.c b/rust/helpers/helpers.c index 625921e27dfb..8de05ae7d928 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -62,6 +62,7 @@ #include "err.c" #include "irq.c" #include "fs.c" +#include "genetlink.c" #include "gpu.c" #include "io.c" #include "jump_label.c" diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b72b2fbe046d..d69f13b77845 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -96,6 +96,7 @@ pub mod module_param; #[cfg(CONFIG_NET)] pub mod net; +pub mod netlink; pub mod num; pub mod of; #[cfg(CONFIG_PM_OPP)] diff --git a/rust/kernel/netlink.rs b/rust/kernel/netlink.rs new file mode 100644 index 000000000000..24177fb685b4 --- /dev/null +++ b/rust/kernel/netlink.rs @@ -0,0 +1,336 @@ +// 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) +#![cfg(CONFIG_NET)] + +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 =3D 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 onl= y, 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, +} + +impl NetlinkSkBuff { + /// Creates a new `NetlinkSkBuff` with the given size. + pub fn new(size: usize, flags: alloc::Flags) -> Result { + // SAFETY: `genlmsg_new` only requires its arguments to be valid i= ntegers. + let skb =3D unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; + let skb =3D 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 { + let skb =3D self.skb.as_ptr(); + // SAFETY: The skb and family pointers are valid. + let hdr =3D unsafe { bindings::genlmsg_put(skb, portid, seq, famil= y.as_raw(), 0, cmd) }; + let hdr =3D 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, +} + +impl GenlMsg { + /// Puts an attribute into the message. + #[inline] + fn put(&mut self, attrtype: c_int, value: &T) -> Result + where + T: ?Sized + AsBytes, + { + let skb =3D self.skb.skb.as_ptr(); + let len =3D size_of_val(value); + let ptr =3D core::ptr::from_ref(value).cast::(); + // 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 =3D 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 =3D ManuallyDrop::new(self); + // SAFETY: The `skb` and `family` pointers are valid. We pass owne= rship 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_fa= mily`. + const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1= ]> { + // The below shifts are verified correct by test_family_flags_bitf= ield() below. + // + // Although bindgen generates helpers to change bitfields based on= the C headers, these + // helpers unfortunately can't be used in const context. Since `Fa= mily` needs to be filled + // out at build-time, we use this helper instead. + let mut bits =3D 0; + if self.netnsok { + bits |=3D 1 << 0; + } + if self.parallel_ops { + bits |=3D 1 << 1; + } + // SAFETY: This bitfield is represented as an u8. + unsafe { core::mem::transmute::>(bits) } + } +} + +/// A generic netlink family. +#[repr(transparent)] +pub struct Family { + inner: Opaque, +} + +// 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 provide= d 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 =3D mcgrps.len() as u8; + if n_mcgrps as usize !=3D mcgrps.len() { + panic!("too many mcgrps"); + } + let mut genl_family =3D 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 =3D 0; + while i < name.len() { + genl_family.name[i] =3D name[i]; + i +=3D 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) !=3D 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 provide= d with invalid arguments. + pub const fn const_new(name: &CStr) -> MulticastGroup { + let mut group: bindings::genl_multicast_group =3D pin_init::zeroed= (); + + let name =3D name.to_bytes_with_nul(); + if group.name.len() < name.len() { + panic!("genl_multicast_group name too long"); + } + let mut i =3D 0; + while i < name.len() { + group.name[i] =3D name[i]; + i +=3D 1; + } + + MulticastGroup { group } + } +} + +/// A registration of a generic netlink family. +/// +/// This type represents the registration of a [`Family`]. When an instanc= e of this type is +/// dropped, its respective generic netlink family will be unregistered fr= om the system. +/// +/// # Invariants +/// +/// `self.family` always holds a valid reference to an initialized and reg= istered [`Family`]. +pub struct Registration { + family: &'static Family, +} + +impl Family { + /// Registers the generic netlink family with the kernel. + pub fn register(&'static self) -> Result { + // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` s= truct. + // The `genl_family` struct is static, so it will outlive the regi= stration. + 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 register= ed `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 =3D 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 =3D FamilyFlags { + netnsok, + parallel_ops, + } + .into_bitfield(); + + // SAFETY: The bit field is stored as u8. + let b_val: u8 =3D unsafe { core::mem::transmute(b_fam._bit= field_1) }; + // SAFETY: The bit field is stored as u8. + let c_val: u8 =3D unsafe { core::mem::transmute(c_bitfield= ) }; + assert_eq!(b_val, c_val); + } + } + } +} --=20 2.54.0.545.g6539524ca2-goog From nobody Mon May 4 13:04:19 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C81ED359A8E for ; Mon, 4 May 2026 09:05:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885512; cv=none; b=JMxAD+SQfyfie1EXjqrz07SeYy4wkTggkGk7+FEANxWS6hcqCzpxKKeU6JalGUxnefrVEwEv8tJUwAotxOnAOzkAcdG6TwWwT+Fu53eM5rI35r41wL5XH3dYDZ5GgRQszxblSnSy/5sX52uuGgTNFkQ26vVDBIL1wo7IGHMkKPE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885512; c=relaxed/simple; bh=1S5dJfUyYQg5FGS0pNOidUwpCbERfnNKxzgJNlBKE/0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=m0bhkD+XHeOdP6Z2cxcOFc9Vjzu5jjPgPW+V7eDpkBEQsN9JDrDij+nnRqPzLIfLivksu/Qp5WsqOwVTL4Q6egOyRyv1dPQuddGUFcz9HhovEWQrgUwo4md2wKj6sLPAKD4SFNoopLcRKT0LiqV+QdEJVLKVNSu4Y955S7KBtDw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=nuIhZL48; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="nuIhZL48" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-48d046fac74so5462015e9.3 for ; Mon, 04 May 2026 02:05:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777885509; x=1778490309; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Wv8M0pobvUQaNrtbaUGlGWjxtYtpJJO/kHh47aIpTxM=; b=nuIhZL48M287glH8oqWbxp8Yv4Ee1UQ2iY7CC+SUKBTYv6YdgNycfQMKTMbmpG//BR 4Doc3eje02zIGjpfkoR0nGZK4den3vp92qwtNrReaO35rfb8KODm+EVF878TwENwXXrV c6M1WZMa9nlCIeq23AOjr4BfaTLgrXCqczrndxXWb7zDAsmIVOAI5s4KBdqAn5Tp2A9O alopnJms8tTErwGY7yU1UpnB3vvz4H9ilDyXzf+mY+RVQWlZFCsw/u1z2jB/SdwBpXLX 5W3eDvm6u4F+RLLTv2MTqli4/BU1r1RwEg74C3NBALtq5S+oZKhhTlh9KVBH1FFNNGMh BtDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777885509; x=1778490309; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Wv8M0pobvUQaNrtbaUGlGWjxtYtpJJO/kHh47aIpTxM=; b=L5l3oyummngmjvyzDxRjCt3uhzqVX5OyDc/piaQ/AMhthZmzFZiAc3vMlZOSD8Omch HE/35aIefDyTqDW3bfRK5V1ssyELBWYYKGnEQ7TKU8qQ701taWYzSGgUDIfpd+veo1XJ 7H6vRRrpzL1G0gmDpOLASj8p87Nx5MiVd2d2cSjjKJysNe70PvMvs59kD0dJn64rrzUY /gz92Rr6DLMJXxWmgvgMM+85h2BjcnYj+GoejWMmEoj1sKiNe6EDbnTKe8D53YdCGpkU +OhQMR3WkHLu5ATmvHLofN2lOw+CiBQrTEoIAdL2AxS9fs7thRx+j4lnPz6FXstcxgy0 yfpw== X-Forwarded-Encrypted: i=1; AFNElJ/Rx//X6qdtnUSQkHl28/yBZmanmriJ/7bKD8vtDEnGCom9OVZjXKzsTr1c7UDqzYoen0dR4jZH4HouPKU=@vger.kernel.org X-Gm-Message-State: AOJu0YybwJSU/erUB8BFG0LKazL4RwbIGW7Ce+b06maoBiMKK8hxoSMV WAXlx7qJz+mtQQXTTAbaGpcfhkCJA3TE0Ujgpa8bg4bC0D9PxseXIUrxSiAvko9fYsTB7Z7b5/K yMXE7BEcxCi/cSoCFEg== X-Received: from wmdn1.prod.google.com ([2002:a05:600c:2941:b0:488:890d:75d7]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:c089:b0:489:149a:f9e6 with SMTP id 5b1f17b1804b1-48a98676a8fmr117917645e9.28.1777885509261; Mon, 04 May 2026 02:05:09 -0700 (PDT) Date: Mon, 04 May 2026 09:04:55 +0000 In-Reply-To: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7880; i=aliceryhl@google.com; h=from:subject:message-id; bh=1S5dJfUyYQg5FGS0pNOidUwpCbERfnNKxzgJNlBKE/0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBp+GFAAPLFLzqtCpP86GYCDu+7ToXARljhdNPj8 KQu6ewaxriJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCafhhQAAKCRAEWL7uWMY5 RsyvD/9kn34McFuAkRYhAZhMD68+cF2W+FqMGBON2dgQbossmGSlTAiCf+SmdwRTQ0ioGP2dRIA xC604C9nUO1b35pQ3t7WXI5dCfP6iw2SiBN+t7CgLb8Tilp7xzoZ4bFRkjy2K4wEhWZVRefpk/j Xgm9zn3mX5J0pM/pyeuSsg7gILQO9E1xLJVGEkiGGAnKLwiqk+2nVu+E65o0vq5D+SivyZmQbeL P6xqRaKl21Afh45W6ran2RlVeSX4yxeLwCdP0aPO+n5rpIcA24z9NQ6o+28oc3F+ueAoOVoKC0O 23StIOEAeI0IDKXM5Q+2KVIec6jJlRqdlAs0+Qra32D9rBHUIs2aCheCXZXyUDTkq+aqjgbqO3y VLcMQYSX2NnFXLJ/JS7jYB3kFKCnVX+YQgMxNrIzVCMNB1DWvizLhljdPW8qt/uWp4skAgyz4zX oA8w0lIHYk2e4C5snrn3A1dVLsOrpWD4xgMhZVU3Qy3UHetWD/p8djQ9N1hz/bXTUXFfIA9ild9 UUDAcd/pfUhOUwkyKDcjTJYGt0hFY883JvCC6nPL9rKqVVVE9Nfo6uhmpEd2XkeBS84WyoZC6pr aNs3tlZFQ7EmIvJl4qhuIsrgkAoB1SvsLeIP7zRDEEdVxTusd/i9QAWdHvPsJ82WHRnEu9YYSCX rMcCdSmpaQsCbpg== X-Mailer: b4 0.14.3 Message-ID: <20260504-binder-netlink-v4-2-601b41cd25b2@google.com> Subject: [PATCH v4 2/4] ynl_gen: generate Rust files from yaml files From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To generate netlink frames from Rust code easily, generate Rust libraries with methods for generating different netlink messages as appropriate. The new 'rust' type corresponds to a Rust version of the C target 'kernel'. There is no Rust version of the 'uapi' target since Rust code exports its uapi via C headers - choice of language is opaque to userspace. This logic is kept in the existing ynl_gen_c.py file to reuse CodeWriter and other shared pieces of logic in the existing python file. This has the disadvantage that the gen_c part of the name is now wrong, as it also generates Rust. One possible solution to this could be to rename the file. Signed-off-by: Alice Ryhl --- tools/net/ynl/pyynl/ynl_gen_c.py | 139 +++++++++++++++++++++++++++++++++++= +++- tools/net/ynl/ynl-regen.sh | 2 +- 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen= _c.py index 0e1e486c1185..76b8b2f1ac16 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -19,6 +19,7 @@ import pathlib import os import re import shutil +import subprocess import sys import tempfile import yaml as pyyaml @@ -1744,6 +1745,19 @@ class CodeWriter: else: self.p('}' + line) =20 + def array_start(self, line=3D''): + if line: + line =3D line + ' ' + self.p(line + '[') + self._ind +=3D 1 + + def array_end(self, line=3D''): + if line and line[0] not in {';', ','}: + line =3D ' ' + line + self._ind -=3D 1 + self._nl =3D False + self.p(']' + line) + def write_doc_line(self, doc, indent=3DTrue): words =3D doc.split() line =3D ' *' @@ -3415,10 +3429,126 @@ def find_kernel_root(full_path): return full_path, sub_path[:-1] =20 =20 +def render_rust(family, cw): + cw.p('#![allow(unreachable_pub, clippy::wrong_self_convention)]') + cw.p('use kernel::netlink::{Family, MulticastGroup};') + cw.p('use kernel::prelude::*;') + cw.nl() + + family_upper =3D c_upper(family.ident_name) + family_name =3D f'{family_upper}_NL_FAMILY' + mcgrps_name =3D f'{family_name}_MCGRPS' + + cw.p(f'pub static {family_name}: Family =3D Family::const_new(') + cw._ind +=3D 1 + cw.p('&crate::THIS_MODULE,') + cw.p(f'kernel::uapi::{family.fam_key},') + cw.p(f'kernel::uapi::{family.ver_key},') + if family.mcgrps['list']: + cw.p(f'&{mcgrps_name},') + else: + cw.p('&[],') + cw._ind -=3D 1 + cw.p(');') + cw.nl() + + if family.mcgrps['list']: + cw.array_start(f'static {mcgrps_name}: [MulticastGroup; {len(famil= y.mcgrps["list"])}] =3D ') + for grp in family.mcgrps['list']: + cw.p(f'MulticastGroup::const_new(c"{grp["name"]}"),') + cw.array_end(';') + cw.nl() + + for idx, (op_name, op) in enumerate(item for item in family.msgs.items= () if 'event' in item[1]): + struct_name =3D op_name.capitalize() + + if 'doc' in op: + doc_lines =3D op['doc'].strip().split('\n') + for line in doc_lines: + cw.p(f'/// {line.strip()}') + + cw.block_start(f'pub struct {struct_name}') + cw.p('skb: kernel::netlink::GenlMsg,') + cw.block_end() + cw.nl() + + cw.block_start(f'impl {struct_name}') + cw.p('/// Create a new multicast message.') + cw.p('pub fn new(') + cw._ind +=3D 1 + cw.p('size: usize,') + cw.p('portid: u32,') + cw.p('seq: u32,') + cw.p('flags: kernel::alloc::Flags,') + cw._ind -=3D 1 + cw.block_start(') -> Result') + cw.p(f'const {op.enum_name}: u8 =3D kernel::uapi::{op.enum_name} a= s u8;') + cw.p('let skb =3D kernel::netlink::NetlinkSkBuff::new(size, flags)= ?;') + cw.p(f'let skb =3D skb.genlmsg_put(portid, seq, &{family_name}, {o= p.enum_name})?;') + cw.p('Ok(Self { skb })') + cw.block_end() + cw.nl() + + grp_idx =3D 0 + if 'mcgrp' in op: + grp_idx =3D next(i for i, grp in enumerate(family.mcgrps['list= ']) if grp['name'] =3D=3D op['mcgrp']) + + cw.p('/// Broadcast this message.') + cw.block_start('pub fn multicast(self, portid: u32, flags: kernel:= :alloc::Flags) -> Result') + cw.p(f'self.skb.multicast(&{family_name}, portid, {grp_idx}, flags= )') + cw.block_end() + cw.nl() + + cw.p('/// Check if this message type has listeners.') + cw.block_start('pub fn has_listeners() -> bool') + cw.p(f'{family_name}.has_listeners({grp_idx})') + cw.block_end() + + attr_set_name =3D op['attribute-set'] + attr_set =3D family.attr_sets[attr_set_name] + event_attrs =3D op['event']['attributes'] + + for attr_name in event_attrs: + attr =3D attr_set[attr_name] + method_name =3D attr_name.replace('-', '_') + + if attr.type =3D=3D 'u32': + put_fn =3D 'put_u32' + arg_str =3D ', val' + method_args =3D '(&mut self, val: u32)' + elif attr.type =3D=3D 'string': + put_fn =3D 'put_string' + arg_str =3D ', val' + method_args =3D '(&mut self, val: &CStr)' + elif attr.type =3D=3D 'flag': + put_fn =3D 'put_flag' + arg_str =3D '' + method_args =3D '(&mut self)' + else: + put_fn =3D f'put_{attr.type}' + arg_str =3D ', val' + method_args =3D f'(&mut self, val: {attr.type})' + + cw.nl() + if 'doc' in attr.yaml: + doc_lines =3D attr.yaml['doc'].strip().split('\n') + for line in doc_lines: + cw.p(f'/// {line.strip()}') + + cw.block_start(f'pub fn {method_name}{method_args} -> Result') + cw.p(f'const {attr.enum_name}: c_int =3D kernel::uapi::{attr.e= num_name} as c_int;') + cw.p(f'self.skb.{put_fn}({attr.enum_name}{arg_str})') + cw.block_end() + + cw.block_end() + cw.nl() + cw.p(' ') + + def main(): parser =3D argparse.ArgumentParser(description=3D'Netlink simple parsi= ng generator') parser.add_argument('--mode', dest=3D'mode', type=3Dstr, required=3DTr= ue, - choices=3D('user', 'kernel', 'uapi')) + choices=3D('user', 'kernel', 'uapi', 'rust')) parser.add_argument('--spec', dest=3D'spec', type=3Dstr, required=3DTr= ue) parser.add_argument('--header', dest=3D'header', action=3D'store_true'= , default=3DNone) parser.add_argument('--source', dest=3D'header', action=3D'store_false= ') @@ -3471,6 +3601,13 @@ def main(): render_uapi(parsed, cw) return =20 + if args.mode =3D=3D 'rust': + render_rust(parsed, cw) + cw.close_out_file() + if args.out_file: + subprocess.run(['rustfmt', '--edition', '2021', args.out_file]) + return + hdr_prot =3D f"_LINUX_{parsed.c_name.upper()}_GEN_H" if args.header: cw.p('#ifndef ' + hdr_prot) diff --git a/tools/net/ynl/ynl-regen.sh b/tools/net/ynl/ynl-regen.sh index d9809276db98..4f5ceb4fe147 100755 --- a/tools/net/ynl/ynl-regen.sh +++ b/tools/net/ynl/ynl-regen.sh @@ -17,7 +17,7 @@ done KDIR=3D$(dirname $(dirname $(dirname $(dirname $(realpath $0))))) pushd ${search:-$KDIR} >>/dev/null =20 -files=3D$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user= \)') +files=3D$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user= \|rust\)') for f in $files; do # params: 0 1 2 3 # $YAML YNL-GEN kernel $mode --=20 2.54.0.545.g6539524ca2-goog From nobody Mon May 4 13:04:19 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B35E3590A4 for ; Mon, 4 May 2026 09:05:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885514; cv=none; b=mMqDGWlP9PBIsUOqnSZzKsaIVzTsYfMqQTThWEufOqsB8RJtMRuRtKHK0to3225b2Optcmh30RD+4C707aCkxBL34S9KG9lIXrOt6/R08lr+AMtMbWMdOvo9wZ5b+Lf7pZ5JPqFaLpeLJ5pHy8q8XSuT2wawS3x/2UHUIkOWhiI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885514; c=relaxed/simple; bh=0A6XAfvCdt6W/6IIVvLzFfQVMMJf2MNw3synGGt9C+8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=ulq2Wm1UUC00VIXFO2b0HtL/e9qL1Rfgf1QhfOk4PXVOqOtFdMwXm3n+SqhC5+AJeepHyNPQ6T3aE0/JH32FICmC4+C2U9xGHuTNDHCgDVPUZpvq01akeRYgz4OhsMcfqEPwComVjz5QXLD7+auStLRHSW8UT6E1pJmgfFfkd+I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=Aoy0Ld5N; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="Aoy0Ld5N" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-488e097a270so22636275e9.1 for ; Mon, 04 May 2026 02:05:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777885511; x=1778490311; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WxjmzDKG680fOnwjQkgR5+9rnq3DYmiVavVZFpaNns8=; b=Aoy0Ld5N9RvDeHFF+0wn78sLUJgbq92QXP4Nkf2TwrOT1Dqp8KxEQ2IuAGEycd2Cnx Db5fv/Dc67ZWoCBamCOVpQF+GlaRqvZycrVU7Ud/zVPGIEzp+KUtIxCN1grJSPTD/BZW uhd1Yk83WhEYnkqeQl+3e4PVzsmo1nJTGHhGk4fY3A36lw7sld/zyRoHH3tOgVbbCbny 13R5R1LkJFE/tVt8Ni+htYfz6mQF6PJhY9Q9u2kNry2R67S5rqtGx3m5byJiR3iC5IUk 6rX6EYQbn4uCQ22xuXkToV78ZEh5Yl9WszwMuM2Bl3YKI0Wp9Bai/mFL4r8PKysOb8CY CW/Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777885511; x=1778490311; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=WxjmzDKG680fOnwjQkgR5+9rnq3DYmiVavVZFpaNns8=; b=EXef7k+sulnyz4Rrb3gXRkITZJi55477PfLf9e/jrrrbgcjpsB9W7WA7glG2wZXItx foEA+h5LUlGXVaIkrUBCYW5KLn9T1kFWAyHD2Xab0bags861WgctqxOKVN+RgvNnlJZ6 gUKgsoBUlJLK3u73D5wEC2doWG0DLLBEAB0vMSLFn+199PMKlxPKkcWiMMBH6OcPauAA lwVZcZU3dxgcAoKZObO0XNVKXcqAhX5LEv718MzMjtQRGkCit5c7m02FXnJSgfpmSwsg CMikWw5SEoyULI++EBK6Ia9fmWHyod/DPt30xWMBKTj6V2/akuezabsU42ciwG+QgyXZ 3f5A== X-Forwarded-Encrypted: i=1; AFNElJ/7pnPpviG7LbaXDaZCMO2yVXPnr5N/l0T/LNZfe/SVW+dgLKGzZh/ySwtqS6d6ZrsxUKRigALFSYtB3sk=@vger.kernel.org X-Gm-Message-State: AOJu0YwrSRYtMqjOLN7wLFaSFaGI/K343eA/2LwcmukhlQQZ/9/ChzLx i9BaFXdqxY5IfVKoNss2nnyCt5G9teucQ+TRCU11XGVkvblODeKfqtPRmmuYDYKa6qEptDjgolx ayHa4h9WWqletDogLWA== X-Received: from wmdd13.prod.google.com ([2002:a05:600c:a20d:b0:48a:66a8:9970]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:a011:b0:48a:8cb6:88b9 with SMTP id 5b1f17b1804b1-48a988be2b3mr140212885e9.22.1777885510778; Mon, 04 May 2026 02:05:10 -0700 (PDT) Date: Mon, 04 May 2026 09:04:56 +0000 In-Reply-To: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7772; i=aliceryhl@google.com; h=from:subject:message-id; bh=0A6XAfvCdt6W/6IIVvLzFfQVMMJf2MNw3synGGt9C+8=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBp+GFAnX+ozWqi83GnI7z5xxQzfxq1FXvnAuFBZ ro/hEDwmtqJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCafhhQAAKCRAEWL7uWMY5 RhRUD/oCb7ZGuVAnoCa76M5ZkVRtq3ZzgMhM9cOaZAKFJmk8Iga7Y+Hlh/WxjPGmj5v3LZznrEY upyyhilIY4PL351k68v2mx0GUUBC0eKxE/uUWMf8d7J9YxbS2mJ0aPelkeHVDNtM49mhl9VvGX3 FW04Nz05cUbEY6aguFyHUeTwA19FqXm02MRbOd16CzFAiGWKIx7V/Vg3JJtZTTG2rvq8WsZAJuZ 5XC77IHX/PM0j8OWVDhtr3/7j4VFP+b9JZW5L0ITJJqTnvy/IMNL32sXGNqsIoVK1imW45bfKdR 4GLzIRmxcvGxfj7Adn8FxkFrOuKpFZUDvwda03YRkGVfBKnquskcT6vTpre2VpAkcFqRA2VUMI/ 9FuzdcmhwWUoGqG+3cEKMOoEmLr1uNUDtnkJ71xaLaQvOsb80kxz73+Vtq+zgPOh74JuPNTpigp FZ9GSBiqqZ09B0sal6YmfoKVYqOzKrTYTAtmdyLewbBlSdMzojXtXcO9f8itMDqkunlDGOb/swA t9yU3g/A0mO+/qMdsjHtjOlIHx3VJnzp5Vx2hHHt2/IDHk/ojgYZME3h9wXMB8L33AZ/8V2f7KZ gc5DbPFNGuMn+l2bwgnshdP4N0yRbIqwfluH28ysKTZDV7ihFqqYsOITSK5grn8S6tDaPiwzTxf Sz2G+ArQpUhh1ag== X-Mailer: b4 0.14.3 Message-ID: <20260504-binder-netlink-v4-3-601b41cd25b2@google.com> Subject: [PATCH v4 3/4] rust_binder: add generated netlink.rs file From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To use netlink from Rust Binder, add a new generated netlink file using the new script and Documentation/netlink/specs/binder.yaml. Signed-off-by: Alice Ryhl --- drivers/android/Kconfig | 2 +- drivers/android/binder/netlink.rs | 113 +++++++++++++++++++++++++= ++++ drivers/android/binder/rust_binder_main.rs | 9 ++- rust/uapi/uapi_helper.h | 1 + 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC =20 config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. =20 diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/net= link.rs new file mode 100644 index 000000000000..2a842c7b1b33 --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/binder.yaml */ +/* YNL-GEN rust source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::netlink::{Family, MulticastGroup}; +use kernel::prelude::*; + +pub static BINDER_NL_FAMILY: Family =3D Family::const_new( + &crate::THIS_MODULE, + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] =3D [MulticastGroup::c= onst_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: kernel::netlink::GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 =3D kernel::uapi::BINDER_CMD_REPORT as= u8; + let skb =3D kernel::netlink::NetlinkSkBuff::new(size, flags)?; + let skb =3D skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER= _CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Re= sult { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int =3D kernel::uapi::BINDER_A_REPO= RT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int =3D kernel::uapi::BINDER_A_RE= PORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int =3D kernel::uapi::BINDER_A_R= EPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int =3D kernel::uapi::BINDER_A_REPO= RT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int =3D kernel::uapi::BINDER_A_REPOR= T_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int =3D kernel::uapi::BINDER_A_= REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/b= inder/rust_binder_main.rs index dc1941cd2407..2cb9f4897ad7 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -38,6 +38,8 @@ mod deferred_close; mod defs; mod error; +#[allow(dead_code)] +mod netlink; mod node; mod page_range; mod process; @@ -288,19 +290,22 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker =3D unsafe { Shrinker::new() }; =20 -struct BinderModule {} +struct BinderModule { + _netlink: kernel::netlink::Registration, +} =20 impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only cal= l this once. unsafe { crate::context::CONTEXTS.init() }; =20 + let netlink =3D crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; =20 // SAFETY: The module is being loaded, so we can initialize binder= fs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())?= }; =20 - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } =20 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 #include #include +#include #include #include #include --=20 2.54.0.545.g6539524ca2-goog From nobody Mon May 4 13:04:19 2026 Received: from mail-wr1-f74.google.com (mail-wr1-f74.google.com [209.85.221.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 64BDA35B64D for ; Mon, 4 May 2026 09:05:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885516; cv=none; b=Wxnolqj/ZjjFP2pj35x6kIpT43Bvm1CnZ5RHD63i9ObZjOxiPj4GBr6bIGGr0C3tPNBT3O8ywQuJg2TMyul3aWX0Tly5DiKHszPyTCu96sJnlWSChih3A9BPxB59PjrdOJWENgmYkINLjZv7xdwXkJpyCGUp9mbLo2d+03C7sJY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777885516; c=relaxed/simple; bh=1wCvLKojE5VMmDHf90lahs0vrb8enbEkI/Ogt8PvM1o=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=eLh8ILYpCYEJ6hltI7lJzp1m63mJbWw9URHQoyJx6I0966ezENTyvg+ewh8/3C5o8wBnglVifQ6OOhMn/h767dnv8LNV569g2UNwUPVwhETWYc8O4/SjSSTq03GvLVBzAdh1jYVez80P//OnDt2oY6hsk1c16le5X3pv11xPzQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=gzprFKxl; arc=none smtp.client-ip=209.85.221.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="gzprFKxl" Received: by mail-wr1-f74.google.com with SMTP id ffacd0b85a97d-4411a2c034fso2921291f8f.3 for ; Mon, 04 May 2026 02:05:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1777885513; x=1778490313; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=SEY60xtnrtZlmDpZYjCBRIROfxsrlUrdOXIjmLSez0o=; b=gzprFKxlRQyvj2uWo3GNgiXetnceccJwv5KR2Airyb7j6lb4K5s+Olg4IzwdoUU2KN GRrcnQz4O23u60/7nttMAZg801QDIA5OFFAyI6IZSN3kcY64fznxY4dSXE3rurJvP4Ku kiRU0z5n6W1g4oQEA6GnxclOIwPguV/Fu3alkdXbLSauQwUBZqmXwlleRH2I/TtfYip+ 71zl5teC5i6SBGft87PDY3aPVUsZfK3SamXwdv9WQvaaX2N7/Fka2vboVDEkBpCRA7Az zDSUrtPWZqWzIPODdkFtX5+6lAtDcsjkGMY7xw/4SA+awOou1VmICYam5ENiJ1KJP1qD Ylpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777885513; x=1778490313; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=SEY60xtnrtZlmDpZYjCBRIROfxsrlUrdOXIjmLSez0o=; b=Q8CdWly+murhujgOk4NyYbkt0CHnToJW5y/93PYOLlbZyvq/83JL5NK/6AxgMaJx0s E5uZ8rZaH7OSuqxF/+eSH7AEMBuVV8smOkszFm1zrKHA/+wfm9HTTUnd1Rg0NVxAJaoE i1FbGJAEYjP0ix/4B0RR3uP6XnuNp3+Uz5Ob+mRcakgeLVYTEWOBLAHax7FmZ0waD0B7 LIoeQyosV9f3PtATZtJ25Wdc7ZUmkpH42NYkFmFZ1ZZPzYT1HxqBCeUJH7En8lTVNo1M Oji/5dHdk5xIsxaqeZ8l+0Xmh5rD9XNuYOrp4CsMZqI2WjVQ8jl+RIrS9+dWbHyfGcsI kiMw== X-Forwarded-Encrypted: i=1; AFNElJ8o5BE4gxsn97YMm8732errieosFUwo4kKg/YZgH5Dvi7aZ25UnEDMv0QMlLTkai8QuoiUHIBmQq1K2OSY=@vger.kernel.org X-Gm-Message-State: AOJu0Yyz2vuEkSKWe0WnZRyA2J0GEHZD0j2KsHSyyKhlkZhNrgrCBsow 0fD5kq+EKQg5zJF/HpiuWnKS0mlLHkBJtmhSu8BxFg7O63a0OjPsezhLYMOYkVtJgd7jAP3ONRs hFciyb12LNRNdFU6ArA== X-Received: from wrom11.prod.google.com ([2002:adf:f38b:0:b0:43c:ffef:4ab3]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6000:4287:b0:43d:70de:1c70 with SMTP id ffacd0b85a97d-44bb68c79a0mr14978714f8f.32.1777885512373; Mon, 04 May 2026 02:05:12 -0700 (PDT) Date: Mon, 04 May 2026 09:04:57 +0000 In-Reply-To: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260504-binder-netlink-v4-0-601b41cd25b2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=4353; i=aliceryhl@google.com; h=from:subject:message-id; bh=e73WyOyB+mDDLHLibrQ4vjOLyE9SKfri82WTOXEFb3Q=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBp+GFAKMS1St3zC0Se2iWAaH7evFPI7zqntrHGm yKRSVTsnyuJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCafhhQAAKCRAEWL7uWMY5 RuE7D/4vIMshog5wxMxLVYiT2zWIQW2eTJ05nCquFUkoBrITs6advjQOzEQrZHoF9fHbIplNp9I FbW5IN/0my1AtE1ONhqX3/eyjNgciX51/ZnVfdIopMg8x9TaWPzPhX7rYmBTzTVcqhBK7vsvuKZ rnkx9+zNGM2nBKXojrv/wKlp0trlIHIwyvyw9qF16kwhxNc3PH2iYEnQHCSG8i7zmhxW3lowhdR n9u97rczFLXnvg1jglpSeS1XiBAJNIYuenCukVFKIUJ11wpTemZaNmW9X+b8NdLkqE171YuqPBw TDhjqRKhqggNp9XYTObqhL7j+oJ3ZWijzoRiEFpqZEqdhYVY35Ywr8qQUxzecvhhJ9DfDSHUYqx raN2hse1xy3xE9ukK+C2E1ceNA6z0zqeFxAbjkhZwgsO7vmW2m9i800GboaaiMaztBfM44Dph/2 d4XhDrBuuVoexePsU8IobI0qGjEJkDPnFvCcTEC3qpEvWywGqWprSGIjTcjhrAegb5rcrw/AyNf TgZSc1PK8yZFMAE+y4O4e/s/xjurPkYRAFJ+oTzyHBn2oBMYj2fHU+EEo4bSegwsw9XCMc88XEJ wDKdqKGtpI10ztMb6mlboCu36IQ7OpG8xmViyX8bJ8yNuG4hurOPwSF/3syZ1+Hiec2lbfcP8FR jGeLc+huavhQpGw== X-Mailer: b4 0.14.3 Message-ID: <20260504-binder-netlink-v4-4-601b41cd25b2@google.com> Subject: [PATCH v4 4/4] rust_binder: report netlink transactions From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Carlos Llamas The Android Binder driver supports a netlink API that reports transaction *failures* to a userspace daemon. This allows devices to monitor processes with many failed transactions so that it can e.g. kill misbehaving apps. One very important thing that this monitors is when many oneway messages are sent to a frozen process, so there is special handling to ensure this scenario is surfaced over netlink. Signed-off-by: Carlos Llamas Signed-off-by: Alice Ryhl --- drivers/android/binder/rust_binder_main.rs | 1 - drivers/android/binder/thread.rs | 10 ++++++++ drivers/android/binder/transaction.rs | 40 ++++++++++++++++++++++++++= ++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/b= inder/rust_binder_main.rs index 2cb9f4897ad7..bbef68993b8d 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -38,7 +38,6 @@ mod deferred_close; mod defs; mod error; -#[allow(dead_code)] mod netlink; mod node; mod page_range; diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thre= ad.rs index 97d5f31e8fe3..aa4e93a877ac 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1263,6 +1263,15 @@ fn transaction(self: &Arc, cmd: u32, reader: &= mut UserSliceReader) -> Resu } } =20 + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we rep= ort it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_P= ENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply !=3D 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } =20 @@ -1332,6 +1341,7 @@ fn reply_inner(self: &Arc, info: &mut Transacti= onInfo) -> BinderResult { ); let reply =3D Err(BR_FAILED_REPLY); orig.from.deliver_reply(reply, &orig); + info.reply =3D BR_FAILED_REPLY; err.reply =3D BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder= /transaction.rs index 47d5e4d88b07..3fa7091ed8a6 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. =20 use kernel::{ + netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -17,6 +18,7 @@ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -49,6 +51,44 @@ impl TransactionInfo { pub(crate) fn is_oneway(&self) -> bool { self.flags & TF_ONE_WAY !=3D 0 } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) =3D self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> ke= rnel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report =3D Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KER= NEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid !=3D 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid !=3D 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(self.flags)?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) + } } =20 use core::mem::offset_of; --=20 2.54.0.545.g6539524ca2-goog