1/* $Id: semaphore.c,v 1.1.1.1 2007/08/03 18:52:18 Exp $ 2 * semaphore.c: Sparc64 semaphore implementation. 3 * 4 * This is basically the PPC semaphore scheme ported to use 5 * the sparc64 atomic instructions, so see the PPC code for 6 * credits. 7 */ 8 9#include <linux/sched.h> 10#include <linux/errno.h> 11#include <linux/init.h> 12 13/* 14 * Atomically update sem->count. 15 * This does the equivalent of the following: 16 * 17 * old_count = sem->count; 18 * tmp = MAX(old_count, 0) + incr; 19 * sem->count = tmp; 20 * return old_count; 21 */ 22static __inline__ int __sem_update_count(struct semaphore *sem, int incr) 23{ 24 int old_count, tmp; 25 26 __asm__ __volatile__("\n" 27" ! __sem_update_count old_count(%0) tmp(%1) incr(%4) &sem->count(%3)\n" 28"1: ldsw [%3], %0\n" 29" mov %0, %1\n" 30" cmp %0, 0\n" 31" movl %%icc, 0, %1\n" 32" add %1, %4, %1\n" 33" cas [%3], %0, %1\n" 34" cmp %0, %1\n" 35" membar #StoreLoad | #StoreStore\n" 36" bne,pn %%icc, 1b\n" 37" nop\n" 38 : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) 39 : "r" (&sem->count), "r" (incr), "m" (sem->count) 40 : "cc"); 41 42 return old_count; 43} 44 45static void __up(struct semaphore *sem) 46{ 47 __sem_update_count(sem, 1); 48 wake_up(&sem->wait); 49} 50 51void up(struct semaphore *sem) 52{ 53 /* This atomically does: 54 * old_val = sem->count; 55 * new_val = sem->count + 1; 56 * sem->count = new_val; 57 * if (old_val < 0) 58 * __up(sem); 59 * 60 * The (old_val < 0) test is equivalent to 61 * the more straightforward (new_val <= 0), 62 * but it is easier to test the former because 63 * of how the CAS instruction works. 64 */ 65 66 __asm__ __volatile__("\n" 67" ! up sem(%0)\n" 68" membar #StoreLoad | #LoadLoad\n" 69"1: lduw [%0], %%g1\n" 70" add %%g1, 1, %%g7\n" 71" cas [%0], %%g1, %%g7\n" 72" cmp %%g1, %%g7\n" 73" bne,pn %%icc, 1b\n" 74" addcc %%g7, 1, %%g0\n" 75" membar #StoreLoad | #StoreStore\n" 76" ble,pn %%icc, 3f\n" 77" nop\n" 78"2:\n" 79" .subsection 2\n" 80"3: mov %0, %%g1\n" 81" save %%sp, -160, %%sp\n" 82" call %1\n" 83" mov %%g1, %%o0\n" 84" ba,pt %%xcc, 2b\n" 85" restore\n" 86" .previous\n" 87 : : "r" (sem), "i" (__up) 88 : "g1", "g2", "g3", "g7", "memory", "cc"); 89} 90 91static void __sched __down(struct semaphore * sem) 92{ 93 struct task_struct *tsk = current; 94 DECLARE_WAITQUEUE(wait, tsk); 95 96 tsk->state = TASK_UNINTERRUPTIBLE; 97 add_wait_queue_exclusive(&sem->wait, &wait); 98 99 while (__sem_update_count(sem, -1) <= 0) { 100 schedule(); 101 tsk->state = TASK_UNINTERRUPTIBLE; 102 } 103 remove_wait_queue(&sem->wait, &wait); 104 tsk->state = TASK_RUNNING; 105 106 wake_up(&sem->wait); 107} 108 109void __sched down(struct semaphore *sem) 110{ 111 might_sleep(); 112 /* This atomically does: 113 * old_val = sem->count; 114 * new_val = sem->count - 1; 115 * sem->count = new_val; 116 * if (old_val < 1) 117 * __down(sem); 118 * 119 * The (old_val < 1) test is equivalent to 120 * the more straightforward (new_val < 0), 121 * but it is easier to test the former because 122 * of how the CAS instruction works. 123 */ 124 125 __asm__ __volatile__("\n" 126" ! down sem(%0)\n" 127"1: lduw [%0], %%g1\n" 128" sub %%g1, 1, %%g7\n" 129" cas [%0], %%g1, %%g7\n" 130" cmp %%g1, %%g7\n" 131" bne,pn %%icc, 1b\n" 132" cmp %%g7, 1\n" 133" membar #StoreLoad | #StoreStore\n" 134" bl,pn %%icc, 3f\n" 135" nop\n" 136"2:\n" 137" .subsection 2\n" 138"3: mov %0, %%g1\n" 139" save %%sp, -160, %%sp\n" 140" call %1\n" 141" mov %%g1, %%o0\n" 142" ba,pt %%xcc, 2b\n" 143" restore\n" 144" .previous\n" 145 : : "r" (sem), "i" (__down) 146 : "g1", "g2", "g3", "g7", "memory", "cc"); 147} 148 149int down_trylock(struct semaphore *sem) 150{ 151 int ret; 152 153 /* This atomically does: 154 * old_val = sem->count; 155 * new_val = sem->count - 1; 156 * if (old_val < 1) { 157 * ret = 1; 158 * } else { 159 * sem->count = new_val; 160 * ret = 0; 161 * } 162 * 163 * The (old_val < 1) test is equivalent to 164 * the more straightforward (new_val < 0), 165 * but it is easier to test the former because 166 * of how the CAS instruction works. 167 */ 168 169 __asm__ __volatile__("\n" 170" ! down_trylock sem(%1) ret(%0)\n" 171"1: lduw [%1], %%g1\n" 172" sub %%g1, 1, %%g7\n" 173" cmp %%g1, 1\n" 174" bl,pn %%icc, 2f\n" 175" mov 1, %0\n" 176" cas [%1], %%g1, %%g7\n" 177" cmp %%g1, %%g7\n" 178" bne,pn %%icc, 1b\n" 179" mov 0, %0\n" 180" membar #StoreLoad | #StoreStore\n" 181"2:\n" 182 : "=&r" (ret) 183 : "r" (sem) 184 : "g1", "g7", "memory", "cc"); 185 186 return ret; 187} 188 189static int __sched __down_interruptible(struct semaphore * sem) 190{ 191 int retval = 0; 192 struct task_struct *tsk = current; 193 DECLARE_WAITQUEUE(wait, tsk); 194 195 tsk->state = TASK_INTERRUPTIBLE; 196 add_wait_queue_exclusive(&sem->wait, &wait); 197 198 while (__sem_update_count(sem, -1) <= 0) { 199 if (signal_pending(current)) { 200 __sem_update_count(sem, 0); 201 retval = -EINTR; 202 break; 203 } 204 schedule(); 205 tsk->state = TASK_INTERRUPTIBLE; 206 } 207 tsk->state = TASK_RUNNING; 208 remove_wait_queue(&sem->wait, &wait); 209 wake_up(&sem->wait); 210 return retval; 211} 212 213int __sched down_interruptible(struct semaphore *sem) 214{ 215 int ret = 0; 216 217 might_sleep(); 218 /* This atomically does: 219 * old_val = sem->count; 220 * new_val = sem->count - 1; 221 * sem->count = new_val; 222 * if (old_val < 1) 223 * ret = __down_interruptible(sem); 224 * 225 * The (old_val < 1) test is equivalent to 226 * the more straightforward (new_val < 0), 227 * but it is easier to test the former because 228 * of how the CAS instruction works. 229 */ 230 231 __asm__ __volatile__("\n" 232" ! down_interruptible sem(%2) ret(%0)\n" 233"1: lduw [%2], %%g1\n" 234" sub %%g1, 1, %%g7\n" 235" cas [%2], %%g1, %%g7\n" 236" cmp %%g1, %%g7\n" 237" bne,pn %%icc, 1b\n" 238" cmp %%g7, 1\n" 239" membar #StoreLoad | #StoreStore\n" 240" bl,pn %%icc, 3f\n" 241" nop\n" 242"2:\n" 243" .subsection 2\n" 244"3: mov %2, %%g1\n" 245" save %%sp, -160, %%sp\n" 246" call %3\n" 247" mov %%g1, %%o0\n" 248" ba,pt %%xcc, 2b\n" 249" restore\n" 250" .previous\n" 251 : "=r" (ret) 252 : "0" (ret), "r" (sem), "i" (__down_interruptible) 253 : "g1", "g2", "g3", "g7", "memory", "cc"); 254 return ret; 255} 256