root/kernel/spinlock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. initlock
  2. acquire
  3. release
  4. holding
  5. push_off
  6. pop_off

   1 // Mutual exclusion spin locks.
   2 
   3 #include "types.h"
   4 #include "param.h"
   5 #include "memlayout.h"
   6 #include "spinlock.h"
   7 #include "riscv.h"
   8 #include "proc.h"
   9 #include "defs.h"
  10 
  11 void
  12 initlock(struct spinlock *lk, char *name)
  13 {
  14   lk->name = name;
  15   lk->locked = 0;
  16   lk->cpu = 0;
  17 }
  18 
  19 // Acquire the lock.
  20 // Loops (spins) until the lock is acquired.
  21 void
  22 acquire(struct spinlock *lk)
  23 {
  24   push_off(); // disable interrupts to avoid deadlock.
  25   if (holding(lk))
  26     panic("acquire");
  27 
  28   // On RISC-V, __atomic_exchange_n turns into an atomic swap:
  29   //   a5 = 1
  30   //   s1 = &lk->locked
  31   //   amoswap.w.aq a5, a5, (s1)
  32   //
  33   // Passing __ATOMIC_ACQUIRE to __atomic_exchange_n tells
  34   // the C compiler and the processor to not move loads or stores
  35   // past this point, to ensure that the critical section's memory
  36   // references happen strictly after the lock is acquired.
  37   while (__atomic_exchange_n(&lk->locked, 1, __ATOMIC_ACQUIRE) != 0)
  38     ;
  39 
  40   // Record info about lock acquisition for holding() and debugging.
  41   lk->cpu = mycpu();
  42 }
  43 
  44 // Release the lock.
  45 void
  46 release(struct spinlock *lk)
  47 {
  48   if (!holding(lk))
  49     panic("release");
  50 
  51   lk->cpu = 0;
  52 
  53   // Release the lock, equivalent to lk->locked = 0.
  54   //
  55   // This code doesn't use a C assignment, since the C standard
  56   // implies that an assignment might be implemented with
  57   // multiple store instructions.
  58   //
  59   // On RISC-V, __atomic_store_n turns into a single atomic store:
  60   //   s1 = &lk->locked
  61   //   fence rw,w
  62   //   sw zero,0(s1)
  63   //
  64   // The __ATOMIC_RELEASE argument to __atomic_store_n tells the
  65   // the C compiler and the CPU to not move loads or stores past
  66   // this point, to ensure that all the stores in the critical
  67   // section are visible to other CPUs before the lock is released,
  68   // and that loads in the critical section occur strictly before
  69   // the lock is released.
  70   //
  71   // On RISC-V, this generates a fence instruction before the store:
  72   //   fence rw,w
  73   __atomic_store_n(&lk->locked, 0, __ATOMIC_RELEASE);
  74 
  75   pop_off();
  76 }
  77 
  78 // Check whether this cpu is holding the lock.
  79 // Interrupts must be off.
  80 int
  81 holding(struct spinlock *lk)
  82 {
  83   int r;
  84   r = (lk->locked && lk->cpu == mycpu());
  85   return r;
  86 }
  87 
  88 // push_off/pop_off are like intr_off()/intr_on() except that they are matched:
  89 // it takes two pop_off()s to undo two push_off()s.  Also, if interrupts
  90 // are initially off, then push_off, pop_off leaves them off.
  91 
  92 void
  93 push_off(void)
  94 {
  95   // disable interrupts to prevent an involuntary context
  96   // switch while using mycpu().
  97   uint64 flags = rc_sstatus(SSTATUS_SIE);
  98   int old = !!(flags & SSTATUS_SIE);
  99 
 100   if (mycpu()->noff == 0)
 101     mycpu()->intena = old;
 102   mycpu()->noff += 1;
 103 }
 104 
 105 void
 106 pop_off(void)
 107 {
 108   struct cpu *c = mycpu();
 109   if (intr_get())
 110     panic("pop_off - interruptible");
 111   if (c->noff < 1)
 112     panic("pop_off");
 113   c->noff -= 1;
 114   if (c->noff == 0 && c->intena)
 115     intr_on();
 116 }

/* [<][>][^][v][top][bottom][index][help] */