00001 /* 00002 * Copyright (c) 2009 00003 * The President and Fellows of Harvard College. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions 00007 * are met: 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the University nor the names of its contributors 00014 * may be used to endorse or promote products derived from this software 00015 * without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND 00018 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00020 * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE 00021 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00022 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00023 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00024 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00025 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00026 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00027 * SUCH DAMAGE. 00028 */ 00029 00030 /* Make sure to build out-of-line versions of spinlock inline functions */ 00031 #define SPINLOCK_INLINE /* empty */ 00032 00033 #include <types.h> 00034 #include <lib.h> 00035 #include <cpu.h> 00036 #include <spl.h> 00037 #include <spinlock.h> 00038 #include <current.h> /* for curcpu */ 00039 00040 /* 00041 * Spinlocks. 00042 */ 00043 00044 00045 /* 00046 * Initialize spinlock. 00047 */ 00048 void 00049 spinlock_init(struct spinlock *lk) 00050 { 00051 spinlock_data_set(&lk->lk_lock, 0); 00052 lk->lk_holder = NULL; 00053 } 00054 00055 /* 00056 * Clean up spinlock. 00057 */ 00058 void 00059 spinlock_cleanup(struct spinlock *lk) 00060 { 00061 KASSERT(lk->lk_holder == NULL); 00062 KASSERT(spinlock_data_get(&lk->lk_lock) == 0); 00063 } 00064 00065 /* 00066 * Get the lock. 00067 * 00068 * First disable interrupts (otherwise, if we get a timer interrupt we 00069 * might come back to this lock and deadlock), then use a machine-level 00070 * atomic operation to wait for the lock to be free. 00071 */ 00072 void 00073 spinlock_acquire(struct spinlock *lk) 00074 { 00075 struct cpu *mycpu; 00076 00077 splraise(IPL_NONE, IPL_HIGH); 00078 00079 /* this must work before curcpu initialization */ 00080 if (CURCPU_EXISTS()) { 00081 mycpu = curcpu->c_self; 00082 if (lk->lk_holder == mycpu) { 00083 panic("Deadlock on spinlock %p\n", lk); 00084 } 00085 } 00086 else { 00087 mycpu = NULL; 00088 } 00089 00090 while (1) { 00091 /* 00092 * Do test-test-and-set, that is, read first before 00093 * doing test-and-set, to reduce bus contention. 00094 * 00095 * Test-and-set is a machine-level atomic operation 00096 * that writes 1 into the lock word and returns the 00097 * previous value. If that value was 0, the lock was 00098 * previously unheld and we now own it. If it was 1, 00099 * we don't. 00100 */ 00101 if (spinlock_data_get(&lk->lk_lock) != 0) { 00102 continue; 00103 } 00104 if (spinlock_data_testandset(&lk->lk_lock) != 0) { 00105 continue; 00106 } 00107 break; 00108 } 00109 00110 lk->lk_holder = mycpu; 00111 } 00112 00113 /* 00114 * Release the lock. 00115 */ 00116 void 00117 spinlock_release(struct spinlock *lk) 00118 { 00119 /* this must work before curcpu initialization */ 00120 if (CURCPU_EXISTS()) { 00121 KASSERT(lk->lk_holder == curcpu->c_self); 00122 } 00123 00124 lk->lk_holder = NULL; 00125 spinlock_data_set(&lk->lk_lock, 0); 00126 spllower(IPL_HIGH, IPL_NONE); 00127 } 00128 00129 /* 00130 * Check if the current cpu holds the lock. 00131 */ 00132 bool 00133 spinlock_do_i_hold(struct spinlock *lk) 00134 { 00135 if (!CURCPU_EXISTS()) { 00136 return true; 00137 } 00138 00139 /* Assume we can read lk_holder atomically enough for this to work */ 00140 return (lk->lk_holder == curcpu->c_self); 00141 }