summaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
authorFilipe Xavier <felipeaggger@gmail.com>2026-01-17 07:41:25 -0300
committerMiguel Ojeda <ojeda@kernel.org>2026-02-02 08:09:11 +0100
commit0e62e4f3e56cf6c44926db2ee82ff29b4a28ac03 (patch)
tree951bec16cfad5c0f86c3cd6fc20909855f2b3a0f /rust/kernel
parent08afcc38a64cec3d6065b90391afebfde686a69a (diff)
rust: add `impl_flags!` macro for defining common bitflag operations
We have seen a proliferation of `mod_whatever::foo::Flags` being defined with essentially the same implementation for `BitAnd`, `BitOr`, `.contains()` etc. This macro aims to bring a solution for this, allowing to generate these methods for user-defined structs. With some use cases in KMS and upcoming GPU drivers. Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/We.20really.20need.20a.20common.20.60Flags.60.20type Suggested-by: Daniel Almeida <daniel.almeida@collabora.com> Suggested-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Tested-by: Andreas Hindborg <a.hindborg@kernel.org> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org> Signed-off-by: Filipe Xavier <felipeaggger@gmail.com> Link: https://patch.msgid.link/20260117-feat-add-bitmask-macro-v9-1-45ea1f00f846@gmail.com [ Implemented missing `BitXorAssign<$flag> for $flags`. Sorted `impl`s. Removed prelude addition for now -- I asked the team and they also felt it wasn't needed. We can always add it later on if needed. Fixed intra-doc link (by removing the sentence since it was superfluous anyway). Simplified `empty()` title. Reworded commit slightly. Added docs to enum variants in example to avoid 'missing_docs' lint when used in actual code. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/impl_flags.rs272
-rw-r--r--rust/kernel/lib.rs2
2 files changed, 274 insertions, 0 deletions
diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs
new file mode 100644
index 000000000000..e2bd7639da12
--- /dev/null
+++ b/rust/kernel/impl_flags.rs
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bitflag type generator.
+
+/// Common helper for declaring bitflag and bitmask types.
+///
+/// This macro takes as input:
+/// - A struct declaration representing a bitmask type
+/// (e.g., `pub struct Permissions(u32)`).
+/// - An enumeration declaration representing individual bit flags
+/// (e.g., `pub enum Permission { ... }`).
+///
+/// And generates:
+/// - The struct and enum types with appropriate `#[repr]` attributes.
+/// - Implementations of common bitflag operators
+/// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.).
+/// - Utility methods such as `.contains()` to check flags.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::impl_flags;
+///
+/// impl_flags!(
+/// /// Represents multiple permissions.
+/// #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+/// pub struct Permissions(u32);
+///
+/// /// Represents a single permission.
+/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// pub enum Permission {
+/// /// Read permission.
+/// Read = 1 << 0,
+///
+/// /// Write permission.
+/// Write = 1 << 1,
+///
+/// /// Execute permission.
+/// Execute = 1 << 2,
+/// }
+/// );
+///
+/// // Combine multiple permissions using the bitwise OR (`|`) operator.
+/// let mut read_write: Permissions = Permission::Read | Permission::Write;
+/// assert!(read_write.contains(Permission::Read));
+/// assert!(read_write.contains(Permission::Write));
+/// assert!(!read_write.contains(Permission::Execute));
+/// assert!(read_write.contains_any(Permission::Read | Permission::Execute));
+/// assert!(read_write.contains_all(Permission::Read | Permission::Write));
+///
+/// // Using the bitwise OR assignment (`|=`) operator.
+/// read_write |= Permission::Execute;
+/// assert!(read_write.contains(Permission::Execute));
+///
+/// // Masking a permission with the bitwise AND (`&`) operator.
+/// let read_only: Permissions = read_write & Permission::Read;
+/// assert!(read_only.contains(Permission::Read));
+/// assert!(!read_only.contains(Permission::Write));
+///
+/// // Toggling permissions with the bitwise XOR (`^`) operator.
+/// let toggled: Permissions = read_only ^ Permission::Read;
+/// assert!(!toggled.contains(Permission::Read));
+///
+/// // Inverting permissions with the bitwise NOT (`!`) operator.
+/// let negated = !read_only;
+/// assert!(negated.contains(Permission::Write));
+/// assert!(!negated.contains(Permission::Read));
+/// ```
+#[macro_export]
+macro_rules! impl_flags {
+ (
+ $(#[$outer_flags:meta])*
+ $vis_flags:vis struct $flags:ident($ty:ty);
+
+ $(#[$outer_flag:meta])*
+ $vis_flag:vis enum $flag:ident {
+ $(
+ $(#[$inner_flag:meta])*
+ $name:ident = $value:expr
+ ),+ $( , )?
+ }
+ ) => {
+ $(#[$outer_flags])*
+ #[repr(transparent)]
+ $vis_flags struct $flags($ty);
+
+ $(#[$outer_flag])*
+ #[repr($ty)]
+ $vis_flag enum $flag {
+ $(
+ $(#[$inner_flag])*
+ $name = $value
+ ),+
+ }
+
+ impl ::core::convert::From<$flag> for $flags {
+ #[inline]
+ fn from(value: $flag) -> Self {
+ Self(value as $ty)
+ }
+ }
+
+ impl ::core::convert::From<$flags> for $ty {
+ #[inline]
+ fn from(value: $flags) -> Self {
+ value.0
+ }
+ }
+
+ impl ::core::ops::BitOr for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitor(self, rhs: Self) -> Self::Output {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl ::core::ops::BitOrAssign for $flags {
+ #[inline]
+ fn bitor_assign(&mut self, rhs: Self) {
+ *self = *self | rhs;
+ }
+ }
+
+ impl ::core::ops::BitOr<$flag> for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitor(self, rhs: $flag) -> Self::Output {
+ self | Self::from(rhs)
+ }
+ }
+
+ impl ::core::ops::BitOrAssign<$flag> for $flags {
+ #[inline]
+ fn bitor_assign(&mut self, rhs: $flag) {
+ *self = *self | rhs;
+ }
+ }
+
+ impl ::core::ops::BitAnd for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitand(self, rhs: Self) -> Self::Output {
+ Self(self.0 & rhs.0)
+ }
+ }
+
+ impl ::core::ops::BitAndAssign for $flags {
+ #[inline]
+ fn bitand_assign(&mut self, rhs: Self) {
+ *self = *self & rhs;
+ }
+ }
+
+ impl ::core::ops::BitAnd<$flag> for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitand(self, rhs: $flag) -> Self::Output {
+ self & Self::from(rhs)
+ }
+ }
+
+ impl ::core::ops::BitAndAssign<$flag> for $flags {
+ #[inline]
+ fn bitand_assign(&mut self, rhs: $flag) {
+ *self = *self & rhs;
+ }
+ }
+
+ impl ::core::ops::BitXor for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ Self((self.0 ^ rhs.0) & Self::all_bits())
+ }
+ }
+
+ impl ::core::ops::BitXorAssign for $flags {
+ #[inline]
+ fn bitxor_assign(&mut self, rhs: Self) {
+ *self = *self ^ rhs;
+ }
+ }
+
+ impl ::core::ops::BitXor<$flag> for $flags {
+ type Output = Self;
+ #[inline]
+ fn bitxor(self, rhs: $flag) -> Self::Output {
+ self ^ Self::from(rhs)
+ }
+ }
+
+ impl ::core::ops::BitXorAssign<$flag> for $flags {
+ #[inline]
+ fn bitxor_assign(&mut self, rhs: $flag) {
+ *self = *self ^ rhs;
+ }
+ }
+
+ impl ::core::ops::Not for $flags {
+ type Output = Self;
+ #[inline]
+ fn not(self) -> Self::Output {
+ Self((!self.0) & Self::all_bits())
+ }
+ }
+
+ impl ::core::ops::BitOr for $flag {
+ type Output = $flags;
+ #[inline]
+ fn bitor(self, rhs: Self) -> Self::Output {
+ $flags(self as $ty | rhs as $ty)
+ }
+ }
+
+ impl ::core::ops::BitAnd for $flag {
+ type Output = $flags;
+ #[inline]
+ fn bitand(self, rhs: Self) -> Self::Output {
+ $flags(self as $ty & rhs as $ty)
+ }
+ }
+
+ impl ::core::ops::BitXor for $flag {
+ type Output = $flags;
+ #[inline]
+ fn bitxor(self, rhs: Self) -> Self::Output {
+ $flags((self as $ty ^ rhs as $ty) & $flags::all_bits())
+ }
+ }
+
+ impl ::core::ops::Not for $flag {
+ type Output = $flags;
+ #[inline]
+ fn not(self) -> Self::Output {
+ $flags((!(self as $ty)) & $flags::all_bits())
+ }
+ }
+
+ impl $flags {
+ /// Returns an empty instance where no flags are set.
+ #[inline]
+ pub const fn empty() -> Self {
+ Self(0)
+ }
+
+ /// Returns a mask containing all valid flag bits.
+ #[inline]
+ pub const fn all_bits() -> $ty {
+ 0 $( | $value )+
+ }
+
+ /// Checks if a specific flag is set.
+ #[inline]
+ pub fn contains(self, flag: $flag) -> bool {
+ (self.0 & flag as $ty) == flag as $ty
+ }
+
+ /// Checks if at least one of the provided flags is set.
+ #[inline]
+ pub fn contains_any(self, flags: $flags) -> bool {
+ (self.0 & flags.0) != 0
+ }
+
+ /// Checks if all of the provided flags are set.
+ #[inline]
+ pub fn contains_all(self, flags: $flags) -> bool {
+ (self.0 & flags.0) == flags.0
+ }
+ }
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f812cf120042..996affce2c9e 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -100,6 +100,8 @@ pub mod fs;
#[cfg(CONFIG_I2C = "y")]
pub mod i2c;
pub mod id_pool;
+#[doc(hidden)]
+pub mod impl_flags;
pub mod init;
pub mod io;
pub mod ioctl;