From: Boqun Feng <boqun.feng@gmail.com>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>,
"Miguel Ojeda" <miguel.ojeda.sandonis@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Wedson Almeida Filho" <wedsonaf@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@samsung.com>,
"Alice Ryhl" <aliceryhl@google.com>,
linux-pm@vger.kernel.org,
"Vincent Guittot" <vincent.guittot@linaro.org>,
"Stephen Boyd" <sboyd@kernel.org>, "Nishanth Menon" <nm@ti.com>,
rust-for-linux@vger.kernel.org,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Erik Schilling" <erik.schilling@linaro.org>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Joakim Bech" <joakim.bech@linaro.org>,
"Rob Herring" <robh@kernel.org>,
linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH V3 1/8] rust: Add initial bindings for OPP framework
Date: Fri, 5 Jul 2024 11:19:33 -0700 [thread overview]
Message-ID: <Zog5NYptZRaqbUBz@boqun-archlinux> (raw)
In-Reply-To: <20240705110228.qqhhynbwwuwpcdeo@vireshk-i7>
On Fri, Jul 05, 2024 at 04:32:28PM +0530, Viresh Kumar wrote:
> Hi Boqun,
>
> On 03-07-24, 08:34, Boqun Feng wrote:
> > On Wed, Jul 03, 2024 at 12:44:26PM +0530, Viresh Kumar wrote:
> > > +/// Operating performance point (OPP).
> > > +///
> > > +/// # Invariants
> > > +///
> > > +/// The pointer stored in `Self` is non-null and valid for the lifetime of the ARef instance. In
> > > +/// particular, the ARef instance owns an increment on underlying object´s reference count.
> >
> > Since you use `ARef` pattern now, you may want to rewrite this
> > "invariants".
>
> I copied it from the device's documentation. What all details should I
> be writing here ? A link to some other implementation would be useful.
>
"Invariants" defines "what's a valid instance of a type", so here I
think you could drop the "# Invariants" section at all, unless you need
to use some of the invariants of fields in `dev_mp_opp` as a
justification for safety. For example if you have a pointer in
`dev_mp_opp`, and it's always pointing to a valid `T`, and in one method
of `OPP`, you need to dereference the pointer.
> > > +impl Drop for OPP {
> >
> > I don't think you need the `drop` implementation here, since it should
> > be already handled by `impl AlwaysRefCounted`,
>
> Right.
>
> > could you try to a doc
> > test for this? Something like:
> >
> > let opp: ARef<OPP> = <from a raw dev_pm_opp ponter whose refcount is 1>
> > drop(opp);
>
> I now tested it with a kernel test to see what's going on internally
>
> > IIUC, this will result double-free with the current implementation.
>
> Quite the opposite actually. I am getting double get and a single put :)
>
> Thanks a lot for pointing me to this direction as I have found that my
> implementation was incorrect. This is how I understand it, I can be
> wrong since I am okayish with Rust:
>
> - What's getting returned from `from_raw_opp/from_raw_opp_owned` is a
> reference: `<&'a Self>`.
>
> - Since this is a reference, when it gets out of scope, nothing
> happens. i.e. the `drop()` fn of `struct OPP` never gets called for
> the OPP object, as there is no real OPP object, but just a
> reference.
>
> - When this gets converted to an `ARef` object (implicit typecasting),
> we increment the count. And when that gets dropped, we decrement it.
> But Apart from an `ARef` object, only the reference to the OPP gets
> dropped and hence again, drop() doesn't get called.
>
> - The important part here is that `from_raw_opp()` shouldn't be
> incrementing the refcount, as drop() will never get called. And
> since we reach here from the C implementation, the OPP will remain
> valid for the function call.
>
Right. I wasn't aware that you didn't return a `ARef<OPP>`, which mean
the return value won't handle the refcounting automatically (and because
of that, the refcount shouldn't be increased.)
> - On the other hand, I can't return <&'a Self> from
> from_raw_opp_owned() anymore. In this case the OPP core has already
> incremented the refcount of the OPP (while it searched the OPP on
> behalf of the Rust code). Whatever is returned here, must drop the
> refcount when it goes out of scope. Also the returned OPP reference
> can live for a longer period of time in this case, since the call
> originates from the Rust side. So, it needs to be an explicit
> conversion to ARef which won't increment the refcount, but just
> decrement when the ARef gets out of scope.
>
I think you got it correct ;-) The takeaway is: there are different
types of pointers/references, 1) if users only use a `struct dev_pm_opp
*` shortly and the scope can be told at compile time, you can provide a
`&OPP`, 2) if the scope of usage is somewhat dynamic, and the users
should descrease the refcount after use it, the API should return a
`ARef<OPP>`. And since the refcount inc/dec are already maintained in
`ARef<_>`, `OPP::drop` shouldn't touch the refcount anymore.
Also as you already noticed, calling `into` on a `&OPP` will give a
`ARef<OPP>`, which includes an increment of the refcount, and usually
should be used when the users want to switch to long-term usage after a
quick short-term use (e.g. do a quick check of the status and decide
some more work is needed, and maybe need to transfer the ownership of
the pointer to a workqueue or something).
> Here is the diff that I need:
>
> diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs
> index aaf220e6aeac..a99950b4d835 100644
> --- a/rust/kernel/opp.rs
> +++ b/rust/kernel/opp.rs
> @@ -692,7 +692,7 @@ pub fn opp_from_freq(
> })?;
>
> // SAFETY: The `ptr` is guaranteed by the C code to be valid.
> - Ok(unsafe { OPP::from_raw_opp_owned(ptr)?.into() })
> + unsafe { OPP::from_raw_opp_owned(ptr) }
> }
>
> /// Finds OPP based on level.
> @@ -718,7 +718,7 @@ pub fn opp_from_level(&self, mut level: u32, stype: SearchType) -> Result<ARef<O
> })?;
>
> // SAFETY: The `ptr` is guaranteed by the C code to be valid.
> - Ok(unsafe { OPP::from_raw_opp_owned(ptr)?.into() })
> + unsafe { OPP::from_raw_opp_owned(ptr) }
> }
>
> /// Finds OPP based on bandwidth.
> @@ -743,7 +743,7 @@ pub fn opp_from_bw(&self, mut bw: u32, index: i32, stype: SearchType) -> Result<
> })?;
>
> // SAFETY: The `ptr` is guaranteed by the C code to be valid.
> - Ok(unsafe { OPP::from_raw_opp_owned(ptr)?.into() })
> + unsafe { OPP::from_raw_opp_owned(ptr) }
> }
>
> /// Enable the OPP.
> @@ -834,31 +834,33 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
> }
>
> impl OPP {
> - /// Creates a reference to a [`OPP`] from a valid pointer.
> + /// Creates an owned reference to a [`OPP`] from a valid pointer.
> ///
> /// # Safety
> ///
> - /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
> - /// returned [`OPP`] reference.
> - pub unsafe fn from_raw_opp_owned<'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() })
> + /// The caller must ensure that `ptr` is valid and OPP's refcount is incremented. The refcount
> + /// will be decremented by `dec_ref` when the ARef object is dropped.
Usually the second sentense "The refcount ..." won't need to be put in
the safety requirement, as it just describes how ARef works.
> + pub unsafe fn from_raw_opp_owned(ptr: *mut bindings::dev_pm_opp) -> Result<ARef<Self>> {
> + let ptr = ptr::NonNull::new(ptr).ok_or(ENODEV)?;
> +
> + // SAFETY: The safety requirements guarantee the validity of the pointer.
> + //
> + // INVARIANT: The refcount is already incremented by the C API that returned the pointer,
> + // and we pass ownership of the refcount to the new `ARef<OPP>`.
You can probably drop the "INVARIANT", as it's an invariant of `ARef`
which already guarantees since the safety requirement of
`ARef::from_raw()` meets. At least you can write them as "normal"
comments.
> + Ok(unsafe { ARef::from_raw(ptr.cast()) })
> }
>
> /// Creates a reference to a [`OPP`] from a valid pointer.
> ///
> /// # Safety
> ///
> - /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
> - /// returned [`OPP`] reference.
> + /// The caller must ensure that `ptr` is valid and remains valid for the duration of 'a. The
> + /// refcount is not updated by the Rust API unless the returned reference is converted to an
> + /// ARef object.
Again you could drop the second sentence, or you can put it somewhere
outside the "Safety" section, if you want to.
> pub unsafe fn from_raw_opp<'a>(ptr: *mut bindings::dev_pm_opp) -> Result<&'a Self> {
> - let opp = unsafe { Self::from_raw_opp_owned(ptr) }?;
> -
> - // Take an extra reference to the OPP since the caller didn't take it.
> - opp.inc_ref();
> - Ok(opp)
> + // 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]
> @@ -910,10 +912,3 @@ pub fn is_turbo(&self) -> bool {
> unsafe { bindings::dev_pm_opp_is_turbo(self.as_raw()) }
> }
> }
> -
> -impl Drop for OPP {
> - fn drop(&mut self) {
> - // SAFETY: The safety requirements guarantee that the refcount is nonzero.
> - unsafe { bindings::dev_pm_opp_put(self.as_raw()) }
> - }
> -}
>
> Makes sense ?
>
Yep, looks good to me!
Regards,
Boqun
> --
> viresh
next prev parent reply other threads:[~2024-07-05 18:20 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-03 7:14 [RFC PATCH V3 0/8] Rust bindings for cpufreq and OPP core + sample driver Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 1/8] rust: Add initial bindings for OPP framework Viresh Kumar
2024-07-03 15:34 ` Boqun Feng
2024-07-05 11:02 ` Viresh Kumar
2024-07-05 18:19 ` Boqun Feng [this message]
2024-07-09 11:02 ` Viresh Kumar
2024-07-09 17:45 ` Boqun Feng
2024-07-10 7:36 ` Viresh Kumar
2024-07-10 11:06 ` Viresh Kumar
2024-07-10 21:58 ` Boqun Feng
2024-07-03 7:14 ` [RFC PATCH V3 2/8] rust: Extend OPP bindings for the OPP table Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 3/8] rust: Extend OPP bindings for the configuration options Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 4/8] rust: Add initial bindings for cpufreq framework Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 5/8] rust: Extend cpufreq bindings for policy and driver ops Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 6/8] rust: Extend cpufreq bindings for driver registration Viresh Kumar
2024-07-05 11:39 ` Danilo Krummrich
2024-07-05 11:43 ` Danilo Krummrich
2024-07-03 7:14 ` [RFC PATCH V3 7/8] rust: Extend OPP bindings with CPU frequency table Viresh Kumar
2024-07-03 7:14 ` [RFC PATCH V3 8/8] cpufreq: Add Rust based cpufreq-dt driver Viresh Kumar
2024-07-05 11:32 ` Danilo Krummrich
2024-07-10 8:56 ` Viresh Kumar
2024-07-10 15:28 ` Danilo Krummrich
2024-07-11 6:06 ` Viresh Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Zog5NYptZRaqbUBz@boqun-archlinux \
--to=boqun.feng@gmail.com \
--cc=a.hindborg@samsung.com \
--cc=alex.bennee@linaro.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=erik.schilling@linaro.org \
--cc=gary@garyguo.net \
--cc=joakim.bech@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=manos.pitsidianakis@linaro.org \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=nm@ti.com \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robh@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sboyd@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=viresh.kumar@linaro.org \
--cc=wedsonaf@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox