Re: simple (i386) spinlock implementation questions...

From: Mitchell Blank Jr (mitch@sfgoth.com)
Date: Mon May 15 2000 - 20:30:54 EST


Brett Thompson! wrote:
> 1) What does spin_lock_string do in 2.2 (for SMP kernels)? It's:
>
> #define spin_lock_string \
> "\n1:\t" \
> "lock ; btsl $0,%0\n\t" \
> "jc 2f\n" \

btsl is a test-and-set lock primative. So this tests if the lock is
not-free, jump to '2:'.

> ".section .text.lock,\"ax\"\n" \

This is the tricky part - by changing sections we're making the code
non-linear. This keeps the common case (not contention on the lock)
small, which means the instruction cache is happier. Thus, if we succeeded
in getting the lock above we'll be dropping into the code after
spin_lock_string, NOT the code that follows. The only way we'll
reach the following code is from the "jc 2f" above.

> "2:\t" \
> "testb $1,%0\n\t" \
> "jne 2b\n\t" \
> "jmp 1b\n" \

This loops looking at the lock and waiting for the holder to free it.
We could just have a simple loop on the btsl continuing to try to
grab it, but ecah time we try to grab the lock we force a 'locked cycle'
on the memory bus which hurts performance for all CPUs. It's much more
efficient to just loop reading the value of the spinlock and waiting
for it to update. This will only hit our local cache until the CPU
holding the lock releases it. The CPU's bus snooping logic will notice
the write, update (or invalidate) the local cache, and we'll fall out
of the loop. Of course, another CPU might be also trying to grab
the lock, so we go back to the btsl in order to do another atempt
at an atomic lock grab.

> ".previous"

This un-does the ".section" directive.

> 2) Okay, this is an even sillier question.
>
> #define spin_lock_init(x) do { (x)->lock = 0; } while(0)
>
> How come a do-while loop (or, erm, in this case, a
> non-loop?) used? My professor thinks that it's to prevent spin_lock_init
> from being used in expressions in which order of operations could mess
> things up, but if so, wouldn't { (x)->lock = 0; } also do the trick?

Imagin you had

        if (foo)
                spin_lock_init(x);
        else
                do_somthing_else();

This looks (and should be) perfectly legal. If you protected the macro
only in '{ }' then you'd get:

        if (foo)
                { (x)->lock = 0; };
        else
                do_somthing_else();

Now the {} block is taken as the if-block (ok so far). The semicolon
will be taken as a null statement - since it wasn't an "else" the
compiler concludes that the "if" doesn't have a matching "else".
When it then sees the else on the next line you'll get a parse error.
(Try it out once). A "do {} while(0)" is the correct way of making
sure that the semicolon following the macro always gets eaten by
the parser. Don't worry, it doesn't actually expand to any assembly.

-Mitch

-
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/



This archive was generated by hypermail 2b29 : Mon May 15 2000 - 21:00:27 EST