Thread: Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

From
Bruce Momjian
Date:
Does any of this need to be backpatched?

---------------------------------------------------------------------------

Tom Lane wrote:
> Log Message:
> -----------
> Do all accesses to shared buffer headers through volatile-qualified
> pointers, to ensure that compilers won't rearrange accesses to occur
> while we're not holding the buffer header spinlock.  It's probably
> not necessary to mark volatile in every single place in bufmgr.c,
> but better safe than sorry.  Per trouble report from Kevin Grittner.
> 
> Modified Files:
> --------------
>     pgsql/contrib/pg_buffercache:
>         pg_buffercache_pages.c (r1.4 -> r1.5)
>
(http://developer.postgresql.org/cvsweb.cgi/pgsql/contrib/pg_buffercache/pg_buffercache_pages.c.diff?r1=1.4&r2=1.5)
>     pgsql/src/backend/storage/buffer:
>         bufmgr.c (r1.195 -> r1.196)
>
(http://developer.postgresql.org/cvsweb.cgi/pgsql/src/backend/storage/buffer/bufmgr.c.diff?r1=1.195&r2=1.196)
>         freelist.c (r1.52 -> r1.53)
>
(http://developer.postgresql.org/cvsweb.cgi/pgsql/src/backend/storage/buffer/freelist.c.diff?r1=1.52&r2=1.53)
>     pgsql/src/include/storage:
>         buf_internals.h (r1.79 -> r1.80)
>         (http://developer.postgresql.org/cvsweb.cgi/pgsql/src/include/storage/buf_internals.h.diff?r1=1.79&r2=1.80)
> 
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match
> 

--  Bruce Momjian                        |  http://candle.pha.pa.us pgman@candle.pha.pa.us               |  (610)
359-1001+  If your life is a hard drive,     |  13 Roberts Road +  Christ can be your backup.        |  Newtown Square,
Pennsylvania19073
 


Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Does any of this need to be backpatched?

No --- we didn't have any per-buffer spinlocks before 8.1.

It's possible that at some point we'll need to start thinking about
applying volatile-pointer coding rules to data structures protected by
LWLocks.  This could only become an issue if the compiler (a) inlines
LWLockAcquire/Release, and (b) tries to rearrange loads and stores
around the LWLock code.  I would like to think that the latter is
impossible even with inlining, principally because the compiler can't
ignore the kernel calls that may occur within the LWLock routines;
those should be treated as external function calls and hence sequence
points, no matter how aggressive the compiler gets.  But we'll see.
        regards, tom lane


Re: [COMMITTERS] pgsql: Do all accesses to shared buffer

From
Neil Conway
Date:
On Wed, 2005-12-10 at 22:46 -0400, Tom Lane wrote:
> No --- we didn't have any per-buffer spinlocks before 8.1.

Right. To summarize the problem as I understand it: we need to force $CC
not to move loads and stores around spinlock acquire/release. This can't
be done by just marking the spinlock variables themselves "volatile", as
the volatile qualifier only affects the rearrangement of loads and
stores WRT other volatile variables.

Before 8.1, all the places that use spinlocks directly also write to
shared memory through a volatile pointer (code that just uses LWLocks
*should* be okay, as Tom says). That ensures loads and stores aren't
rearranged outside spinlock acquire/release -- the problem with the 8.1
bufmgr changes is that this was not done. Tom's fix was to add the
necessary volatile qualifiers.

Another way to fix the problem would be to have S_LOCK() and S_UNLOCK()
force $CC to not rearrange loads and stores by themselves, without
relying upon volatile pointers. If this is possible, I think it would be
better: forgetting a volatile qualifier *once* means we are vulnerable
to corruption of shared memory, depending on the vagaries of the
compiler. Does anyone know how this is done?

>From talking with Andrew @ Supernews on IRC, he suggested the asm
"volatile" keyword as a possible solution ("volatile" is used for some
platform's S_LOCK/S_UNLOCK, but not the default S_UNLOCK(), which is
used by x86 and x86_64). [1] suggests that this keyword prevents
rearrangement of code around the inline ASM, but the GCC docs ([2])
don't actually state this: "The volatile keyword indicates that the
instruction has important side-effects ... Note that even a volatile asm
instruction can be moved relative to other code, including across jump
instructions." Per [3], it seems [1]'s understanding of the semantics of
"asm volatile" are incorrect -- so perhaps "asm volatile" isn't the
right tool, after all.

-Neil

[1]
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.4
[2] http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Extended-Asm.html
[3] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17884#c7



Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

From
Tom Lane
Date:
Neil Conway <neilc@samurai.com> writes:
> Another way to fix the problem would be to have S_LOCK() and S_UNLOCK()
> force $CC to not rearrange loads and stores by themselves, without
> relying upon volatile pointers.

That would certainly be better if possible, but AFAIK it's not.
(Perhaps there is a gcc-specific hack, but certainly not one that's
portable to all compilers.  "volatile" is the only tool the C standard
gives us.)

Note that the S_LOCK and S_UNLOCK macros *do* incorporate commands to
prevent hardware-level reorderings, on platforms where this is
necessary.  The problem at hand is basically that those optimization-
fence instructions are not visible to the compiler...

> From talking with Andrew @ Supernews on IRC, he suggested the asm
> "volatile" keyword as a possible solution ("volatile" is used for some
> platform's S_LOCK/S_UNLOCK, but not the default S_UNLOCK(), which is
> used by x86 and x86_64). [1] suggests that this keyword prevents
> rearrangement of code around the inline ASM, but the GCC docs ([2])
> don't actually state this:

Interesting point.  The bug case at hand involved rearrangement of code
around an S_UNLOCK, which on x86 doesn't use any asm block, only a store
through a volatile pointer.  Maybe if it had used such a block we'd not
have seen the bug.  Still, I think we have to do the volatile pointers
in order to guarantee correct results on non-gcc compilers, so it's not
clear that there's any point in pursuing the question of whether gcc by
itself could offer a nicer solution.
        regards, tom lane


Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

From
Martijn van Oosterhout
Date:
On Wed, Oct 12, 2005 at 11:49:47PM -0400, Tom Lane wrote:
> That would certainly be better if possible, but AFAIK it's not.
> (Perhaps there is a gcc-specific hack, but certainly not one that's
> portable to all compilers.  "volatile" is the only tool the C standard
> gives us.)

Indeed. The linux kernel defines the following:

/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

The memory keyword (as the gcc docs state):

>    If your assembler instruction modifies memory in an unpredictable
> fashion, add 'memory' to the list of clobbered registers.  This will
> cause GNU CC to not keep memory values cached in registers across the
> assembler instruction.

They use this bit in all the spinlock and other locking code
specifically for this purpose. You can do things like:

do { barrier(); } while( condition );

where condition uses any memory variable and it will reread it
everytime, just as if the variable was volatile.

> have seen the bug.  Still, I think we have to do the volatile pointers
> in order to guarantee correct results on non-gcc compilers, so it's not
> clear that there's any point in pursuing the question of whether gcc by
> itself could offer a nicer solution.

Yes, we need to look for solutions for other compilers. We just need to
be careful and have people check the spinlock code carefully when they
use other compilers. Maybe in the porting guide?

--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.

Re: [COMMITTERS] pgsql: Do all accesses to shared buffer headers through

From
"Jim C. Nasby"
Date:
On Wed, Oct 12, 2005 at 10:46:14PM -0400, Tom Lane wrote:
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
> > Does any of this need to be backpatched?
> 
> No --- we didn't have any per-buffer spinlocks before 8.1.
> 
> It's possible that at some point we'll need to start thinking about
> applying volatile-pointer coding rules to data structures protected by
> LWLocks.  This could only become an issue if the compiler (a) inlines
> LWLockAcquire/Release, and (b) tries to rearrange loads and stores
> around the LWLock code.  I would like to think that the latter is
> impossible even with inlining, principally because the compiler can't
> ignore the kernel calls that may occur within the LWLock routines;
> those should be treated as external function calls and hence sequence
> points, no matter how aggressive the compiler gets.  But we'll see.

Sorry if I'm just confused here, but don't LWLocks protect data
structures susceptible to corruption? And if that's the case don't we
need to be sure that the compiler can't optimize around them? Or will
this only result in things like buffers not getting un-pinned?
-- 
Jim C. Nasby, Sr. Engineering Consultant      jnasby@pervasive.com
Pervasive Software      http://pervasive.com    work: 512-231-6117
vcard: http://jim.nasby.net/pervasive.vcf       cell: 512-569-9461


Re: [COMMITTERS] pgsql: Do all accesses to shared buffer

From
Neil Conway
Date:
On Mon, 2005-17-10 at 16:48 -0500, Jim C. Nasby wrote:
> Sorry if I'm just confused here, but don't LWLocks protect data
> structures susceptible to corruption? And if that's the case don't we
> need to be sure that the compiler can't optimize around them?

LWLocks certainly do protect shared data, and if the compiler rearranged
loads and stores around LWLocks acquire/release, it would result in
corruption. Tom was arguing it is unlikely the compiler will actually do
this (because LWLockAcquire is an out-of-line function call that might
invoke a system call, unlike SpinLockAcquire).

-Neil




Re: [COMMITTERS] pgsql: Do all accesses to shared buffer

From
Tom Lane
Date:
Neil Conway <neilc@samurai.com> writes:
> LWLocks certainly do protect shared data, and if the compiler rearranged
> loads and stores around LWLocks acquire/release, it would result in
> corruption. Tom was arguing it is unlikely the compiler will actually do
> this (because LWLockAcquire is an out-of-line function call that might
> invoke a system call, unlike SpinLockAcquire).

Actually it seems the sore spot is more likely to be SpinLockRelease,
which on many architectures expands to nothing more than an inlined
*(lockptr) = 0;

The lock pointer is declared as pointer to volatile, which should
discourage the compiler from removing the store altogether, but as
we've seen the compiler may be willing to rearrange the store with
respect to adjacent loads/stores that aren't marked volatile.

SpinLockAcquire contains an out-of-line function call, so although
the compiler could theoretically misoptimize things in the fast path,
it's probably much less risky than SpinLockRelease.

BTW, we may be perfectly safe on architectures like PPC, where
S_UNLOCK includes an __asm__ __volatile__ section for a hardware-level
optimization fence instruction.  I wonder though if it'd be a good idea
to be marking those fence instructions with the "clobbers memory"
qualifier to ensure this?
        regards, tom lane


Re: [COMMITTERS] pgsql: Do all accesses to shared buffer

From
Martijn van Oosterhout
Date:
On Fri, Oct 21, 2005 at 06:09:00PM -0400, Tom Lane wrote:
> BTW, we may be perfectly safe on architectures like PPC, where
> S_UNLOCK includes an __asm__ __volatile__ section for a hardware-level
> optimization fence instruction.  I wonder though if it'd be a good idea
> to be marking those fence instructions with the "clobbers memory"
> qualifier to ensure this?

Judging by the comments in the linux kernel w.r.t their barrier()
instruction, there are certain versions of gcc that (incorrectly) do
strange things with the "volatile" tag of asm statements. Cloberring
memory is the way to guarentee what you want...

Have a nice day,
--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.