On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
> > On Wed, Apr 14, 2021 at 08:45:51PM +0200, oj...@kernel.org wrote:
> > >   - Featureful language: sum types, pattern matching, generics,
> > >     RAII, lifetimes, shared & exclusive references, modules &
> > >     visibility, powerful hygienic and procedural macros...
> > 
> > IMO RAII is over-valued, but just in case you care, the below seems to
> > work just fine. No fancy new language needed, works today. Similarly you
> > can create refcount_t guards, or with a little more work full blown
> > smart_ptr crud.
> 
> Peter, we do care, thank you for posting this. It's a great example for us to
> discuss some of the minutiae of what we think Rust brings to the table in
> addition to what's already possible in C.

Another fairly common use case is a lockless, racy test of a
particular field, as an optimization before we take the lock before we
test it for realsies.  In this particular case, we can't allocate
memory while holding a spinlock, so we check to see without taking the
spinlock to see whether we should allocate memory (which is expensive,
and unnecessasry most of the time):

alloc_transaction:
        /*
         * This check is racy but it is just an optimization of allocating new
         * transaction early if there are high chances we'll need it. If we
         * guess wrong, we'll retry or free the unused transaction.
         */
        if (!data_race(journal->j_running_transaction)) {
                /*
                 * If __GFP_FS is not present, then we may be being called from
                 * inside the fs writeback layer, so we MUST NOT fail.
                 */
                if ((gfp_mask & __GFP_FS) == 0)
                        gfp_mask |= __GFP_NOFAIL;
                new_transaction = kmem_cache_zalloc(transaction_cache,
                                                    gfp_mask);
                if (!new_transaction)
                        return -ENOMEM;
        }
        ...
repeat:
        read_lock(&journal->j_state_lock);
        ...
        if (!journal->j_running_transaction) {
                read_unlock(&journal->j_state_lock);
                if (!new_transaction)
                        goto alloc_transaction;
                write_lock(&journal->j_state_lock);
                if (!journal->j_running_transaction &&
                    (handle->h_reserved || !journal->j_barrier_count)) {
                        jbd2_get_transaction(journal, new_transaction);
                        new_transaction = NULL;
                }
                write_unlock(&journal->j_state_lock);
                goto repeat;
        }
        ...


The other thing that I'll note is that diferent elements in thet
journal structure are protected by different spinlocks; we don't have
a global lock protecting the entire structure, which is critical for
scalability on systems with a large number of CPU's with a lot of
threads all wanting to perform file system operations.

So having a guard structure which can't be bypassed on the entire
structure would result in a pretty massive performance penalty for the
ext4 file system.  I know that initially the use of Rust in the kernel
is targetted for less performance critical modules, such as device
drivers, but I thought I would mention some of the advantages of more
advanced locking techniques.

Cheers,

                                        - Ted

Reply via email to