perplexing kernel codes of Linux,pls help

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
cxsnew

perplexing kernel codes of Linux,pls help

Post by cxsnew »

I can't understand the following kernel codes in Linux 2.2.10(include/asm-i386/atomic.h):

/*
* Make sure gcc doesn't try to be clever and move
*things around on us. We need to use _exactly_ the *address the user gave us,not some alias that contains *the same information.
*/
#define __atomic_fool_gcc(x) (*(volatile struct { int a[100]; } *)x)
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:perplexing kernel codes of Linux,pls help

Post by Pype.Clicker »

roughly, the volatile keyword is used to tell GCC that the memory location should be used "as is", and not cached in a local variable or a register.

when you do

Code: Select all

extern int x;
while (x) sleep();
for instance because your process waits for an interrupt to change X's value, you must prevent GCC to translate it as

Code: Select all

mov eax,[x]
.sb:
or eax, eax
je .eb
  call sleep
  jmp .sb
.eb:
probably that here the linux kernel dezigners didn't want to make the variable globally volatile, but only in the scope of a few instructions, so they just used a cast to a volatile type for the few required locations.
cxsnew

Re:perplexing kernel codes of Linux,pls help

Post by cxsnew »

I think maybe what you said is about the keyword
volatile.You can prevent GCC from putting a variable in a register by using volatile when declare a variable .

But I want to know why the OS designer make a pointer type cast between the x and (volatile struct {int a[100]}*) ,what will be happen if not?The following is the usage example in Linux:

static __inline__ void atomic_inc(volatile atomic_t *v)
{
   __asm__ __volatile__(
      LOCK "incl %0"
      :"=m" (__atomic_fool_gcc(v))
      :"m" (__atomic_fool_gcc(v)));
}

any tip?thanks!
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:perplexing kernel codes of Linux,pls help

Post by Pype.Clicker »

err ... this goes beyond my knowledge ... maybe you should try to mail the maintainer of this source ...
cxsnew

Re:perplexing kernel codes of Linux,pls help

Post by cxsnew »

Thanks for your enthusiastic help in any case.
Maybe this problem will be involved with the optimazation of compiler,we all need this aspect of knowledge.I will post here if I known this later.
Curufir

Re:perplexing kernel codes of Linux,pls help

Post by Curufir »

Congratulations, that's some truly wierd code you've dug up ;D.

I think the answer lies in what it's trying to achieve. Essentially this code gives you a locked incrementation of an integer, most likely used for something like a mutex in the SMP part of the kernel. So using values in registers is simply not worthwhile, because a mutex implies that the value is shared between processes/threads and they'll be reading the value from shared memory I think.

Now then perhaps what happens is that gcc stores the value of the incremented integer somewhere without actually checking to see if the incrementation happens. Now that wouldn't make it atomic, because there's no rollback to the original value if the incrementation fails. So it does this weird mem read/write to fix the value to the one that's there after the incrementation, making it atomic. Now anything that relies on that integer for synchronisation operations has to be looking at the same integer in memory.

My C code is rusty as hell though, so quite why they need to cast it like that is as much a mystery (Probably more) to me as it is to you. Best guess is that because the integer may be shared it has to exist at a clearly defined point in memory, and perhaps GCC handles optimisation of these pointers in different ways, shifting to a local copy at a different location without the type cast.
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:perplexing kernel codes of Linux,pls help

Post by Pype.Clicker »

another possible way to find out would be to compare the produced code with and without the cast ... maybe it comes for retro-compatibility with older version of GCC too ...
Beth

Re:perplexing kernel codes of Linux,pls help

Post by Beth »

cxsnew wrote: I think maybe what you said is about the keyword
volatile.You can prevent GCC from putting a variable in a register by using volatile when declare a variable .

But I want to know why the OS designer make a pointer type cast between the x and (volatile struct {int a[100]}*) ,what will be happen if not?The following is the usage example in Linux:

static __inline__ void atomic_inc(volatile atomic_t *v)
{
???__asm__ __volatile__(
??????LOCK "incl %0"
??????:"=m" (__atomic_fool_gcc(v))
??????:"m" (__atomic_fool_gcc(v)));
}
If not, it'll probably still produce the same code...because, if we look, "v" is already declared "volatile" in the parameter list and the compiler should automatically be treating any instance of it inside the function as "volatile" because of this...

Why is it written this way? I'm just guessing that it's a case of being overly-defensive with the code to _explicitly 100% ensure_ that the compiler always sees "v" as "volatile" by slapping "volatile" on every single instance so that it won't, even for a split-second, see it in any other context...

If left out, then it most probably would produce the same code...but the point here is that we're NOT depending on any specific compiler functionality...if GCC or its optimiser changes the way it looks at "volatile" things in future, this code is more or less "future-proof"...that's the idea behind being overly defensive and explicitly stating "volatile" on every single instance of "v"...it's probably not strictly needed but, simply, why take the chance? Atomic, synchronising, multi-threaded / multi-processor stuff can often be incredibly delicate stuff...the most minor different choice of assembly instruction could completely ruin things by not being atomic when it should be...the author's being overly-paranoid but with good reason...this is important stuff so it pays well to get paranoid and ensure that nothing goes wrong...

If you ever get the chance to debug any sort of concurrent code like this (likely if making your own multi-threaded OS) then you'll soon learn exactly why it pays to be over-defensive...when things go wrong, multi-threaded / multi-processor code is a total _nightmare_ to debug...you won't even get the same errors every time you run the code and, sometimes, even when the code's wrong, the bug might not show up because it "coincidentally" avoided a clash between threads...the next time you run it, though, you might not be so lucky...

I think the author has written plenty of this sort of code before and has learnt to simply not take any chances...and, though overly-defensive perhaps, explicitly declaring "volatile" each and every time simply leaves no room for GCC to interpret things in a way other than the intended purpose...the author just isn't letting anything go wrong by pedantically type casting "volatile" onto every single instance of "v"...it might be redundent to do so with most GCC versions but, simply, why take any chances? Linux's infamous "stability" depends on this being 100% right, after all...

Beth :)
Post Reply