Re: [TECH TOPIC] Rust for Linux

Wedson Almeida Filho
2021-07-07T17:01:47Z

On Wed, Jul 07, 2021 at 05:44:41PM +0200, Greg KH wrote:
> On Wed, Jul 07, 2021 at 05:15:01PM +0200, Miguel Ojeda wrote:
> > For instance, we have a `Ref` type that is similar to `Arc` but reuses
> > the `refcount_t` from C and introduces saturation instead of aborting
> > [3]
> >
> > [3] https://github.com/Rust-for-Linux/linux/blob/rust/rust/kernel/sync/arc.rs
>
> This is interesting code in that I think you are missing the part where
> you still need a lock on the object to prevent one thread from grabbing
> a reference while another one is dropping the last reference. Or am I
> missing something?

You are missing something :)

> The code here:
>
> fn drop(&mut self) {
> // SAFETY: By the type invariant, there is necessarily a reference to the object. We cannot
> // touch `refcount` after it's decremented to a non-zero value because another thread/CPU
> // may concurrently decrement it to zero and free it. It is ok to have a raw pointer to
> // freed/invalid memory as long as it is never dereferenced.
> let refcount = unsafe { self.ptr.as_ref() }.refcount.get();
>
> // INVARIANT: If the refcount reaches zero, there are no other instances of `Ref`, and
> // this instance is being dropped, so the broken invariant is not observable.
> // SAFETY: Also by the type invariant, we are allowed to decrement the refcount.
> let is_zero = unsafe { rust_helper_refcount_dec_and_test(refcount) };
> if is_zero {
> // The count reached zero, we must free the memory.
> //
> // SAFETY: The pointer was initialised from the result of `Box::leak`.
> unsafe { Box::from_raw(self.ptr.as_ptr()) };
> }
> }
>
> Has a lot of interesting comments, and maybe just because I know nothing
> about Rust, but why on the first line of the comment is there always
> guaranteed a reference to the object at this point in time?

It's an invariant of the `Ref<T>` type: if a `Ref<T>` exists, there necessarily
is a non-zero reference count. You cannot have a `Ref<T>` with a zero refcount.

`drop` is called when a `Ref<T>` is about to be destroyed. Since it is about to
be destroyed, it still exists, therefore the ref-count is necessarily non-zero.

> And yes
> it's ok to have a pointer to memory that is not dereferenced, but is
> that what is happening here?

`refcount` is a raw pointer. When it is declared and initialised, it points to
valid memory. The comment is saying that we should be careful with the case
where another thread ends up freeing the object (after this thread has
decremented its share of the count); and that we're not violating any Rust
aliasing rules by having this raw pointer (as long as we don't dereference it).

> I feel you are trying to duplicate the logic of a "struct kref" here,

`struct kref` would give us the ability to specify a `release` function when
calling `refcount_dec_and_test`, but we don't need this round-trip to C code
because we know at compile-time what the `release` function is: it's the `drop`
implementation for the wrapped type (`T` in `Ref<T>`).

> and that requires a lock to work properly. Where is the lock here?

We don't need a lock. Once the refcount reaches zero, we know that nothing else
has pointers to the memory block; the lifetime rules guarantee that if there is
a reference to a `Ref<T>`, then it cannot outlive the `Ref<T>` itself. To
produce a new `Ref<T>` from an existing one, `clone` is called, which increments
the refcount.

For cases when you want to hold on to something without incrementing its
refcount, the common pattern in Rust is to use "weak" pointers. `Ref<T>` doesn't
support them because we haven't needed them yet and they have extra cost.