All of lore.kernel.org
 help / color / mirror / Atom feed
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

  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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.