1 /*
2  * Locks for smp ppc
3  *
4  * Written by Cort Dougan (cort@cs.nmt.edu)
5  */
6 
7 
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/delay.h>
11 #include <linux/spinlock.h>
12 #include <asm/processor.h>
13 #include <asm/system.h>
14 #include <asm/io.h>
15 
16 #if SPINLOCK_DEBUG
17 
18 #undef INIT_STUCK
19 #define INIT_STUCK 200000000 /*0xffffffff*/
20 
21 /*
22  * Try to acquire a spinlock.
23  * Only does the stwcx. if the load returned 0 - the Programming
24  * Environments Manual suggests not doing unnecessary stcwx.'s
25  * since they may inhibit forward progress by other CPUs in getting
26  * a lock.
27  */
__spin_trylock(volatile unsigned long * lock)28 static unsigned long __spin_trylock(volatile unsigned long *lock)
29 {
30 	unsigned long ret;
31 
32 	__asm__ __volatile__ ("\n\
33 1:	lwarx	%0,0,%1\n\
34 	cmpwi	0,%0,0\n\
35 	bne	2f\n"
36 	PPC405_ERR77(0,%1)
37 "	stwcx.	%2,0,%1\n\
38 	bne-	1b\n\
39 	isync\n\
40 2:"
41 	: "=&r"(ret)
42 	: "r"(lock), "r"(1)
43 	: "cr0", "memory");
44 
45 	return ret;
46 }
47 
_spin_lock(spinlock_t * lock)48 void _spin_lock(spinlock_t *lock)
49 {
50 	int cpu = smp_processor_id();
51 	unsigned int stuck = INIT_STUCK;
52 	while (__spin_trylock(&lock->lock)) {
53 		while ((unsigned volatile long)lock->lock != 0) {
54 			if (!--stuck) {
55 				printk("_spin_lock(%p) CPU#%d NIP %p"
56 				       " holder: cpu %ld pc %08lX\n",
57 				       lock, cpu, __builtin_return_address(0),
58 				       lock->owner_cpu,lock->owner_pc);
59 				stuck = INIT_STUCK;
60 				/* steal the lock */
61 				/*xchg_u32((void *)&lock->lock,0);*/
62 			}
63 		}
64 	}
65 	lock->owner_pc = (unsigned long)__builtin_return_address(0);
66 	lock->owner_cpu = cpu;
67 }
68 
spin_trylock(spinlock_t * lock)69 int spin_trylock(spinlock_t *lock)
70 {
71 	if (__spin_trylock(&lock->lock))
72 		return 0;
73 	lock->owner_cpu = smp_processor_id();
74 	lock->owner_pc = (unsigned long)__builtin_return_address(0);
75 	return 1;
76 }
77 
_spin_unlock(spinlock_t * lp)78 void _spin_unlock(spinlock_t *lp)
79 {
80   	if ( !lp->lock )
81 		printk("_spin_unlock(%p): no lock cpu %d curr PC %p %s/%d\n",
82 		       lp, smp_processor_id(), __builtin_return_address(0),
83 		       current->comm, current->pid);
84 	if ( lp->owner_cpu != smp_processor_id() )
85 		printk("_spin_unlock(%p): cpu %d trying clear of cpu %d pc %lx val %lx\n",
86 		      lp, smp_processor_id(), (int)lp->owner_cpu,
87 		      lp->owner_pc,lp->lock);
88 	lp->owner_pc = lp->owner_cpu = 0;
89 	wmb();
90 	lp->lock = 0;
91 }
92 
93 
94 /*
95  * Just like x86, implement read-write locks as a 32-bit counter
96  * with the high bit (sign) being the "write" bit.
97  * -- Cort
98  */
_read_lock(rwlock_t * rw)99 void _read_lock(rwlock_t *rw)
100 {
101 	unsigned long stuck = INIT_STUCK;
102 	int cpu = smp_processor_id();
103 
104 again:
105 	/* get our read lock in there */
106 	atomic_inc((atomic_t *) &(rw)->lock);
107 	if ( (signed long)((rw)->lock) < 0) /* someone has a write lock */
108 	{
109 		/* turn off our read lock */
110 		atomic_dec((atomic_t *) &(rw)->lock);
111 		/* wait for the write lock to go away */
112 		while ((signed long)((rw)->lock) < 0)
113 		{
114 			if(!--stuck)
115 			{
116 				printk("_read_lock(%p) CPU#%d\n", rw, cpu);
117 				stuck = INIT_STUCK;
118 			}
119 		}
120 		/* try to get the read lock again */
121 		goto again;
122 	}
123 	wmb();
124 }
125 
_read_unlock(rwlock_t * rw)126 void _read_unlock(rwlock_t *rw)
127 {
128 	if ( rw->lock == 0 )
129 		printk("_read_unlock(): %s/%d (nip %08lX) lock %lx\n",
130 		       current->comm,current->pid,current->thread.regs->nip,
131 		      rw->lock);
132 	wmb();
133 	atomic_dec((atomic_t *) &(rw)->lock);
134 }
135 
_write_lock(rwlock_t * rw)136 void _write_lock(rwlock_t *rw)
137 {
138 	unsigned long stuck = INIT_STUCK;
139 	int cpu = smp_processor_id();
140 
141 again:
142 	if ( test_and_set_bit(31,&(rw)->lock) ) /* someone has a write lock */
143 	{
144 		while ( (rw)->lock & (1<<31) ) /* wait for write lock */
145 		{
146 			if(!--stuck)
147 			{
148 				printk("write_lock(%p) CPU#%d lock %lx)\n",
149 				       rw, cpu,rw->lock);
150 				stuck = INIT_STUCK;
151 			}
152 			barrier();
153 		}
154 		goto again;
155 	}
156 
157 	if ( (rw)->lock & ~(1<<31)) /* someone has a read lock */
158 	{
159 		/* clear our write lock and wait for reads to go away */
160 		clear_bit(31,&(rw)->lock);
161 		while ( (rw)->lock & ~(1<<31) )
162 		{
163 			if(!--stuck)
164 			{
165 				printk("write_lock(%p) 2 CPU#%d lock %lx)\n",
166 				       rw, cpu,rw->lock);
167 				stuck = INIT_STUCK;
168 			}
169 			barrier();
170 		}
171 		goto again;
172 	}
173 	wmb();
174 }
175 
_write_unlock(rwlock_t * rw)176 void _write_unlock(rwlock_t *rw)
177 {
178 	if ( !(rw->lock & (1<<31)) )
179 		printk("_write_lock(): %s/%d (nip %08lX) lock %lx\n",
180 		      current->comm,current->pid,current->thread.regs->nip,
181 		      rw->lock);
182 	wmb();
183 	clear_bit(31,&(rw)->lock);
184 }
185 
186 #endif
187