Search code examples
catomicspinlockxv6

Do pushcli() and popcli() in the spinlock of XV6 need atomic?


As learning XV6's spinlock,I thought a problem and don't find answer in google. In spinlock, pushcli() and popcli() used to insure interrupt handler run currently:

void pushcli(void)
{
  int eflags;

  eflags = readeflags();
  cli();
  if (mycpu()->ncli == 0)
    mycpu()->intena = eflags & FL_IF;
  mycpu()->ncli += 1;
}

void popcli(void)
{
  if (readeflags() & FL_IF)
    panic("popcli - interruptible");
  if (--mycpu()->ncli < 0)
    panic("popcli");
  if (mycpu()->ncli == 0 && mycpu()->intena)
    sti();
}

However, the spinlock is used to dure with threads and func in it like xchg() should be atomic,but codes in pushcli() and popcli like mycpu()->ncli += 1; are not atomic , which might cause incorrect result of ncli and intena.So why we can use nonatomic operations in pushcli()?If we shouldn't, what can we do to correct it to safe?


Solution

  • Short answer:

    • mycpu()->ncli is local to each CPU
    • lk->locked is shared between all CPUs

    No pushcli and popcli don't need be atomic because they can't be preempted.

    We have two different context here:

    1. The access to spinlock->locked (lk->locked) member

      // The xchg is atomic.
      while(xchg(&lk->locked, 1) != 0)
        ;
      
    2. And the enabling/disable of interruption: cli()/sti()

    In the first case, the variable is stored in memory, any cpu could want to access the lock at any time, the atomic instruction is need here: only one cpu can have access to it.

    In the second case, we are in the kernel code of a cpu: the code executed is local to given processor and once cli is called the cpu can't interrupt (so can't be preempted) until sti is called.

    Since the cpu deals with a variable that is private to it mycpu()->ncli;, no atomicity is needed here.


    To be exact, mycpu() read a shared array cpus[], and by design return the cpu structure referring to the CPU that executed the function. Note that mycpy() require interrupt to be disabled to execute (if not, you got a kernal panic).