Questions about mutex implementation in kern/kern_mutex.c
John Baldwin
jhb at freebsd.org
Thu Sep 16 18:16:08 UTC 2010
On Thursday, September 16, 2010 1:33:07 pm Andrey Simonenko wrote:
> On Wed, Sep 15, 2010 at 08:46:00AM -0700, Matthew Fleming wrote:
> > I'll take a stab at answering these...
> >
> > On Wed, Sep 15, 2010 at 6:44 AM, Andrey Simonenko
> > <simon at comsys.ntu-kpi.kiev.ua> wrote:
> > > Hello,
> > >
> > > I have questions about mutex implementation in kern/kern_mutex.c
> > > and sys/mutex.h files (current versions of these files):
> > >
> > > 1. Is the following statement correct for a volatile pointer or integer
> > > variable: if a volatile variable is updated by the compare-and-set
> > > instruction (e.g. atomic_cmpset_ptr(&val, ...)), then the current
> > > value of such variable can be read without any special instruction
> > > (e.g. v = val)?
> > >
> > > I checked Assembler code for a function with "v = val" and "val = v"
> > > like statements generated for volatile variable and simple variable
> > > and found differences: on ia64 "v = val" was implemented by ld.acq and
> > > "val = v" was implemented by st.rel; on mips and sparc64 Assembler code
> > > can have different order of lines for volatile and simple variable
> > > (depends on the code of a function).
> >
> > I think this depends somewhat on the hardware and what you mean by
> > "current" value.
>
> "Current" value means that the value of a variable read by one thread
> is equal to the value of this variable successfully updated by another
> thread by the compare-and-set instruction. As I understand from the kernel
> source code, atomic_cmpset_ptr() allows to update a variable in a way that
> all other CPUs will invalidate corresponding cache lines that contain
> the value of this variable.
That is not true. It is likely true on x86, but it is certainly not true on
other architectures such as sparc64 where a write may be held in a store
buffer for an indeterminate amount of time (and note that some lock releases
are simple stores with a "rel" memory barrier). All that we require is that
if the value is stale, the atomic_cmpset() that attempts to set MTX_CONTESTED
will fail.
> The mtx_owned(9) macro uses this property, mtx_owned() does not use anything
> special to compare the value of m->mtx_lock (volatile) with current thread
> pointer, all other functions that update m->mtx_lock of unowned mutex use
> compare-and-set instruction. Also I cannot find anything special in
> generated Assembler code for volatile variables (except for ia64 where
> acquire loads and release stores are used).
No, mtx_owned() is just not harmed by the races it loses. You can certainly
read a stale value of mtx_lock in mtx_owned() if some other thread owns the
lock or has just released the lock. However, we don't care, because in both
of those cases, mtx_owned() returns false. What does matter is that
mtx_owned() can only return true if we currently hold the mutex. This works
because 1) the same thread cannot call mtx_unlock() and mtx_owned() at the
same time, and 2) even CPUs that hold writes in store buffers will snoop their
store buffer for local reads on that CPU. That is, a given CPU will never
read a stale value of a memory word that is "older" than a write it has
performed to that word.
> > If you want a value that is not in-flux, then something like
> > atomic_cmpset_ptr() setting to the current value is needed, so that
> > you force any other atomic_cmpset to fail. However, since there is no
> > explicit lock involved, there is no strong meaning for "current" value
> > and a read that does not rely on a value cached in a register is
> > likely sufficient. While the "volatile" keyword in C has no explicit
> > hardware meaning, it often means that a load from memory (or,
> > presumably, L1-L3 cache) is required.
>
> The "volatile" keyword here and all questions are related to the base C
> compiler, current version and currently supported architectures in FreeBSD.
> Yes, here under "volatile" I want to say that the value of a variable is
> not cached in a register and it is referenced by its address in all
> commands.
>
> There are some places in the kernel where a variable is updated in
> something like "do { v = value; } while (!atomic_cmpset_int(&value, ...));"
> and that variable is not "volatile", but the compiler generates correct
> Assembler code. So "volatile" is not a requirement for all cases.
Hmm, I suspect that many of those places actually do use volatile. The
various lock cookies (mtx_lock, etc.) are declared volatile in the structure.
Otherwise the compiler would be free to conclude that 'v = value;' is a loop
invariant and move it out of the loop which would break. Given that, the
construct you referred to does in fact require 'value' to be volatile.
--
John Baldwin
More information about the freebsd-hackers
mailing list