From patchwork Mon May 19 07:07:07 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891250 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (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 7CCFC2673A3 for ; Mon, 19 May 2025 07:07:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638474; cv=none; b=nOpDQW/V4ODk4ndt3irkmSNUY9u/zmHiZgFrgmiGYVnw1pCbNuiY6pnTprW3+b+M3nIZ8P75Dx5T7SAPek6fMDOUi9+j+WeZeZ+3ITrGX6JcesTkb2HxvpN+D29k2cjeDKp2Mfc4AbjDr9nBVq2fkhwe1EAqcUBn4y9++YMB7nw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638474; c=relaxed/simple; bh=mmdJGfj1J92fyXHZLWhOyf340q+M42KPjIPk+rr8ZdM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=DHOmSfXlsB7fz0mag5zVpJUd8clYP7L+zCiB+FJJpSToKyrnxCsnzquA31fwhFNyB/Ig1eXkES9ZsRp87WulbzcwLhIu2pamZfB8bTP5uy+bXHi0PALuJNJ0wEHNCvPpumjWsWHahJ6iQCO6ZprYQH7szdFcZlfoQUAiG17rJZg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=r0RsFeRJ; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="r0RsFeRJ" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-231d4679580so22971755ad.1 for ; Mon, 19 May 2025 00:07:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638470; x=1748243270; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=a3/wI85QVIhlqw2UpEhcb3+sPWDU0QZqMy/18ZqpoOk=; b=r0RsFeRJ51AknYUosnfC8+1IV7KQr9rjOq9NoppZYKDbMezBhllNfVaN05YUm8xAz+ Jq5hY2lRBnMiuqiepBiX8clk+h2HU4NMVfPosgyzPR8Vrg8Bcimor052PPjEEqtIACZ9 tH6oGb1AJ/gXP+ejPbbY5kQ8XkBARpCY2TnD5Uqv9Dh2kjb83kb8pAJ5Pce4vZKlnOgW vynZcqrux6sk64u/j82MmWyWpggIZ8nybpJt8qpSh7kANL1NE2AxOGpkbB6dIRUDGp9r VNlWMZReVCzsPT+lK2bVEDjsf/kch41AecrYrTVTSB6e11sOmvNAXEDkA5fxDVL+F1pR w3Nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638470; x=1748243270; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=a3/wI85QVIhlqw2UpEhcb3+sPWDU0QZqMy/18ZqpoOk=; b=hPXWi8gd1S6Yavoe9MFmA1r2UMNzCbleWpdrPmoicYtN5ApUf4AsrQoqsYnPrabPCO 5ionQr8z3pOXECqZwnFAP7DedVJQWI/v8QlXPjpzkqs0s6uSSvZVwMDLxnJP04+b5L5q Y5aOEZU19VCirG2wySNnXVkwXtaDsHFb7+zjcEsxzseAH/a7e9iG1Kcris/TfG7SwQ+b hNI5sUh75zpMeT8abpNaMJeBKCOEajTrfEF2MGKzqzh1wM+rJl6mpAcw+gaoVd3uuBCu gJgxnf++sGt7vltqBW/uJHsI4JP3tQFeO7grqAiPK1hjdoeieMdNCbs7TfyO8UW9KzSN 4Prw== X-Gm-Message-State: AOJu0YwdMv4a/2TUXDp3iJ22yCnE9Ki7XRaVD5h0SO6FmsBUskuPm0Is ZaRWBrzRAkaupvYBxcbQJYJHPIu2PpzvE6OxNKbuP/9y2TN0RFSm6HpnbblgDx3XBfM= X-Gm-Gg: ASbGncs381I3RmUiw4E53yfWE9mAowTQH9lPHn9n9jiYoAw+xjLuGw6eUDhyHmqDsRJ WudE3dKjw5T4dq6tAbcB6xgsYsoqF18FRryrLZkUi3jG3YfCQ3si7vGDCh5WhRnuzfV+BvdngJi XKg4I0KcCbVptAuEOucLEol3iF54mbNlN/IFM6nsRUqSPN8pnjEtz25PxgP0Ap/7bGRE03YZvDt zFBbpo8YzpsCWsz3T2GA+qZ7K7xCwElFjlKzaRrUQPMVHJzg7pk33X8g1ayW9LZgqwaN83dtUGH itvEQZfKqYuoudm08gN91yN0fqnKmNYNEA+gVlwAWe0y2zMPajrl X-Google-Smtp-Source: AGHT+IFw7FZxkI/daEY9X+IfCeDBEfiSzb6xsNooBRE42Xq9dUAbYn7PCdnIHpUVKiHdEZ4KX8VyKA== X-Received: by 2002:a17:903:19ce:b0:224:a74:28cd with SMTP id d9443c01a7336-231d454b072mr177377655ad.31.1747638470399; Mon, 19 May 2025 00:07:50 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-231d4ed259esm52927915ad.222.2025.05.19.00.07.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:07:49 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Yury Norov , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , linux-kernel@vger.kernel.org Subject: [PATCH V12 02/15] rust: cpumask: Add initial abstractions Date: Mon, 19 May 2025 12:37:07 +0530 Message-Id: <24d77314f3dc848dbe73e19aed87a69dde55ed0b.1747634382.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add initial Rust abstractions for struct cpumask, covering a subset of its APIs. Additional APIs can be added as needed. These abstractions will be used in upcoming Rust support for cpufreq and OPP frameworks. Signed-off-by: Viresh Kumar Reviewed-by: Yury Norov [NVIDIA] --- rust/kernel/cpumask.rs | 330 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 331 insertions(+) create mode 100644 rust/kernel/cpumask.rs diff --git a/rust/kernel/cpumask.rs b/rust/kernel/cpumask.rs new file mode 100644 index 000000000000..c90bfac9346a --- /dev/null +++ b/rust/kernel/cpumask.rs @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! CPU Mask abstractions. +//! +//! C header: [`include/linux/cpumask.h`](srctree/include/linux/cpumask.h) + +use crate::{ + alloc::{AllocError, Flags}, + prelude::*, + types::Opaque, +}; + +#[cfg(CONFIG_CPUMASK_OFFSTACK)] +use core::ptr::{self, NonNull}; + +#[cfg(not(CONFIG_CPUMASK_OFFSTACK))] +use core::mem::MaybeUninit; + +use core::ops::{Deref, DerefMut}; + +/// A CPU Mask. +/// +/// Rust abstraction for the C `struct cpumask`. +/// +/// # Invariants +/// +/// A [`Cpumask`] instance always corresponds to a valid C `struct cpumask`. +/// +/// The callers must ensure that the `struct cpumask` is valid for access and +/// remains valid for the lifetime of the returned reference. +/// +/// ## Examples +/// +/// The following example demonstrates how to update a [`Cpumask`]. +/// +/// ``` +/// use kernel::bindings; +/// use kernel::cpumask::Cpumask; +/// +/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: u32, clear_cpu: i32) { +/// // SAFETY: The `ptr` is valid for writing and remains valid for the lifetime of the +/// // returned reference. +/// let mask = unsafe { Cpumask::as_mut_ref(ptr) }; +/// +/// mask.set(set_cpu); +/// mask.clear(clear_cpu); +/// } +/// ``` +#[repr(transparent)] +pub struct Cpumask(Opaque); + +impl Cpumask { + /// Creates a mutable reference to an existing `struct cpumask` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime + /// of the returned reference. + pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask) -> &'a mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Creates a reference to an existing `struct cpumask` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime + /// of the returned reference. + pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Obtain the raw `struct cpumask` pointer. + pub fn as_raw(&self) -> *mut bindings::cpumask { + let this: *const Self = self; + this.cast_mut().cast() + } + + /// Set `cpu` in the cpumask. + /// + /// ATTENTION: Contrary to C, this Rust `set()` method is non-atomic. + /// This mismatches kernel naming convention and corresponds to the C + /// function `__cpumask_set_cpu()`. + #[inline] + pub fn set(&mut self, cpu: u32) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `__cpumask_set_cpu`. + unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) }; + } + + /// Clear `cpu` in the cpumask. + /// + /// ATTENTION: Contrary to C, this Rust `clear()` method is non-atomic. + /// This mismatches kernel naming convention and corresponds to the C + /// function `__cpumask_clear_cpu()`. + #[inline] + pub fn clear(&mut self, cpu: i32) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to + // `__cpumask_clear_cpu`. + unsafe { bindings::__cpumask_clear_cpu(cpu, self.as_raw()) }; + } + + /// Test `cpu` in the cpumask. + /// + /// Equivalent to the kernel's `cpumask_test_cpu` API. + #[inline] + pub fn test(&self, cpu: i32) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_test_cpu`. + unsafe { bindings::cpumask_test_cpu(cpu, self.as_raw()) } + } + + /// Set all CPUs in the cpumask. + /// + /// Equivalent to the kernel's `cpumask_setall` API. + #[inline] + pub fn setall(&mut self) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_setall`. + unsafe { bindings::cpumask_setall(self.as_raw()) }; + } + + /// Checks if cpumask is empty. + /// + /// Equivalent to the kernel's `cpumask_empty` API. + #[inline] + pub fn empty(&self) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_empty`. + unsafe { bindings::cpumask_empty(self.as_raw()) } + } + + /// Checks if cpumask is full. + /// + /// Equivalent to the kernel's `cpumask_full` API. + #[inline] + pub fn full(&self) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_full`. + unsafe { bindings::cpumask_full(self.as_raw()) } + } + + /// Get weight of the cpumask. + /// + /// Equivalent to the kernel's `cpumask_weight` API. + #[inline] + pub fn weight(&self) -> u32 { + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_weight`. + unsafe { bindings::cpumask_weight(self.as_raw()) } + } + + /// Copy cpumask. + /// + /// Equivalent to the kernel's `cpumask_copy` API. + #[inline] + pub fn copy(&self, dstp: &mut Self) { + // SAFETY: By the type invariant, `Self::as_raw` is a valid argument to `cpumask_copy`. + unsafe { bindings::cpumask_copy(dstp.as_raw(), self.as_raw()) }; + } +} + +/// A CPU Mask pointer. +/// +/// Rust abstraction for the C `struct cpumask_var_t`. +/// +/// # Invariants +/// +/// A [`CpumaskVar`] instance always corresponds to a valid C `struct cpumask_var_t`. +/// +/// The callers must ensure that the `struct cpumask_var_t` is valid for access and remains valid +/// for the lifetime of [`CpumaskVar`]. +/// +/// ## Examples +/// +/// The following example demonstrates how to create and update a [`CpumaskVar`]. +/// +/// ``` +/// use kernel::cpumask::CpumaskVar; +/// +/// let mut mask = CpumaskVar::new_zero(GFP_KERNEL).unwrap(); +/// +/// assert!(mask.empty()); +/// mask.set(2); +/// assert!(mask.test(2)); +/// mask.set(3); +/// assert!(mask.test(3)); +/// assert_eq!(mask.weight(), 2); +/// +/// let mask2 = CpumaskVar::try_clone(&mask).unwrap(); +/// assert!(mask2.test(2)); +/// assert!(mask2.test(3)); +/// assert_eq!(mask2.weight(), 2); +/// ``` +pub struct CpumaskVar { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: NonNull, + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + mask: Cpumask, +} + +impl CpumaskVar { + /// Creates a zero-initialized instance of the [`CpumaskVar`]. + pub fn new_zero(_flags: Flags) -> Result { + Ok(Self { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: { + let mut ptr: *mut bindings::cpumask = ptr::null_mut(); + + // SAFETY: It is safe to call this method as the reference to `ptr` is valid. + // + // INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of + // scope. + unsafe { bindings::zalloc_cpumask_var(&mut ptr, _flags.as_raw()) }; + NonNull::new(ptr.cast()).ok_or(AllocError)? + }, + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + // SAFETY: FFI type is valid to be zero-initialized. + // + // INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of scope. + mask: unsafe { core::mem::zeroed() }, + }) + } + + /// Creates an instance of the [`CpumaskVar`]. + /// + /// # Safety + /// + /// The caller must ensure that the returned [`CpumaskVar`] is properly initialized before + /// getting used. + pub unsafe fn new(_flags: Flags) -> Result { + Ok(Self { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: { + let mut ptr: *mut bindings::cpumask = ptr::null_mut(); + + // SAFETY: It is safe to call this method as the reference to `ptr` is valid. + // + // INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of + // scope. + unsafe { bindings::alloc_cpumask_var(&mut ptr, _flags.as_raw()) }; + NonNull::new(ptr.cast()).ok_or(AllocError)? + }, + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The associated memory is freed when the `CpumaskVar` goes out of scope. + mask: unsafe { MaybeUninit::uninit().assume_init() }, + }) + } + + /// Creates a mutable reference to an existing `struct cpumask_var_t` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime + /// of the returned reference. + pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask_var_t) -> &'a mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Creates a reference to an existing `struct cpumask_var_t` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime + /// of the returned reference. + pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask_var_t) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Clones cpumask. + pub fn try_clone(cpumask: &Cpumask) -> Result { + // SAFETY: The returned cpumask_var is initialized right after this call. + let mut cpumask_var = unsafe { Self::new(GFP_KERNEL) }?; + + cpumask.copy(&mut cpumask_var); + Ok(cpumask_var) + } +} + +// Make [`CpumaskVar`] behave like a pointer to [`Cpumask`]. +impl Deref for CpumaskVar { + type Target = Cpumask; + + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + fn deref(&self) -> &Self::Target { + // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask. + unsafe { &*self.ptr.as_ptr() } + } + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + fn deref(&self) -> &Self::Target { + &self.mask + } +} + +impl DerefMut for CpumaskVar { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + fn deref_mut(&mut self) -> &mut Cpumask { + // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask. + unsafe { self.ptr.as_mut() } + } + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + fn deref_mut(&mut self) -> &mut Cpumask { + &mut self.mask + } +} + +impl Drop for CpumaskVar { + fn drop(&mut self) { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `free_cpumask_var`. + unsafe { + bindings::free_cpumask_var(self.as_raw()) + }; + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index de07aadd1ff5..75f78f6bfaa6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ pub mod block; #[doc(hidden)] pub mod build_assert; +pub mod cpumask; pub mod cred; pub mod device; pub mod device_id; From patchwork Mon May 19 07:07:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891249 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (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 E7051268C48 for ; Mon, 19 May 2025 07:07:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638480; cv=none; b=g/dS75IxZFexI1LSH/Z9TBn7kyUpdNYuNThrhHQcokipL/cZnhlapgJGBbeBum4s88UkI/5IsmS1thIWF5RMsl0tmEvuYrODAtO/6pt0zSevgrRZlzKV6id66K1JfPE+DAVQCOrYIkzQt49pM7XCwCbllhZrnwlvsCGNwaU2oZQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638480; c=relaxed/simple; bh=4HWksdgns8ejVb++Q4NJVdKDSuHEDHdMmvi+j+YhGlo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=f8yFE7eufgklEthrbw04FuQOBLbSHvJjrI/75yyLDLGxjmxBtQcE0aV6xUXa55uNcKk99whUF09Dkmimdem8tmKVoOgNBXoFncgjzVzHsVP90EZ5Xj4oKHoFlBvoKxg4X67xKr3LmdYtuN4JJLMfT4RXUTl3MncydEIiK2zLXsY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=oURx1vhr; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="oURx1vhr" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-30e9397802eso1695226a91.1 for ; Mon, 19 May 2025 00:07:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638478; x=1748243278; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WNsggH78Gujq22EIg5QA+ygoUlPHiqAj8S6vH5ayXvk=; b=oURx1vhrE893jp72/YQ/sUBnRtO8aTJTcAZq5IkvzRVzrRghsh/zfhBt3kO+2Ga9/G fnJkmwPOzd1K3K4/rrKi9ygZw1pfP93z2gBG8A344F1E5f7w5mN9TfgGNcvlT6Eijth2 Y2YFbybWguowNkOL5aE+oxSz7y2h+7zuZoRC0T+gxDZxj84wWzcmMcLv9Rn63M4Gbd9/ HLmVcagmqxawzKlfHBGBXrW+LhRhyTm13qBaomzswvepcjQl2Thmjm2o6R3asXRgHWbn 5HUMxLz35a+CGiV3YJJe2VzhL6cSdsEsj3ETo/AAcjtm6de/iYaX+dfhcp+i2R3ILLm2 XH4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638478; x=1748243278; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WNsggH78Gujq22EIg5QA+ygoUlPHiqAj8S6vH5ayXvk=; b=UlDINAGINxFr2p+G+5/8K8NeAck49et6IBzEz11IO1EIb4y+o4RaKSpD0EY7ciOk6n ieTmxgolleOzL/AIUNJicK2KZjiBp7CDIxndR/VCuAZFRmg7zc5YafUAdqKknBbuOT/l 4h6wnG3EvuuMozQkTgvo4HeFhUDJZVIWwZrFsOYtmKZB7N9dH/lRyD9JcOVBePIgY74Q VPEaBg6VkOPVr1TgroX0/9AfCxSTuOG1bUpYfP2xY31c9R7o6kHa3b8OfIOvPQu+pcEF H/o5kv1+iLntEKuP4Z0L5eLnq5dgGaR1/dm1zA6O1tcxo9CTuYQTnWenEFKXzgAg5d0+ RtGw== X-Forwarded-Encrypted: i=1; AJvYcCW/sfeDF4V+hdAxE8FGVCkrBJsBbD0A1xpSTRYYI9xEtHQnCH8eK/wYBhLZdbUTXVVv+niMYrNWUQ==@vger.kernel.org X-Gm-Message-State: AOJu0Yz8eUD9VWFfD2P7j5NdzraIgM3EIWVZNXc+0BRZ0/STteeso+eE yUJ2QCVh7/69SCD3yVoNZ+YQgbPmiyRcaLO0FUT388W7ABt9sR0cBZZ5VnyVPZvt5Ck= X-Gm-Gg: ASbGncuQglStylSlOaeGFEhzl7BEAd+bVbHaKcMmxMbIlDIsAtK7pxTUddHIyfCtJLy BUP01Q2e956kAUHY3CGli1PFBaWfEh0Jsm6BLIOPp9R5o0ePiU5p/0TOP8i9qmE7KUJBV25kYEa XYccVPdgP8uGoucWETy8VjdmqoV5EQSbUO0a/qYduX0d1HzqsJr1BB9u9Jn8MAyS+r0ff2mBz4N gtdDD9WbC1GuAbSTxjDdsgvftTEivHEnTnNPmRxEYyhcPbGamLJowWKEy/OXx6OU/7+zhp7+vNj Nk89nwMMMfFU6MCJzXbImrBOJ/tsIcq+eSZDaYOx+3n055JEOMiZRdPrUiTvQP8= X-Google-Smtp-Source: AGHT+IHyX79iTRiEese5DXhXzl4RcrDdeFt3S9RU8Ax4nAHLTvc2jaPooDG34VMRP3xDbTxlkEsoKQ== X-Received: by 2002:a17:90b:558e:b0:30e:840a:92ed with SMTP id 98e67ed59e1d1-30e840a93d2mr16053139a91.31.1747638478085; Mon, 19 May 2025 00:07:58 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-30ef3c50c31sm1582169a91.45.2025.05.19.00.07.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:07:57 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michael Turquette , Stephen Boyd Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Andrew Ballance , Daniel Almeida , linux-kernel@vger.kernel.org Subject: [PATCH V12 04/15] rust: clk: Add helpers for Rust code Date: Mon, 19 May 2025 12:37:09 +0530 Message-Id: <0ec0250c1170a8a6efb2db7a6cb49ae974d7ce05.1747634382.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Non-trivial C macros and inlined C functions cannot be used directly in the Rust code and are used via functions ("helpers") that wrap those so that they can be called from Rust. In order to prepare for adding Rust abstractions for the clock APIs, add clock helpers required by the Rust implementation. Reviewed-by: Daniel Almeida Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/clk.c | 66 +++++++++++++++++++++++++++++++++ rust/helpers/helpers.c | 1 + 4 files changed, 69 insertions(+) create mode 100644 rust/helpers/clk.c diff --git a/MAINTAINERS b/MAINTAINERS index bd7c54af4fd4..608689342aaf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5883,6 +5883,7 @@ F: include/dt-bindings/clock/ F: include/linux/clk-pr* F: include/linux/clk/ F: include/linux/of_clk.h +F: rust/helpers/clk.c X: drivers/clk/clkdev.c COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ab37e1d35c70..f53d6e1a21f2 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/clk.c b/rust/helpers/clk.c new file mode 100644 index 000000000000..6d04372c9f3b --- /dev/null +++ b/rust/helpers/clk.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +/* + * The "inline" implementation of below helpers are only available when + * CONFIG_HAVE_CLK or CONFIG_HAVE_CLK_PREPARE aren't set. + */ +#ifndef CONFIG_HAVE_CLK +struct clk *rust_helper_clk_get(struct device *dev, const char *id) +{ + return clk_get(dev, id); +} + +void rust_helper_clk_put(struct clk *clk) +{ + clk_put(clk); +} + +int rust_helper_clk_enable(struct clk *clk) +{ + return clk_enable(clk); +} + +void rust_helper_clk_disable(struct clk *clk) +{ + clk_disable(clk); +} + +unsigned long rust_helper_clk_get_rate(struct clk *clk) +{ + return clk_get_rate(clk); +} + +int rust_helper_clk_set_rate(struct clk *clk, unsigned long rate) +{ + return clk_set_rate(clk, rate); +} +#endif + +#ifndef CONFIG_HAVE_CLK_PREPARE +int rust_helper_clk_prepare(struct clk *clk) +{ + return clk_prepare(clk); +} + +void rust_helper_clk_unprepare(struct clk *clk) +{ + clk_unprepare(clk); +} +#endif + +struct clk *rust_helper_clk_get_optional(struct device *dev, const char *id) +{ + return clk_get_optional(dev, id); +} + +int rust_helper_clk_prepare_enable(struct clk *clk) +{ + return clk_prepare_enable(clk); +} + +void rust_helper_clk_disable_unprepare(struct clk *clk) +{ + clk_disable_unprepare(clk); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index e1c21eba9b15..ae595c9cd91b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -11,6 +11,7 @@ #include "bug.c" #include "build_assert.c" #include "build_bug.c" +#include "clk.c" #include "cpumask.c" #include "cred.c" #include "device.c" From patchwork Mon May 19 07:07:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891248 Received: from mail-pf1-f174.google.com (mail-pf1-f174.google.com [209.85.210.174]) (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 396752690FA for ; Mon, 19 May 2025 07:08:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638490; cv=none; b=iC+OqGXvEPzLjMVb9yp9zZsZFIm1EYSXGZBFm6kx2MmoUYJt44TQ//rUrYekIQHpFlvpLIQWGnSpQN6Y9aWUxcupWq9lOeJ596LYvm+G0OnFZbgvEWL6NezkNBczJ8FiI/HICoDJzJ8IswQ/e6kEtSXvoyOsBb8n9a8eLphGRFI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638490; c=relaxed/simple; bh=RPs39+G/2/WRBdJP3KwTQnQlR3Lp/kvURzUIgtDgvwA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=hICRliiDScy2+mR7xUPUr9FN7dvKIckC8OE0sborXe9K0wjMK96upJIcjJcJPu7a4kbgn0L1NkvHjw4pgk7U+/pJvbrY/7GSN9vGT8NrVmOE1vhQKJ99FzX3iEc87xl78uZClqxNaS/1FCCgmvCeYqRbqC9caWt5RZtZY/4aOR8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=TpvJi+Kr; arc=none smtp.client-ip=209.85.210.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="TpvJi+Kr" Received: by mail-pf1-f174.google.com with SMTP id d2e1a72fcca58-73bf5aa95e7so3602001b3a.1 for ; Mon, 19 May 2025 00:08:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638486; x=1748243286; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=06XWI3zWvI8xB81BSzBcpZnXhq7zBjUBfZT4cp51CDs=; b=TpvJi+KreG9k+1tnyhg46J2UAumtPDce3pxuk/EPgy9RF25DuB/valuzxXRAOIzTX/ 5zr5MF4xvECZ3/d+xd0M24bFSUzUOtAN24ifMNxOgaFLFeYy16psv0DRCgTt8rzNaK1g hGphfvcsFtV4K5sjUuyugXdLGPIfWxl/ktSsqZ6z0mIpMYWBK5Y1VXjTCOyp0beMSAD0 uOer9o4z9VYJwtpYkawNXQCfECTbUDncvYusT5Ml6duR/7j3ibBX6PdpTUnOrKhEsPtF I8praT4zGMDz5KdygZFooZovOscrT0qfiBc1aIb4kmBiVWPQt37P9ykwEnd9NY8M9KF9 3p/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638486; x=1748243286; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=06XWI3zWvI8xB81BSzBcpZnXhq7zBjUBfZT4cp51CDs=; b=gmm68rXpm3sMhDdvPIVjFijxnydInREu2OiCEf1blitqYZ5JAQwnxMFvftdU4f1WRD EpsRg6jfsIDLxeiO8XU3cuzdUsqR60NfJ92aMeisRFV6ckMhkULGYyBM7FdjYKx1+/xv duRVCTbFctj3pE/YcReqmyyV5lLdcs1UdoSrw3RbuBlfLmnenXHNpIZpZWprwm41yfzf vL2zS9h9Y4kfNVbiHEiun9KdBEYFTXMW839w/5TCTpV47d6hfIBIRUKXMUQgqbwe5WUf g3SCKLyI+TsnZDMcopsER6GwA7TWFhCOuiPlCFIvjUvsNBZ36s3q8KhcZlTtGn19x2Tt vidw== X-Forwarded-Encrypted: i=1; AJvYcCUp1N22dCYeSAZIYmU+vEbDSkvUFiu67oYDeOhsSYGjDLJYB8LzMXgNx4tZud4nIC5gdddz327rYA==@vger.kernel.org X-Gm-Message-State: AOJu0YwN3hU0DxxO0rwFc+tK0eqm7nXGFI1755Nxx2l2GfyKTpiVIPsW oHmsVrdewVSvrcSy4vbykwO+eiM3BMAyAoRJQHijYU9lxlFuhdGUSJM1oBjnTHZ0e1EMHOr9l7Y 903xL X-Gm-Gg: ASbGnctvspBlpvvqASdKR5uAtiWBnSpDASawpgalfzMSBA4juLFITtTCNyVGJiyj0sS kA0WJWCTBKikPDaMq6HjJko7FH4ToOZNm/5JMguua7s3ocD6ZuD2zKL1NCJ81eCZ8NY34K/icCX aBVWcpe1sIYNDqhIMAOJ9grYOUrZ3ppw5SB6jHMOmWq+ofFkXOHXZU4foPAmXt0R14aIpT/NJNI 5PgSRW08IAiUx8K4z143n6RVwNsPtzBOCIDPQSIqrX+5/QGo7cck2JhwjV51rP7cLd5/UhLgWOI 8H6CDk8za3WJbHFx33l4u9oVjP/Bh7D4cJeZIQBN4H4Tc3HfiPr1 X-Google-Smtp-Source: AGHT+IHDxTCILxiZSszxnTc982DJY8orsb9n0sCiFXe5hNTg/g6o+f+sr8ozsUbLXvO9AluZPucbQw== X-Received: by 2002:a05:6a21:a0cb:b0:218:c22:e3e6 with SMTP id adf61e73a8af0-2180c22e3fdmr15396853637.12.1747638486393; Mon, 19 May 2025 00:08:06 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-742acb8731esm5294876b3a.168.2025.05.19.00.08.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:08:05 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , Anisse Astier , linux-kernel@vger.kernel.org Subject: [PATCH V12 06/15] rust: macros: enable use of hyphens in module names Date: Mon, 19 May 2025 12:37:11 +0530 Message-Id: <21b4c30db60f22d56cc6386a18564705ad3a6f4a.1747634382.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Anisse Astier Some modules might need naming that contains hyphens "-" to match the auto-probing by name in the platform devices that comes from the device tree. But rust identifiers cannot contain hyphens, so replace the module name by an underscore anywhere we'd use it as an identifier. Signed-off-by: Anisse Astier Reviewed-by: Alice Ryhl [Viresh: Replace "-" with '-', and fix line length checkpatch warnings] Signed-off-by: Viresh Kumar --- rust/macros/module.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index a9418fbc9b44..27cc72d474f0 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -185,7 +185,9 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { let info = ModuleInfo::parse(&mut it); - let mut modinfo = ModInfoBuilder::new(info.name.as_ref()); + /* Rust does not allow hyphens in identifiers, use underscore instead */ + let name_identifier = info.name.replace('-', "_"); + let mut modinfo = ModInfoBuilder::new(name_identifier.as_ref()); if let Some(author) = info.author { modinfo.emit("author", &author); } @@ -310,14 +312,15 @@ mod __module_init {{ #[doc(hidden)] #[link_section = \"{initcall_section}\"] #[used] - pub static __{name}_initcall: extern \"C\" fn() -> kernel::ffi::c_int = __{name}_init; + pub static __{name_identifier}_initcall: extern \"C\" fn() -> + kernel::ffi::c_int = __{name_identifier}_init; #[cfg(not(MODULE))] #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] core::arch::global_asm!( r#\".section \"{initcall_section}\", \"a\" - __{name}_initcall: - .long __{name}_init - . + __{name_identifier}_initcall: + .long __{name_identifier}_init - . .previous \"# ); @@ -325,7 +328,7 @@ mod __module_init {{ #[cfg(not(MODULE))] #[doc(hidden)] #[no_mangle] - pub extern \"C\" fn __{name}_init() -> kernel::ffi::c_int {{ + pub extern \"C\" fn __{name_identifier}_init() -> kernel::ffi::c_int {{ // SAFETY: This function is inaccessible to the outside due to the double // module wrapping it. It is called exactly once by the C side via its // placement above in the initcall section. @@ -335,13 +338,13 @@ mod __module_init {{ #[cfg(not(MODULE))] #[doc(hidden)] #[no_mangle] - pub extern \"C\" fn __{name}_exit() {{ + pub extern \"C\" fn __{name_identifier}_exit() {{ // SAFETY: // - This function is inaccessible to the outside due to the double // module wrapping it. It is called exactly once by the C side via its // unique name, - // - furthermore it is only called after `__{name}_init` has returned `0` - // (which delegates to `__init`). + // - furthermore it is only called after `__{name_identifier}_init` has + // returned `0` (which delegates to `__init`). unsafe {{ __exit() }} }} @@ -381,6 +384,7 @@ unsafe fn __exit() {{ ", type_ = info.type_, name = info.name, + name_identifier = name_identifier, modinfo = modinfo.buffer, initcall_section = ".initcall6.init" ) From patchwork Mon May 19 07:07:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891247 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (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 E4C22267728 for ; Mon, 19 May 2025 07:08:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638499; cv=none; b=qD7DTyTKwNqbA4L5yivfuELOykemawUUgvbq1sQzI+qicH0F62RX26PNCIDxIirDtAj/Jko7/iOPmerOG3yqGwodxYOmohHWh4dBCShXK+VynWSiy5btYmvhdDrAYyk3qbBoCq6fXkddNPBpIAIceFCcKLeN9YEikep7YA+N8zc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638499; c=relaxed/simple; bh=iaCNxDEvQFF0BxKHRPZ6sTywDQH6RmrBQNe/gDyVxqg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qcU3arJbtryVhflo8bgorWNXLuUbhDjt7NnxUqErsr0aMk5Oynitnr5d/kbdHb7kUnbVFE55AAyt3r5ufSs+uatkwUqdC9V4mQZszDF/vmbwLBgckjBj6vYYEAdtaDpDDJS4TTeObeffScFw7IWWfkFAdLw7oSPnSrDdxYMI4eE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=U2WCg5hH; arc=none smtp.client-ip=209.85.210.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="U2WCg5hH" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-742c035f2afso696658b3a.2 for ; Mon, 19 May 2025 00:08:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638495; x=1748243295; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=T0gY3g4CMyk/KLPQ47ChexP08SbMxnytGLgjzVXuWIc=; b=U2WCg5hH3h+SIVcy5nQ4Soix+uZdrkOWAh2I7IUFpVJHaQmuyeUDDRjmMxjPnvLiok f5dYBSzT5rWFkhL6YgBtJNVIEX3HAjcd7h+SGCyM56xoMixrWyQQPz8i6IqIj33Q/fMh 9bNaGPD9ovUSO0meORwBRODvQv78vKjuotAmrA0OFqhi0tNExR9F9hdkMGEift16K6WT Ti3gjWpXESl2IvieGsikHrncj7T2AVQdCnPL+6vpvDesqKDyeYHgLEwDa9zmzs309VIy ym5KUBqJEjrZM/Ac3L1iSmAmv1RTD9tI/391eaS2osheOZ0qwuruMJHoWrHsWzvqtPHL 4/3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638495; x=1748243295; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=T0gY3g4CMyk/KLPQ47ChexP08SbMxnytGLgjzVXuWIc=; b=F3x7cPH7bgrJTqS348Dt/b2+nwIWWGdhHQDi/ZUuJxxwC9fK8Z/EjZbkixuYsYNQzd 5Fb7hJF72GLqg0vrhFNiZ1EIcfSZCjdiE7iS3qQt0xf/ePHJeQ5hr3F5Gz+lf4lELkco oQWjHerXai4S//Gm7UTFoywHohU74U2lCjnNKa8r38Uh2K5+4KaUqXctPoAn6lU3LlOa a/Lj9s2r0drklwoO6wX0MGrcBNOwKFgIvmZ0nGp1JriFj6yhYI80ZzW8rF7bdA32fqvG Bivr7epOUG/qkazViZLYNIi2AlA/oHS55FvNPF45703XYvgzp8U2ChwbFrN6rfLB0zSQ Onaw== X-Forwarded-Encrypted: i=1; AJvYcCWTYjGM3FTkBRZg5rWdAxijccNtqtPJaXiaNLohG/Du3wNia9v6o67xVhiqdwF/kqqLv511glP53g==@vger.kernel.org X-Gm-Message-State: AOJu0YxwcuU84mtYijC3SyrGONU7IXcjf0KXOSKUYfnRWoC8+srXByzA cy0xThWhQJnzFAx2pxZgSvM6peHPPaiuGYfRtkvJ8wlPDRYOtAXsog/fbrpA31M+hzg= X-Gm-Gg: ASbGnctISvzJPVFvLBaahj0ahsuR1uAv+nl7PZ38R9WUK4y4STkE16DytePfiUT42Xq uboUFubeUayeWA2FMNwlBpbZlZmNCTAPNGEAG1+sFhP0CvwKadP1fTzpHtuFDKA0yXk7mn4IT8X ST70Nzwqog/02bQd2huPwwYoFZkI6pQuyVJwoKcj6d85A72V8yx2MYzCYJ/IEMRldP/MzPYAKCY wH+kLryEmKX5d1SoOtAF6rSPGcmnMNkV9jbGDfADhSl8+yyLY6uXF+D/7znP90k826SMcHyurMY 8jfvIBVdSW2EuOoktARtqc4VOg3O91LHBtfxs+Ki50f98C/XlXFL X-Google-Smtp-Source: AGHT+IH9j6Bth5f8cWmvK9+922RPLy2g7bS9UUlSkK9L8CsCt0G/VVlItFC0SYxSb2QpytTZz9o0Uw== X-Received: by 2002:aa7:88c8:0:b0:73e:2dc5:a93c with SMTP id d2e1a72fcca58-742a97c4fd9mr15166932b3a.11.1747638495149; Mon, 19 May 2025 00:08:15 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-742a970c86asm5738760b3a.57.2025.05.19.00.08.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:08:14 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Viresh Kumar , Nishanth Menon , Stephen Boyd Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , linux-kernel@vger.kernel.org Subject: [PATCH V12 08/15] rust: opp: Add initial abstractions for OPP framework Date: Mon, 19 May 2025 12:37:13 +0530 Message-Id: <1a237a773715cb0738d831aeef9352ed04d2eac8.1747634382.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce initial Rust abstractions for the Operating Performance Points (OPP) framework. This includes bindings for `struct dev_pm_opp` and `struct dev_pm_opp_data`, laying the groundwork for further OPP integration. Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 2 + rust/kernel/opp.rs | 299 ++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 rust/kernel/opp.rs diff --git a/MAINTAINERS b/MAINTAINERS index 475abf72869c..931e418f89ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18165,6 +18165,7 @@ F: Documentation/devicetree/bindings/opp/ F: Documentation/power/opp.rst F: drivers/opp/ F: include/linux/pm_opp.h +F: rust/kernel/opp.rs OPL4 DRIVER M: Clemens Ladisch diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ac92c67d2c38..529f22891e0b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index de0a840fcc99..ea589254b4ac 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -67,6 +67,8 @@ #[cfg(CONFIG_NET)] pub mod net; pub mod of; +#[cfg(CONFIG_PM_OPP)] +pub mod opp; pub mod page; #[cfg(CONFIG_PCI)] pub mod pci; diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs new file mode 100644 index 000000000000..8f0493a8b6e8 --- /dev/null +++ b/rust/kernel/opp.rs @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Operating performance points. +//! +//! This module provides rust abstractions for interacting with the OPP subsystem. +//! +//! C header: [`include/linux/pm_opp.h`](srctree/include/linux/pm_opp.h) +//! +//! Reference: + +use crate::{ + clk::Hertz, + device::Device, + error::{code::*, to_result, Result}, + ffi::c_ulong, + types::{ARef, AlwaysRefCounted, Opaque}, +}; + +use core::ptr; + +/// The voltage unit. +/// +/// Represents voltage in microvolts, wrapping a [`c_ulong`] value. +/// +/// ## Examples +/// +/// ``` +/// use kernel::opp::MicroVolt; +/// +/// let raw = 90500; +/// let volt = MicroVolt(raw); +/// +/// assert_eq!(usize::from(volt), raw); +/// assert_eq!(volt, MicroVolt(raw)); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct MicroVolt(pub c_ulong); + +impl From for c_ulong { + #[inline] + fn from(volt: MicroVolt) -> Self { + volt.0 + } +} + +/// The power unit. +/// +/// Represents power in microwatts, wrapping a [`c_ulong`] value. +/// +/// ## Examples +/// +/// ``` +/// use kernel::opp::MicroWatt; +/// +/// let raw = 1000000; +/// let power = MicroWatt(raw); +/// +/// assert_eq!(usize::from(power), raw); +/// assert_eq!(power, MicroWatt(raw)); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct MicroWatt(pub c_ulong); + +impl From for c_ulong { + #[inline] + fn from(power: MicroWatt) -> Self { + power.0 + } +} + +/// Handle for a dynamically created [`OPP`]. +/// +/// The associated [`OPP`] is automatically removed when the [`Token`] is dropped. +/// +/// ## Examples +/// +/// The following example demonstrates how to create an [`OPP`] dynamically. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Data, MicroVolt, Token}; +/// use kernel::types::ARef; +/// +/// fn create_opp(dev: &ARef, freq: Hertz, volt: MicroVolt, level: u32) -> Result { +/// let data = Data::new(freq, volt, level, false); +/// +/// // OPP is removed once token goes out of scope. +/// data.add_opp(dev) +/// } +/// ``` +pub struct Token { + dev: ARef, + freq: Hertz, +} + +impl Token { + /// Dynamically adds an [`OPP`] and returns a [`Token`] that removes it on drop. + fn new(dev: &ARef, mut data: Data) -> Result { + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_add_dynamic(dev.as_raw(), &mut data.0) })?; + Ok(Self { + dev: dev.clone(), + freq: data.freq(), + }) + } +} + +impl Drop for Token { + fn drop(&mut self) { + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_remove(self.dev.as_raw(), self.freq.into()) }; + } +} + +/// OPP data. +/// +/// Rust abstraction for the C `struct dev_pm_opp_data`, used to define operating performance +/// points (OPPs) dynamically. +/// +/// ## Examples +/// +/// The following example demonstrates how to create an [`OPP`] with [`Data`]. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Data, MicroVolt, Token}; +/// use kernel::types::ARef; +/// +/// fn create_opp(dev: &ARef, freq: Hertz, volt: MicroVolt, level: u32) -> Result { +/// let data = Data::new(freq, volt, level, false); +/// +/// // OPP is removed once token goes out of scope. +/// data.add_opp(dev) +/// } +/// ``` +#[repr(transparent)] +pub struct Data(bindings::dev_pm_opp_data); + +impl Data { + /// Creates a new instance of [`Data`]. + /// + /// This can be used to define a dynamic OPP to be added to a device. + pub fn new(freq: Hertz, volt: MicroVolt, level: u32, turbo: bool) -> Self { + Self(bindings::dev_pm_opp_data { + turbo, + freq: freq.into(), + u_volt: volt.into(), + level, + }) + } + + /// Adds an [`OPP`] dynamically. + /// + /// Returns a [`Token`] that ensures the OPP is automatically removed + /// when it goes out of scope. + #[inline] + pub fn add_opp(self, dev: &ARef) -> Result { + Token::new(dev, self) + } + + /// Returns the frequency associated with this OPP data. + #[inline] + fn freq(&self) -> Hertz { + Hertz(self.0.freq) + } +} + +/// A reference-counted Operating performance point (OPP). +/// +/// Rust abstraction for the C `struct dev_pm_opp`. +/// +/// # Invariants +/// +/// The pointer stored in `Self` is non-null and valid for the lifetime of the [`OPP`]. +/// +/// Instances of this type are reference-counted. The reference count is incremented by the +/// `dev_pm_opp_get` function and decremented by `dev_pm_opp_put`. The Rust type `ARef` +/// represents a pointer that owns a reference count on the [`OPP`]. +/// +/// A reference to the [`OPP`], &[`OPP`], isn't refcounted by the Rust code. +#[repr(transparent)] +pub struct OPP(Opaque); + +/// SAFETY: It is okay to send the ownership of [`OPP`] across thread boundaries. +unsafe impl Send for OPP {} + +/// SAFETY: It is okay to access [`OPP`] through shared references from other threads because we're +/// either accessing properties that don't change or that are properly synchronised by C code. +unsafe impl Sync for OPP {} + +/// SAFETY: The type invariants guarantee that [`OPP`] is always refcounted. +unsafe impl AlwaysRefCounted for OPP { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::dev_pm_opp_get(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::dev_pm_opp_put(obj.cast().as_ptr()) } + } +} + +impl OPP { + /// Creates an owned reference to a [`OPP`] from a valid pointer. + /// + /// The refcount is incremented by the C code and will be decremented by `dec_ref` when the + /// [`ARef`] object is dropped. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and the refcount of the [`OPP`] is incremented. + /// The caller must also ensure that it doesn't explicitly drop the refcount of the [`OPP`], as + /// the returned [`ARef`] object takes over the refcount increment on the underlying object and + /// the same will be dropped along with it. + pub unsafe fn from_raw_opp_owned(ptr: *mut bindings::dev_pm_opp) -> Result> { + let ptr = ptr::NonNull::new(ptr).ok_or(ENODEV)?; + + // SAFETY: The safety requirements guarantee the validity of the pointer. + // + // INVARIANT: The reference-count is decremented when [`OPP`] goes out of scope. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Creates a reference to a [`OPP`] from a valid pointer. + /// + /// The refcount is not updated by the Rust API unless the returned reference is converted to + /// an [`ARef`] object. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for the duration of `'a`. + #[inline] + pub unsafe fn from_raw_opp<'a>(ptr: *mut bindings::dev_pm_opp) -> Result<&'a Self> { + // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the + // duration of 'a. The cast is okay because [`OPP`] is `repr(transparent)`. + Ok(unsafe { &*ptr.cast() }) + } + + #[inline] + fn as_raw(&self) -> *mut bindings::dev_pm_opp { + self.0.get() + } + + /// Returns the frequency of an [`OPP`]. + pub fn freq(&self, index: Option) -> Hertz { + let index = index.unwrap_or(0); + + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + Hertz(unsafe { bindings::dev_pm_opp_get_freq_indexed(self.as_raw(), index) }) + } + + /// Returns the voltage of an [`OPP`]. + #[inline] + pub fn voltage(&self) -> MicroVolt { + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + MicroVolt(unsafe { bindings::dev_pm_opp_get_voltage(self.as_raw()) }) + } + + /// Returns the level of an [`OPP`]. + #[inline] + pub fn level(&self) -> u32 { + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_get_level(self.as_raw()) } + } + + /// Returns the power of an [`OPP`]. + #[inline] + pub fn power(&self) -> MicroWatt { + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + MicroWatt(unsafe { bindings::dev_pm_opp_get_power(self.as_raw()) }) + } + + /// Returns the required pstate of an [`OPP`]. + #[inline] + pub fn required_pstate(&self, index: u32) -> u32 { + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_get_required_pstate(self.as_raw(), index) } + } + + /// Returns true if the [`OPP`] is turbo. + #[inline] + pub fn is_turbo(&self) -> bool { + // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_is_turbo(self.as_raw()) } + } +} From patchwork Mon May 19 07:07:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891246 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) (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 0A1B7266B59 for ; Mon, 19 May 2025 07:08:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638506; cv=none; b=A0Os4kUZSh2bMEmsNyX343v2nSyJjbs6QL+kouEZHD7w+LrQT7YEvHirz+wK9RNnNKts3UhLTCMBBBdPbSy2wk92J111aHcyWxgY3fDx5q93I9kbonLjb0WRwSvni4kLKIyh8ypv54etGqAtOz8+a7gUOLwo/J4oVhSjUsBc0Jo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638506; c=relaxed/simple; bh=GQZ/C13qepiseJqyRqcGLUXRE9t1VF7vW3VUh3qqVoY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iJBcmJiRjgXR8Vj2jeJ5LKAmTJxDyTVvkAzYKDB6m3cqBHhBt4NHXOPavJ27u0rr+BCiPnM2Zrqty6cU7ZdYPF6yuPvKvWSH1LeBoCTgq7OkwtAV/NLy8ilYb2HCNUFV0VCXwC9pmhGa35tPyXHEJ8TPSnNjmNleHtFQ7bOg63A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=D9YCbw7Z; arc=none smtp.client-ip=209.85.210.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="D9YCbw7Z" Received: by mail-pf1-f175.google.com with SMTP id d2e1a72fcca58-742c7a52e97so676290b3a.3 for ; Mon, 19 May 2025 00:08:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638503; x=1748243303; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=81nTFGvkA/IBrcIFlaJ92uVSha8rn1gDNqnm+5GgOQw=; b=D9YCbw7ZApPOTbxWi04xxtq55Q2gvReahm1BH5ikGbzOGhSkb8OPkurYUsYSmNxtyj saYfrEiXGzJGZgrrxARe7A0W2Q78sAj3Zqss3U9mqxMroIt9M0V3Yd4qiNDno1wf3FJO Ylp853auef4/9/ZdtikCqfSsvlXVSucc558p2MFtcDQOHG/0ReYyWpZoqqZLtSqTeNbt Y5sVbdA48cjUNrXYOI8on5dNl/CC23f8WfezUYfiN94ryJUixMUtS80d75oGTtJwuULk PtomNgdDr+YBHhHBOccYVxzd/xb0/DsvLw94jam6lBJIkEh013xRAr51hzItkNA5yYm4 P7og== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638503; x=1748243303; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=81nTFGvkA/IBrcIFlaJ92uVSha8rn1gDNqnm+5GgOQw=; b=ImmuRCL/q1Zrk2C6v02DDyrluf5IC4VbRT1qVuMLWg/x7OtJ2qU+rath63TEI+d9mn 1M0xVu34gcvwHINE8QrsRp3AaCWdTTJbNrAHN3ZT9h3dT1H+HwcNDgNsGDi+Rym8QvnM TXx/lr+rEoGkGqSz/Rn71Ii+fl7+3bCwbsLrO6jACgqh2nQFtzCo5q4+UJqy3WYTOThZ xesJAl3WbTD8nMiYXgDqLglYG36HGUmngsWDawKPU+ULhZioHTRGWQCqqaGyOPivcr51 CtICYfzemxroLE7Rg7xmBaft2SiybNeT9IT8IKj3IqoYPW8SUoi+OW1eMMxyAFAd0LC7 Z9QQ== X-Forwarded-Encrypted: i=1; AJvYcCX99fEG36cXedqmKrkkLsbXyTSX5587+LQn2mGzZkGwJgPp9lLcysIFs+c+hNz+97Muo4Lg6DDBbg==@vger.kernel.org X-Gm-Message-State: AOJu0YzChhp9tDd3tSHQGgfB2dhKIxjGnH2Jq0cgPIllRhfIm6UyD772 e46Ic/oy87jj0oZO3AZsaYhbp0O3uftfIvyEaldEErF0vBfRdQsrZLVIR+iEnlI79p8= X-Gm-Gg: ASbGncvuk4mUXCh9V6Ej95msJKFOMuQt0nMboJ026GeNOJhnR87dsILklGPXZehjxLy VPInCSwJCPO/HgF71sNMXQ/g2n7lyXN4qE7CkPkWu9S8Q4/yPHExD2jvElx/Bd/1GQyLN5nebIq VS5/jdLoULNkpY+MDSPEBfjH90+9Q9ReXtspGbBVnMNwBmXqHu9UTTgXEr6p1RbuWxrSJVGuU56 IoguGHzaFDUw4Mnfk8LbKjDT8T4iwsEZR78R3NY0hGXB1cmkycxlVZooywVeYMyO5uoPcnW+CXf bUcUjGNb0TDCI7vMacgeDaY1xA3UtgUzkpeyDEtgbAHOWouqa/1y X-Google-Smtp-Source: AGHT+IHCRYPAYiAYx5Tx1UMCGZquBQ1QMiYKJ2/LxxD9n3pNoawWDcoNg8DtRj0MGmDb4xlY0ykjbA== X-Received: by 2002:a05:6a20:6a28:b0:201:4061:bd94 with SMTP id adf61e73a8af0-2170ccaf4e4mr15295210637.19.1747638502864; Mon, 19 May 2025 00:08:22 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-231d4e981desm53007485ad.124.2025.05.19.00.08.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:08:22 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Nishanth Menon , Stephen Boyd , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , linux-kernel@vger.kernel.org Subject: [PATCH V12 10/15] rust: opp: Add abstractions for the configuration options Date: Mon, 19 May 2025 12:37:15 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Introduce Rust abstractions for the OPP core configuration options, enabling safe access to various configurable aspects of the OPP framework. Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 295 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 2 deletions(-) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index 18f55c00a4d6..7be6fd33d93f 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -12,12 +12,29 @@ clk::Hertz, cpumask::{Cpumask, CpumaskVar}, device::Device, - error::{code::*, from_err_ptr, to_result, Error, Result}, + error::{code::*, from_err_ptr, from_result, to_result, Error, Result, VTABLE_DEFAULT_ERROR}, ffi::c_ulong, + prelude::*, + str::CString, types::{ARef, AlwaysRefCounted, Opaque}, }; -use core::ptr; +use core::{marker::PhantomData, ptr}; + +use macros::vtable; + +/// Creates a null-terminated slice of pointers to [`Cstring`]s. +fn to_c_str_array(names: &[CString]) -> Result> { + // Allocated a null-terminated vector of pointers. + let mut list = KVec::with_capacity(names.len() + 1, GFP_KERNEL)?; + + for name in names.iter() { + list.push(name.as_ptr() as _, GFP_KERNEL)?; + } + + list.push(ptr::null(), GFP_KERNEL)?; + Ok(list) +} /// The voltage unit. /// @@ -205,6 +222,280 @@ pub enum SearchType { Ceil, } +/// OPP configuration callbacks. +/// +/// Implement this trait to customize OPP clock and regulator setup for your device. +#[vtable] +pub trait ConfigOps { + /// This is typically used to scale clocks when transitioning between OPPs. + #[inline] + fn config_clks(_dev: &Device, _table: &Table, _opp: &OPP, _scaling_down: bool) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// This provides access to the old and new OPPs, allowing for safe regulator adjustments. + #[inline] + fn config_regulators( + _dev: &Device, + _opp_old: &OPP, + _opp_new: &OPP, + _data: *mut *mut bindings::regulator, + _count: u32, + ) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } +} + +/// OPP configuration token. +/// +/// Returned by the OPP core when configuration is applied to a [`Device`]. The associated +/// configuration is automatically cleared when the token is dropped. +pub struct ConfigToken(i32); + +impl Drop for ConfigToken { + fn drop(&mut self) { + // SAFETY: This is the same token value returned by the C code via `dev_pm_opp_set_config`. + unsafe { bindings::dev_pm_opp_clear_config(self.0) }; + } +} + +/// OPP configurations. +/// +/// Rust abstraction for the C `struct dev_pm_opp_config`. +/// +/// ## Examples +/// +/// The following example demonstrates how to set OPP property-name configuration for a [`Device`]. +/// +/// ``` +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Config, ConfigOps, ConfigToken}; +/// use kernel::str::CString; +/// use kernel::types::ARef; +/// use kernel::macros::vtable; +/// +/// #[derive(Default)] +/// struct Driver; +/// +/// #[vtable] +/// impl ConfigOps for Driver {} +/// +/// fn configure(dev: &ARef) -> Result { +/// let name = CString::try_from_fmt(fmt!("{}", "slow"))?; +/// +/// // The OPP configuration is cleared once the [`ConfigToken`] goes out of scope. +/// Config::::new() +/// .set_prop_name(name)? +/// .set(dev) +/// } +/// ``` +#[derive(Default)] +pub struct Config +where + T: Default, +{ + clk_names: Option>, + prop_name: Option, + regulator_names: Option>, + supported_hw: Option>, + + // Tuple containing (required device, index) + required_dev: Option<(ARef, u32)>, + _data: PhantomData, +} + +impl Config { + /// Creates a new instance of [`Config`]. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Initializes clock names. + pub fn set_clk_names(mut self, names: KVec) -> Result { + if self.clk_names.is_some() { + return Err(EBUSY); + } + + if names.is_empty() { + return Err(EINVAL); + } + + self.clk_names = Some(names); + Ok(self) + } + + /// Initializes property name. + pub fn set_prop_name(mut self, name: CString) -> Result { + if self.prop_name.is_some() { + return Err(EBUSY); + } + + self.prop_name = Some(name); + Ok(self) + } + + /// Initializes regulator names. + pub fn set_regulator_names(mut self, names: KVec) -> Result { + if self.regulator_names.is_some() { + return Err(EBUSY); + } + + if names.is_empty() { + return Err(EINVAL); + } + + self.regulator_names = Some(names); + + Ok(self) + } + + /// Initializes required devices. + pub fn set_required_dev(mut self, dev: ARef, index: u32) -> Result { + if self.required_dev.is_some() { + return Err(EBUSY); + } + + self.required_dev = Some((dev, index)); + Ok(self) + } + + /// Initializes supported hardware. + pub fn set_supported_hw(mut self, hw: KVec) -> Result { + if self.supported_hw.is_some() { + return Err(EBUSY); + } + + if hw.is_empty() { + return Err(EINVAL); + } + + self.supported_hw = Some(hw); + Ok(self) + } + + /// Sets the configuration with the OPP core. + /// + /// The returned [`ConfigToken`] will remove the configuration when dropped. + pub fn set(self, dev: &Device) -> Result { + let (_clk_list, clk_names) = match &self.clk_names { + Some(x) => { + let list = to_c_str_array(x)?; + let ptr = list.as_ptr(); + (Some(list), ptr) + } + None => (None, ptr::null()), + }; + + let (_regulator_list, regulator_names) = match &self.regulator_names { + Some(x) => { + let list = to_c_str_array(x)?; + let ptr = list.as_ptr(); + (Some(list), ptr) + } + None => (None, ptr::null()), + }; + + let prop_name = self + .prop_name + .as_ref() + .map_or(ptr::null(), |p| p.as_char_ptr()); + + let (supported_hw, supported_hw_count) = self + .supported_hw + .as_ref() + .map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32)); + + let (required_dev, required_dev_index) = self + .required_dev + .as_ref() + .map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx)); + + let mut config = bindings::dev_pm_opp_config { + clk_names, + config_clks: if T::HAS_CONFIG_CLKS { + Some(Self::config_clks) + } else { + None + }, + prop_name, + regulator_names, + config_regulators: if T::HAS_CONFIG_REGULATORS { + Some(Self::config_regulators) + } else { + None + }, + supported_hw, + supported_hw_count, + + required_dev, + required_dev_index, + }; + + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. The OPP core guarantees not to access fields of [`Config`] after this call + // and so we don't need to save a copy of them for future use. + let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ConfigToken(ret)) + } + } + + /// Config's clk callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn config_clks( + dev: *mut bindings::device, + opp_table: *mut bindings::opp_table, + opp: *mut bindings::dev_pm_opp, + _data: *mut kernel::ffi::c_void, + scaling_down: bool, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: 'dev' is guaranteed by the C code to be valid. + let dev = unsafe { Device::get_device(dev) }; + T::config_clks( + &dev, + // SAFETY: 'opp_table' is guaranteed by the C code to be valid. + &unsafe { Table::from_raw_table(opp_table, &dev) }, + // SAFETY: 'opp' is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp(opp)? }, + scaling_down, + ) + .map(|()| 0) + }) + } + + /// Config's regulator callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn config_regulators( + dev: *mut bindings::device, + old_opp: *mut bindings::dev_pm_opp, + new_opp: *mut bindings::dev_pm_opp, + regulators: *mut *mut bindings::regulator, + count: kernel::ffi::c_uint, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: 'dev' is guaranteed by the C code to be valid. + let dev = unsafe { Device::get_device(dev) }; + T::config_regulators( + &dev, + // SAFETY: 'old_opp' is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp(old_opp)? }, + // SAFETY: 'new_opp' is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp(new_opp)? }, + regulators, + count, + ) + .map(|()| 0) + }) + } +} + /// A reference-counted OPP table. /// /// Rust abstraction for the C `struct opp_table`. From patchwork Mon May 19 07:07:17 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891245 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (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 4C02E26B087 for ; Mon, 19 May 2025 07:08:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638515; cv=none; b=jX8ivY9G0iQjD3/YCXSMZfcth/FgPVC3zqp7MjiLckLt2nk8fHgBfvDoOY6GoT8rGOW/EgcIdksiQbHmHTWRwgCAQdd5d8a0XjqUGbCjPcLe1x00fZwy2B458UP//qOx8kc2REr2YDGUUOHUDsZUyErXQisRe26DAu3jRQuDIQw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638515; c=relaxed/simple; bh=w75xk8hcGxaoso0uJH+G6H2tRb4FrllY4PtzoOGYwhU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jGrWOfw7wb4ny9uVZ0IodLNyHMNpWQ5Us5o4OeFUsByL+FgmDZ4FCZoLA5mpKtR1Fhh6Y/dVeVFIpVZbcUBayeCjySnvj29VufndwhfzXC6iipeakUhNM0zQRQCiMGZEu2WQEtumNhou6ZqC206z/N76sZeaQJblpWv1CF/nX4o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=uzdhqiMK; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="uzdhqiMK" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-23035b3edf1so32633035ad.3 for ; Mon, 19 May 2025 00:08:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638511; x=1748243311; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DyCYDi1jmiTRdeD7r0ayi/v9y79Pi1U/bl1JrfBBA8w=; b=uzdhqiMKVtUw6fFV3+qQsxZoC5sc7YKpeR5mMJvsie0WTZB9J5qSXEFBZMFBRuxkMw tW0r/IS4nuTBMrKCUs9icWpeusiJTt3x/jeNYhIzejfu26tIGbd7MGWnY2IlqwVRdKWl t4r7MWOhBwbFxRvVCaFhH7FOf2nncvPiYRZND7MNi5KzStk4AuWEy0D88dWtvWyr71kI WGk4o9pIwAQkhWhUASSvB+JljluxuNYoMupLDWR2khybTkMK/b8TRH+ocpSyub2wG1Ms 9U8OULZkqj3drhrN6mVMtRT3yX5cCeLTGLCMncOVpaEESWudbN89RLhi2qFTKbgroYcT zdzA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638511; x=1748243311; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DyCYDi1jmiTRdeD7r0ayi/v9y79Pi1U/bl1JrfBBA8w=; b=i3fuNXubgz1Nf1hOBW8laru4OWMf5t91PBbqSguoD4LpnEOtC0BibrIhqZsa8JM9HQ QkyOI3E7ljK7u5cbUpU24Z4SNSw1vK1HyfSemPX5gZ4zLNuBbhdlrKwvOkMhd7TtR9Z+ eM9GZQW1IDO9lzApbye3vqzGznYzYdB8bZOtdBDC5iwiEVD41tDRzaAU+BrBvHz4oyv+ FVJZWpfTq0Rh+0CL1NdgQXV0pS2+52mqINHeuL3Iqgs3Q10tCfHgqgUsCirMaQ+u9af8 2+CfES4VpagojZ+2UfBMEPsrfR3xvBamhLY+2h5OtBzLabdOhtMM1aDpP3ifoXLkDVEY uRdQ== X-Gm-Message-State: AOJu0YwtLP3HiB5gqc8/CyVutVynkxLf3iHQCVhnla6IeO8CZSfG/WcF XWneqSLx2yt0kmg86P9k0gdvOjwk+XzcXIPHm4urpmGqi5CkHctHmbuMtkjn7T/oim0= X-Gm-Gg: ASbGncs22veT01vU0/9vcUXBj1LfmKaX7PJcL+7BJEONna7mH11h5umHTaSqu2UvbR+ G7T8ivCc9tiqFIuR2ckZuXianKFf5DX2gZkZ2x3vrNcpZmaqP9RNDh8cyA7j2AIbCKk0zq+3T1G mfIbX5w2KU1R5Ifp0J0XPckpPHefq4xjVQRwnqbyaDQVECDM+HOOp51JvdEFdAlUn79tZ/thTg4 sfHNlwjX+3D7So19e2JThYL1rJTQVPWsM1aln8BzZz5EviySngx2itf4mWDJSSFjZ0RVNQFu9ka 6O6DEBTy9EbxuW9/nqyA7ui3jIFOLq0uIKbP/iOQhMQ13X+boAn0 X-Google-Smtp-Source: AGHT+IFp+mTkIdQA+QBEpF9I1WDmxW6BCz1216dFbfVhAVDgVNSZI0SSTQtOTG0aLfaPnStrV3lcug== X-Received: by 2002:a17:903:2285:b0:223:26da:4b8e with SMTP id d9443c01a7336-231d438a1a3mr176961085ad.4.1747638511390; Mon, 19 May 2025 00:08:31 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-231d4e98251sm52805555ad.138.2025.05.19.00.08.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:08:30 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , linux-kernel@vger.kernel.org Subject: [PATCH V12 12/15] rust: cpufreq: Extend abstractions for policy and driver ops Date: Mon, 19 May 2025 12:37:17 +0530 Message-Id: <2f11306b8a7f05b441256fbd9784ccc2d59b3356.1747634382.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Extend the cpufreq abstractions to include support for policy handling and driver operations. Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 463 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 461 insertions(+), 2 deletions(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 2aa024615d4d..4e6d85bd06f4 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -10,13 +10,25 @@ use crate::{ clk::Hertz, - error::{code::*, to_result, Result}, + cpumask, + device::Device, + error::{code::*, from_err_ptr, to_result, Result, VTABLE_DEFAULT_ERROR}, ffi::c_ulong, prelude::*, + types::ForeignOwnable, types::Opaque, }; -use core::{ops::Deref, pin::Pin}; +#[cfg(CONFIG_COMMON_CLK)] +use crate::clk::Clk; + +use core::{ + ops::{Deref, DerefMut}, + pin::Pin, + ptr, +}; + +use macros::vtable; /// Default transition latency value in nanoseconds. pub const ETERNAL_LATENCY_NS: u32 = bindings::CPUFREQ_ETERNAL as u32; @@ -362,3 +374,450 @@ pub fn to_table(mut self) -> Result { TableBox::new(self.entries) } } + +/// CPU frequency policy. +/// +/// Rust abstraction for the C `struct cpufreq_policy`. +/// +/// # Invariants +/// +/// A [`Policy`] instance always corresponds to a valid C `struct cpufreq_policy`. +/// +/// The callers must ensure that the `struct cpufreq_policy` is valid for access and remains valid +/// for the lifetime of the returned reference. +/// +/// ## Examples +/// +/// The following example demonstrates how to create a CPU frequency table. +/// +/// ``` +/// use kernel::cpufreq::{ETERNAL_LATENCY_NS, Policy}; +/// +/// fn update_policy(policy: &mut Policy) { +/// policy +/// .set_dvfs_possible_from_any_cpu(true) +/// .set_fast_switch_possible(true) +/// .set_transition_latency_ns(ETERNAL_LATENCY_NS); +/// +/// pr_info!("The policy details are: {:?}\n", (policy.cpu(), policy.cur())); +/// } +/// ``` +#[repr(transparent)] +pub struct Policy(Opaque); + +impl Policy { + /// Creates a reference to an existing `struct cpufreq_policy` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_policy) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Creates a mutable reference to an existing `struct cpufreq_policy` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy) -> &'a mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Returns a raw mutable pointer to the C `struct cpufreq_policy`. + #[inline] + fn as_raw(&self) -> *mut bindings::cpufreq_policy { + let this: *const Self = self; + this.cast_mut().cast() + } + + #[inline] + fn as_ref(&self) -> &bindings::cpufreq_policy { + // SAFETY: By the type invariant, the pointer stored in `self` is valid. + unsafe { &*self.as_raw() } + } + + #[inline] + fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy { + // SAFETY: By the type invariant, the pointer stored in `self` is valid. + unsafe { &mut *self.as_raw() } + } + + /// Returns the primary CPU for the [`Policy`]. + #[inline] + pub fn cpu(&self) -> u32 { + self.as_ref().cpu + } + + /// Returns the minimum frequency for the [`Policy`]. + #[inline] + pub fn min(&self) -> Hertz { + Hertz::from_khz(self.as_ref().min as usize) + } + + /// Set the minimum frequency for the [`Policy`]. + #[inline] + pub fn set_min(&mut self, min: Hertz) -> &mut Self { + self.as_mut_ref().min = min.as_khz() as u32; + self + } + + /// Returns the maximum frequency for the [`Policy`]. + #[inline] + pub fn max(&self) -> Hertz { + Hertz::from_khz(self.as_ref().max as usize) + } + + /// Set the maximum frequency for the [`Policy`]. + #[inline] + pub fn set_max(&mut self, max: Hertz) -> &mut Self { + self.as_mut_ref().max = max.as_khz() as u32; + self + } + + /// Returns the current frequency for the [`Policy`]. + #[inline] + pub fn cur(&self) -> Hertz { + Hertz::from_khz(self.as_ref().cur as usize) + } + + /// Returns the suspend frequency for the [`Policy`]. + #[inline] + pub fn suspend_freq(&self) -> Hertz { + Hertz::from_khz(self.as_ref().suspend_freq as usize) + } + + /// Sets the suspend frequency for the [`Policy`]. + #[inline] + pub fn set_suspend_freq(&mut self, freq: Hertz) -> &mut Self { + self.as_mut_ref().suspend_freq = freq.as_khz() as u32; + self + } + + /// Provides a wrapper to the generic suspend routine. + #[inline] + pub fn generic_suspend(&mut self) -> Result<()> { + // SAFETY: By the type invariant, the pointer stored in `self` is valid. + to_result(unsafe { bindings::cpufreq_generic_suspend(self.as_mut_ref()) }) + } + + /// Provides a wrapper to the generic get routine. + #[inline] + pub fn generic_get(&self) -> Result { + // SAFETY: By the type invariant, the pointer stored in `self` is valid. + Ok(unsafe { bindings::cpufreq_generic_get(self.cpu()) }) + } + + /// Provides a wrapper to the register with energy model using the OPP core. + #[cfg(CONFIG_PM_OPP)] + #[inline] + pub fn register_em_opp(&mut self) { + // SAFETY: By the type invariant, the pointer stored in `self` is valid. + unsafe { bindings::cpufreq_register_em_with_opp(self.as_mut_ref()) }; + } + + /// Gets [`cpumask::Cpumask`] for a cpufreq [`Policy`]. + #[inline] + pub fn cpus(&mut self) -> &mut cpumask::Cpumask { + // SAFETY: The pointer to `cpus` is valid for writing and remains valid for the lifetime of + // the returned reference. + unsafe { cpumask::CpumaskVar::as_mut_ref(&mut self.as_mut_ref().cpus) } + } + + /// Sets clock for the [`Policy`]. + /// + /// # Safety + /// + /// The caller must guarantee that the returned [`Clk`] is not dropped while it is getting used + /// by the C code. + #[cfg(CONFIG_COMMON_CLK)] + pub unsafe fn set_clk(&mut self, dev: &Device, name: Option<&CStr>) -> Result { + let clk = Clk::get(dev, name)?; + self.as_mut_ref().clk = clk.as_raw(); + Ok(clk) + } + + /// Allows / disallows frequency switching code to run on any CPU. + #[inline] + pub fn set_dvfs_possible_from_any_cpu(&mut self, val: bool) -> &mut Self { + self.as_mut_ref().dvfs_possible_from_any_cpu = val; + self + } + + /// Returns if fast switching of frequencies is possible or not. + #[inline] + pub fn fast_switch_possible(&self) -> bool { + self.as_ref().fast_switch_possible + } + + /// Enables / disables fast frequency switching. + #[inline] + pub fn set_fast_switch_possible(&mut self, val: bool) -> &mut Self { + self.as_mut_ref().fast_switch_possible = val; + self + } + + /// Sets transition latency (in nanoseconds) for the [`Policy`]. + #[inline] + pub fn set_transition_latency_ns(&mut self, latency_ns: u32) -> &mut Self { + self.as_mut_ref().cpuinfo.transition_latency = latency_ns; + self + } + + /// Sets cpuinfo `min_freq`. + #[inline] + pub fn set_cpuinfo_min_freq(&mut self, min_freq: Hertz) -> &mut Self { + self.as_mut_ref().cpuinfo.min_freq = min_freq.as_khz() as u32; + self + } + + /// Sets cpuinfo `max_freq`. + #[inline] + pub fn set_cpuinfo_max_freq(&mut self, max_freq: Hertz) -> &mut Self { + self.as_mut_ref().cpuinfo.max_freq = max_freq.as_khz() as u32; + self + } + + /// Set `transition_delay_us`, i.e. the minimum time between successive frequency change + /// requests. + #[inline] + pub fn set_transition_delay_us(&mut self, transition_delay_us: u32) -> &mut Self { + self.as_mut_ref().transition_delay_us = transition_delay_us; + self + } + + /// Returns reference to the CPU frequency [`Table`] for the [`Policy`]. + pub fn freq_table(&self) -> Result<&Table> { + if self.as_ref().freq_table.is_null() { + return Err(EINVAL); + } + + // SAFETY: The `freq_table` is guaranteed to be valid for reading and remains valid for the + // lifetime of the returned reference. + Ok(unsafe { Table::from_raw(self.as_ref().freq_table) }) + } + + /// Sets the CPU frequency [`Table`] for the [`Policy`]. + /// + /// # Safety + /// + /// The caller must guarantee that the [`Table`] is not dropped while it is getting used by the + /// C code. + #[inline] + pub unsafe fn set_freq_table(&mut self, table: &Table) -> &mut Self { + self.as_mut_ref().freq_table = table.as_raw(); + self + } + + /// Returns the [`Policy`]'s private data. + pub fn data(&mut self) -> Option<::Borrowed<'_>> { + if self.as_ref().driver_data.is_null() { + None + } else { + // SAFETY: The data is earlier set from [`set_data`]. + Some(unsafe { T::borrow(self.as_ref().driver_data) }) + } + } + + /// Sets the private data of the [`Policy`] using a foreign-ownable wrapper. + /// + /// # Errors + /// + /// Returns `EBUSY` if private data is already set. + fn set_data(&mut self, data: T) -> Result<()> { + if self.as_ref().driver_data.is_null() { + // Transfer the ownership of the data to the foreign interface. + self.as_mut_ref().driver_data = ::into_foreign(data) as _; + Ok(()) + } else { + Err(EBUSY) + } + } + + /// Clears and returns ownership of the private data. + fn clear_data(&mut self) -> Option { + if self.as_ref().driver_data.is_null() { + None + } else { + let data = Some( + // SAFETY: The data is earlier set by us from [`set_data`]. It is safe to take + // back the ownership of the data from the foreign interface. + unsafe { ::from_foreign(self.as_ref().driver_data) }, + ); + self.as_mut_ref().driver_data = ptr::null_mut(); + data + } + } +} + +/// CPU frequency policy created from a CPU number. +/// +/// This struct represents the CPU frequency policy obtained for a specific CPU, providing safe +/// access to the underlying `cpufreq_policy` and ensuring proper cleanup when the `PolicyCpu` is +/// dropped. +struct PolicyCpu<'a>(&'a mut Policy); + +impl<'a> PolicyCpu<'a> { + fn from_cpu(cpu: u32) -> Result { + // SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU. + let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(cpu) })?; + + Ok(Self( + // SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of + // the returned reference. + unsafe { Policy::from_raw_mut(ptr) }, + )) + } +} + +impl<'a> Deref for PolicyCpu<'a> { + type Target = Policy; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> DerefMut for PolicyCpu<'a> { + fn deref_mut(&mut self) -> &mut Policy { + self.0 + } +} + +impl<'a> Drop for PolicyCpu<'a> { + fn drop(&mut self) { + // SAFETY: The underlying pointer is guaranteed to be valid for the lifetime of `self`. + unsafe { bindings::cpufreq_cpu_put(self.0.as_raw()) }; + } +} + +/// CPU frequency driver. +/// +/// Implement this trait to provide a CPU frequency driver and its callbacks. +/// +/// Reference: +#[vtable] +pub trait Driver { + /// Driver's name. + const NAME: &'static CStr; + + /// Driver's flags. + const FLAGS: u16; + + /// Boost support. + const BOOST_ENABLED: bool; + + /// Policy specific data. + /// + /// Require that `PData` implements `ForeignOwnable`. We guarantee to never move the underlying + /// wrapped data structure. + type PData: ForeignOwnable; + + /// Driver's `init` callback. + fn init(policy: &mut Policy) -> Result; + + /// Driver's `exit` callback. + fn exit(_policy: &mut Policy, _data: Option) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `online` callback. + fn online(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `offline` callback. + fn offline(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `suspend` callback. + fn suspend(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `resume` callback. + fn resume(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `ready` callback. + fn ready(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `verify` callback. + fn verify(data: &mut PolicyData) -> Result<()>; + + /// Driver's `setpolicy` callback. + fn setpolicy(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target` callback. + fn target(_policy: &mut Policy, _target_freq: u32, _relation: Relation) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target_index` callback. + fn target_index(_policy: &mut Policy, _index: TableIndex) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `fast_switch` callback. + fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `adjust_perf` callback. + fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `get_intermediate` callback. + fn get_intermediate(_policy: &mut Policy, _index: TableIndex) -> u32 { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target_intermediate` callback. + fn target_intermediate(_policy: &mut Policy, _index: TableIndex) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `get` callback. + fn get(_policy: &mut Policy) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `update_limits` callback. + fn update_limits(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `bios_limit` callback. + fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `set_boost` callback. + fn set_boost(_policy: &mut Policy, _state: i32) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `register_em` callback. + fn register_em(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } +} From patchwork Mon May 19 07:07:19 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 891244 Received: from mail-pg1-f179.google.com (mail-pg1-f179.google.com [209.85.215.179]) (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 A32EF26B2C0 for ; Mon, 19 May 2025 07:08:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638521; cv=none; b=BBTvrYLBrZioIuy8CwtDia3Qq8CcUHT8fes+vq46wVirzmI6Bnl+ssmnJiOZS4j2I+1imDxayXgptSNO2ICuxc1NXMCTfDR6qdPwiZAUHABVCunYTL79IgPrJTxNr/dHqGvgDlhhOg5qBHq/4Zm8cQE2onO1Mixg8R5BMzlXEaY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747638521; c=relaxed/simple; bh=QB1NDnIN2+10EfH7zVK4+js+l6z1NvQ0KLDj/lkYjH0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oXc0miy0HX/0tAXAweKU/BSjeRN1jGFb3uCRxPySQbqpsYaAFMPHOE4Q5NRAfQH+jGRhjAMuoDcJgzX6gca/twMiB1LWeryQYPWJh1X/c/egMu3/6bIyAAdZk7iGGDCvRF5xJhvs4arbVeVSt3Y7if5plOoM0GIVdSHyemIJQKo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=O+cGX1l+; arc=none smtp.client-ip=209.85.215.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="O+cGX1l+" Received: by mail-pg1-f179.google.com with SMTP id 41be03b00d2f7-b0b2d1f2845so2785538a12.3 for ; Mon, 19 May 2025 00:08:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1747638519; x=1748243319; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zV8iUMBX+j7TsKW8lH5BBlCVzlR9XbeiRbiHVvWBeLs=; b=O+cGX1l++cFgpf0MJm1qSDy7j8sY3gwFAhZiW6UaS2xgQSqUCvK6PNz+2/VXJCT0ch PzRG8QT+RhDAe1Ci4DPdXatS/MJ4v+/qqd+O/EfrFDdtsPsrwOljQwV+BSvK1BRzd7FT 1oleRszUJDnkcHmZQOge1VvOLKkrM57M2Tbj/cJHQumD6rW6B8t3UKr4usLP3Z1oCVs/ pdbp2OMQrW3hep+hrHwtp/BP3L9axskR1tUeCYcE43N0tVffVuK8keEt4mR8wD7mlepE mneUVrDBtRLUOPoi23YuJhlSuyozokCi4sOtLBXJb9pjPvIynnMX5Gff8PFl0tUgKTIu Cz9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747638519; x=1748243319; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zV8iUMBX+j7TsKW8lH5BBlCVzlR9XbeiRbiHVvWBeLs=; b=N2H6YqYAJ13kLOpXCrIHpRaKRqiI9ERfYaQMrHxgGOhNryBsXcTHPFMu2P845tJA4q bg/Qrkjuz7Bhi9lE9TQ/rOFklwLDnqyBhUVccGcXDvN/sm3oUDNYBOqOb4FR0PvfQ5Z8 qr8zIEs0Y/HURIQFOZoVJdWawJUHqfMvpoucaWKZrCh/6x4BMZA47EMB1XmtSVtVJpZ4 bkhkRDWxuhw6/zb/0v9quA5RGUqPpPBcWVvF+djbF1oK8uo7jjQDEQbaGFzsVFwsLgYu TRImM2q6tDmoJ31KCBHwds07jBOT65QNMjMg5rnYvUeeZdBL1URf1fYdzwb2pQa4ViKH l7bw== X-Forwarded-Encrypted: i=1; AJvYcCVjQ3oBmVZzX0RuWGtc2wlFJbtBPg225vVrdnzVthLgXjNefcGkQa31YwEyBdDijAb7tgP54ZHAhw==@vger.kernel.org X-Gm-Message-State: AOJu0YwWqnPj92DvSlve6Dr/VeeIL5c6985JyurkvFKnhqoj9b1Ry0R+ u1DPHrWsRV/DkMu/yDXQ6/EZ5cOEiv9OueJ5cRmRC7UxiET01iR70ApP8FVyV60TdrM= X-Gm-Gg: ASbGncu/xU0209AmElyIZ/ZD7s7CtZ7Ifa9sKb710RL8yAUNEDY4rrsQBjpuZqKhB5p pjJ6I5JcAi4bV0dBdQ3HbqapwmgziNhtTGFwGur/T3iEDp1vQD9SuyS265m/aK/3uOA7eLeIXG7 gMVop4N6So63FTaMxmAUmnh1j/j55m6eR3Q+BJEuHFNGk8sRr93bOjfoUiPGQYHhLEo8gbOGKib OCx0RgoEt+hyn6uuhlAxlLbkMoaSeND81NY6RxhwgLP06S1/IlKS8Ef+1DT+a3KwJP22Eo9mZrI Qxfx+ox9tfLb8511dlHgtGkTdiOblwLl7e7LJWRd2qav2b/ik6lu95A4qvWFOpM= X-Google-Smtp-Source: AGHT+IHEjkPhd0v9dHUnrSumRKqtP+XO8qTwXgH5hnKDzbAp7zerm30lnT7DxIhX8VkHWyBQkdAPVw== X-Received: by 2002:a17:903:18d:b0:224:c47:cbd with SMTP id d9443c01a7336-231d3257fc6mr161226955ad.0.1747638518855; Mon, 19 May 2025 00:08:38 -0700 (PDT) Received: from localhost ([122.172.81.72]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-231d4eba368sm53296525ad.200.2025.05.19.00.08.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 May 2025 00:08:38 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Nishanth Menon , Stephen Boyd , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?utf-8?q?Alex_Benn?= =?utf-8?q?=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , Andrew Ballance , linux-kernel@vger.kernel.org Subject: [PATCH V12 14/15] rust: opp: Extend OPP abstractions with cpufreq support Date: Mon, 19 May 2025 12:37:19 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Extend the OPP abstractions to include support for interacting with the cpufreq core, including the ability to retrieve frequency tables from OPP table. Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index 7be6fd33d93f..1e5fd9887b3a 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -19,6 +19,69 @@ types::{ARef, AlwaysRefCounted, Opaque}, }; +#[cfg(CONFIG_CPU_FREQ)] +/// Frequency table implementation. +mod freq { + use super::*; + use crate::cpufreq; + use core::ops::Deref; + + /// OPP frequency table. + /// + /// A [`cpufreq::Table`] created from [`Table`]. + pub struct FreqTable { + dev: ARef, + ptr: *mut bindings::cpufreq_frequency_table, + } + + impl FreqTable { + /// Creates a new instance of [`FreqTable`] from [`Table`]. + pub(crate) fn new(table: &Table) -> Result { + let mut ptr: *mut bindings::cpufreq_frequency_table = ptr::null_mut(); + + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_init_cpufreq_table(table.dev.as_raw(), &mut ptr) + })?; + + Ok(Self { + dev: table.dev.clone(), + ptr, + }) + } + + /// Returns a reference to the underlying [`cpufreq::Table`]. + #[inline] + fn table(&self) -> &cpufreq::Table { + // SAFETY: The `ptr` is guaranteed by the C code to be valid. + unsafe { cpufreq::Table::from_raw(self.ptr) } + } + } + + impl Deref for FreqTable { + type Target = cpufreq::Table; + + #[inline] + fn deref(&self) -> &Self::Target { + self.table() + } + } + + impl Drop for FreqTable { + fn drop(&mut self) { + // SAFETY: The pointer was created via `dev_pm_opp_init_cpufreq_table`, and is only + // freed here. + unsafe { + bindings::dev_pm_opp_free_cpufreq_table(self.dev.as_raw(), &mut self.as_raw()) + }; + } + } +} + +#[cfg(CONFIG_CPU_FREQ)] +pub use freq::FreqTable; + use core::{marker::PhantomData, ptr}; use macros::vtable; @@ -753,6 +816,13 @@ pub fn adjust_voltage( }) } + /// Creates [`FreqTable`] from [`Table`]. + #[cfg(CONFIG_CPU_FREQ)] + #[inline] + pub fn cpufreq_table(&mut self) -> Result { + FreqTable::new(self) + } + /// Configures device with [`OPP`] matching the frequency value. #[inline] pub fn set_rate(&self, freq: Hertz) -> Result<()> {