1

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?

wcyiming
  • 23
  • 4

1 Answers1

2

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).

Mathieu
  • 8,840
  • 7
  • 32
  • 45
  • 1
    Of course, the `*mycpu()` object is also stored in memory, and could in principle be accessed concurrently by another core, but presumably the code is designed so that no other core ever tries to do so. – Nate Eldredge Apr 07 '22 at 14:56
  • @NateEldredge you're right, by design, `mycpu()` return the cpu structure for the cpu executing the function, accessing the `cpus[NCPU]` array. – Mathieu Apr 07 '22 at 15:28