Re: spin_unlock optimization(i386)

Andrea Arcangeli (andrea@suse.de)
Thu, 25 Nov 1999 00:51:32 +0100 (CET)


On Wed, 24 Nov 1999, Erich Boleyn wrote:

>
>> About the _read_ memory barrier (rmb()) it must also prevent _following_
>> reads to be reordered _before_ the barrier.
>>
>> read 0x0
>> rmb(); /* lock on the bus, I was proposing to replace with a write (wrong!) */
>> read 0x20
>>
>> The rmb() ensure the "read 0x20" will be executed _after_ "read 0x0".
>>
>> If I understood well by this thread, a write in the middle of two reads
>> on IA32 only ensure that the 0x0 is read _before_ the write. But the read
>> 0x20 can happen even before the write and before "read 0x0". This in turn
>> mean that rmb() can't be implemented with a write in the zero page...
>
>Uhh, if this is to a cacheable memory type, then the read memory barrier is
>irrelevant, as all writes by other processors are guaranteed to be observed
>in order, so, if some other processor stores into 0x20, then 0x0, your
>program will never execute as if it had gotten the wrong data sequence.

Does this mean that an IA32 SMP system provides _always_ strong ordering
while accessing RAM in WB or WT caches? On a single CPU we always see
strong ordering even if the accesses on RAM are speculative, but on SMP on
most CPUs we have to serialize instructions by hand. In the common code
(the one we Manfred told) a mb() is definitely necessary as not only IA32
has to run such code.

Assume this piece of code that is basically the wait_event interface:

unsigned long event_happened = 0;

wait_for_event()
{
current_task_state = WANT_TO_GO_TO_SLEEP;
if (!event_happened)
schedule(); /* schedule will put the task to sleep if
current_task_state is set to
WANT_TO_GO_TO_SLEEP or it will
return immediatly if current_task_state
is set to RUNNING */
}

event_callback() /* this is called when the event happens
and it may run on a CPU different than
the one where wait_for_event() runs */
{
event_happened = 1;
current_task_state = RUNNING;
}

The above interface rely on the ordering as written above.

Even the compiler should be allowed to reorder the above instructions as
the compiler always assumes that the program is single threaded. So for
example the event_callback() could be implemented in ASM this way without
a _compiler_ barrier:

event_callback() /* this is called when the event happens
and it may run on a CPU different than
the one where wait_for_event() runs */
{
current_task_state = RUNNING; <--|
event_happened = 1; <------------|
}

See what happens to the wait_for_event() call if the event_callback() gets
reordered in the above way:

CPU 0 -> wait_for_event()) CPU 1 -> event_callback()
-------------------- -----------------------
current_task_state = RUNNING
current_task_state = GO_TO_SLEEP; /* overwrite */

read event_happened and it's still zero so
here we call schedule() with
current_task_state set to SLEEP

schedule()
event_happened = 1; /*too late */
/* deadlock due missed event */

I hope I am been clear enough, if not please ask more details.

So at least a barrier() for the compiler between the read/writes is
necessary also for IA32.

What is interesting to know is if the IA32 enforce strong ordering in
software in SMP across multiple CPUs like if both current_task_state and
even_happened would been allocated on _not_ cachable memory. If that is
true then it seems to me we can drop some _more_ lock on the bus from the
IA32 port... (as first the xchg in set_mb() can be removed and replaced
with a normal write in IA32 for example). NOTE: set_mb() and in turn
set_current_state() (that uses set_mb()) is still necessary (for other
archs like Alpha) in order to write correct (SMP safe) common code of
course.

Andrea

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/