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