svn commit: r312975 - head/sys/i386/include

Bruce Evans brde at optusnet.com.au
Thu Feb 2 18:14:25 UTC 2017


On Thu, 2 Feb 2017, Mateusz Guzik wrote:

> On Wed, Feb 01, 2017 at 10:12:57PM +1100, Bruce Evans wrote:
>> On Mon, 30 Jan 2017, Bruce Evans wrote:
>>
>>> On Mon, 30 Jan 2017, Mateusz Guzik wrote:
>>>
>>>> Log:
>>>> i386: add atomic_fcmpset
>>>>
>>>> Tested by:	pho
>>>
>>> This is has some bugs and style bugs.
>>
>> This is still broken.  The invalid asm breaks building at least atomic.o
>> with gcc-4.2.1.
>>
>> Tested fix:
>> ...
>
> Uh, I ended up with the same fix. Committed in r313080.

Thanks.

> Sorry for breakage in the first place.
>
>> The semantics of fcmpset seem to be undocumented.  On x86, *expect is
>> updated non-atomically by a store in the output parameter.  I think
>> cmpxchg updates the "a" register atomically, but then the output
>> parameter causes this to be stored non-atomically to *expect.  A better
>> API would somehow return the "a" register and let the caller store it
>> if it wants.  Ordinary cmpset can be built on this by not storing, and
>> the caller can do the store atomically to a different place if *expect
>> is too volatile to be atomic.
>>
>
> The primitive was modeled after atomic_compare_exchange_* from c11
> atomics. I don't see what's the benefit of storing the result
> separately.
>
> As it is, the primitive fits nicely into loops like "inc not zero".
>
> Like this:
> r = *counter;
> for (;;) {
> 	if (r == 0)
> 		break;
> 	if (atomic_fcmpset_int(counter, &r, r + 1))
> 		break;
> 	// r we can loop back to re-test r
> }

You won't want to ifdef this for SMP, so the i386 implementation has
further bugs  like I expected (fcmpset is not implemented in the
CPU_DISABLE_CMPXCHG case).

>> Maybe just decouple the input parameter from the output parameter.  The
>> following works right (for an amd64 API):
>>
>> Y static __inline int
>> Y atomic_xfcmpset_long(volatile u_long *dst, u_long *expect_out, u_long expect_in,
>> Y     u_long src)

The output parameter is not well named in this or in fcmpset, since
when the comparison fails it holds the compared copy of *dst, not of
*expect_in, and otherwise it is not useful and holds a copy of src.

>> If the caller doesn't want to use *expect_out, it passes a pointer to an
>> unused local variable.  The compiler can then optimize away the store
>> since it is not hidden in the asm.
>
> _fcmpset is specifically for callers who want the value back. Ones which
> don't can use the _cmpset variant.

The main caller of _xfcmpset would be the _cmpset variantL

static __inline int
atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src)
{
 	u_int dummy __unused;

 	return (atomic_xfcmpset_int(dst, &dummy, expect, src));
}

Actually, _cmpset can be built out of _fcmpset even more easily:

static __inline int
atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src)
{
 	return (atomic_fcmpset_int(dst, &expect, src));
}

This has to be a function since a macro would expose *&expect to
clobbering.

Bruce


More information about the svn-src-all mailing list