/*1* Copyright (c) 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/* Make sure to build out-of-line versions of spinlock inline functions */30#define SPINLOCK_INLINE /* empty */3132#include <types.h>33#include <lib.h>34#include <cpu.h>35#include <spl.h>36#include <spinlock.h>37#include <current.h> /* for curcpu */3839/*40* Spinlocks.41*/424344/*45* Initialize spinlock.46*/47void48spinlock_init(struct spinlock *lk)49{50spinlock_data_set(&lk->lk_lock, 0);51lk->lk_holder = NULL;52}5354/*55* Clean up spinlock.56*/57void58spinlock_cleanup(struct spinlock *lk)59{60KASSERT(lk->lk_holder == NULL);61KASSERT(spinlock_data_get(&lk->lk_lock) == 0);62}6364/*65* Get the lock.66*67* First disable interrupts (otherwise, if we get a timer interrupt we68* might come back to this lock and deadlock), then use a machine-level69* atomic operation to wait for the lock to be free.70*/71void72spinlock_acquire(struct spinlock *lk)73{74struct cpu *mycpu;7576splraise(IPL_NONE, IPL_HIGH);7778/* this must work before curcpu initialization */79if (CURCPU_EXISTS()) {80mycpu = curcpu->c_self;81if (lk->lk_holder == mycpu) {82panic("Deadlock on spinlock %p\n", lk);83}84}85else {86mycpu = NULL;87}8889while (1) {90/*91* Do test-test-and-set, that is, read first before92* doing test-and-set, to reduce bus contention.93*94* Test-and-set is a machine-level atomic operation95* that writes 1 into the lock word and returns the96* previous value. If that value was 0, the lock was97* previously unheld and we now own it. If it was 1,98* we don't.99*/100if (spinlock_data_get(&lk->lk_lock) != 0) {101continue;102}103if (spinlock_data_testandset(&lk->lk_lock) != 0) {104continue;105}106break;107}108109lk->lk_holder = mycpu;110}111112/*113* Release the lock.114*/115void116spinlock_release(struct spinlock *lk)117{118/* this must work before curcpu initialization */119if (CURCPU_EXISTS()) {120KASSERT(lk->lk_holder == curcpu->c_self);121}122123lk->lk_holder = NULL;124spinlock_data_set(&lk->lk_lock, 0);125spllower(IPL_HIGH, IPL_NONE);126}127128/*129* Check if the current cpu holds the lock.130*/131bool132spinlock_do_i_hold(struct spinlock *lk)133{134if (!CURCPU_EXISTS()) {135return true;136}137138/* Assume we can read lk_holder atomically enough for this to work */139return (lk->lk_holder == curcpu->c_self);140}141142143