Re: [brauner-vfs:vfs.misc 12/13] include/linux/fs.h:911:9: error: call to '__compiletime_assert_207' declared with 'error' attribute: Need native word sized stores/loads for atomicity.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 22 Jan 2024 at 23:08, Baokun Li <libaokun1@xxxxxxxxxx> wrote:
>
> >>> include/linux/fs.h:911:9: error: call to '__compiletime_assert_207' declared with 'error' attribute: Need native word sized stores/loads for atomicity.
> >       911 |         return smp_load_acquire(&inode->i_size);

Ahh, yes. We used to allow READ_ONCE() on non-atomic data structures
(we still do, but we used to too), but with truly atomic cases it's
wrong, since reading a 64-bit value with two 32-bit instructions is
clearly ever atomic.

So your patch is hitting the "these atomic and/or ordered accesses
need to be able to actually be done as *one* access" sanity check.

And the reason is that while we have special-cases 32-bit AMP and
preemption code to avoid this:

> >     891       static inline loff_t i_size_read(const struct inode *inode)
> >     892       {
> >     893       #if BITS_PER_LONG==32 && defined(CONFIG_SMP)
> >     894               loff_t i_size;
> >     895               unsigned int seq;
> >     896
> >     897               do {
> >     898                       seq = read_seqcount_begin(&inode->i_size_seqcount);
> >     899                       i_size = inode->i_size;
> >     900               } while (read_seqcount_retry(&inode->i_size_seqcount, seq));
> >     901               return i_size;
> >     902       #elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION)
> >     903               loff_t i_size;
> >     904
> >     905               preempt_disable();
> >     906               i_size = inode->i_size;
> >     907               preempt_enable();
> >     908               return i_size;

We have *not* special-cased the UP and non-preempt code, because in
that case doing a 64-bit load with two 32-bit accesses is obviously
fine (modulo interrupts, which *really* shouldn't be changing i_size.

So this last case:

> >     909       #else
> >     910               /* Pairs with smp_store_release() in i_size_write() */
> >   > 911               return smp_load_acquire(&inode->i_size);
> >     912       #endif

used to be just a simple

        return inode->i_size;

but now that you changed it to smp_load_acquire(), our "native size"
sanity checks kick in.

The solution is either to add a new case here (saying "if we're not
SMP, the smp_load_acquire() is pointless"), or to just remove the size
check from the non-SMP version of smp_load_acquire().

Honestly, that sounds like the right thing to do anyway. Our non-SMP
case looks like this:

#ifndef smp_load_acquire
#define smp_load_acquire(p)                                             \
({                                                                      \
        __unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p);               \
        compiletime_assert_atomic_type(*p);                             \
        barrier();                                                      \
        (typeof(*p))___p1;                                              \
})
#endif

and that compiletime_assert_atomic_type() might as well go away. Yes,
it removes some type-checking coverage, but honestly, the non-SMP case
simply isn't relevant. No developer uses a UP build for testing
anyway, so the "less coverage" is pretty much completely theoretical.

So I *think* the fix is as trivial as something like this:

  --- a/include/asm-generic/barrier.h
  +++ b/include/asm-generic/barrier.h
  @@ -193,7 +193,6 @@ do {                                      \
   #ifndef smp_store_release
   #define smp_store_release(p, v)                              \
   do {                                                         \
  -     compiletime_assert_atomic_type(*p);                     \
        barrier();                                              \
        WRITE_ONCE(*p, v);                                      \
   } while (0)
  @@ -203,7 +202,6 @@ do {                                      \
   #define smp_load_acquire(p)                                  \
   ({                                                           \
        __unqual_scalar_typeof(*p) ___p1 = READ_ONCE(*p);       \
  -     compiletime_assert_atomic_type(*p);                     \
        barrier();                                              \
        (typeof(*p))___p1;                                      \
   })

because the extra type checking here only makes for extra work, not
for extra safety.

Hmm?

                Linus




[Index of Archives]     [Linux Kernel]     [Linux USB Development]     [Yosemite News]     [Linux SCSI]

  Powered by Linux