xref: /linux/arch/parisc/include/asm/spinlock.h (revision e58e871becec2d3b04ed91c0c16fe8deac9c9dfa)
1 #ifndef __ASM_SPINLOCK_H
2 #define __ASM_SPINLOCK_H
3 
4 #include <asm/barrier.h>
5 #include <asm/ldcw.h>
6 #include <asm/processor.h>
7 #include <asm/spinlock_types.h>
8 
9 static inline int arch_spin_is_locked(arch_spinlock_t *x)
10 {
11 	volatile unsigned int *a = __ldcw_align(x);
12 	return *a == 0;
13 }
14 
15 #define arch_spin_lock(lock) arch_spin_lock_flags(lock, 0)
16 
17 static inline void arch_spin_unlock_wait(arch_spinlock_t *x)
18 {
19 	volatile unsigned int *a = __ldcw_align(x);
20 
21 	smp_cond_load_acquire(a, VAL);
22 }
23 
24 static inline void arch_spin_lock_flags(arch_spinlock_t *x,
25 					 unsigned long flags)
26 {
27 	volatile unsigned int *a;
28 
29 	mb();
30 	a = __ldcw_align(x);
31 	while (__ldcw(a) == 0)
32 		while (*a == 0)
33 			if (flags & PSW_SM_I) {
34 				local_irq_enable();
35 				cpu_relax();
36 				local_irq_disable();
37 			} else
38 				cpu_relax();
39 	mb();
40 }
41 
42 static inline void arch_spin_unlock(arch_spinlock_t *x)
43 {
44 	volatile unsigned int *a;
45 	mb();
46 	a = __ldcw_align(x);
47 	*a = 1;
48 	mb();
49 }
50 
51 static inline int arch_spin_trylock(arch_spinlock_t *x)
52 {
53 	volatile unsigned int *a;
54 	int ret;
55 
56 	mb();
57 	a = __ldcw_align(x);
58         ret = __ldcw(a) != 0;
59 	mb();
60 
61 	return ret;
62 }
63 
64 /*
65  * Read-write spinlocks, allowing multiple readers but only one writer.
66  * Linux rwlocks are unfair to writers; they can be starved for an indefinite
67  * time by readers.  With care, they can also be taken in interrupt context.
68  *
69  * In the PA-RISC implementation, we have a spinlock and a counter.
70  * Readers use the lock to serialise their access to the counter (which
71  * records how many readers currently hold the lock).
72  * Writers hold the spinlock, preventing any readers or other writers from
73  * grabbing the rwlock.
74  */
75 
76 /* Note that we have to ensure interrupts are disabled in case we're
77  * interrupted by some other code that wants to grab the same read lock */
78 static  __inline__ void arch_read_lock(arch_rwlock_t *rw)
79 {
80 	unsigned long flags;
81 	local_irq_save(flags);
82 	arch_spin_lock_flags(&rw->lock, flags);
83 	rw->counter++;
84 	arch_spin_unlock(&rw->lock);
85 	local_irq_restore(flags);
86 }
87 
88 /* Note that we have to ensure interrupts are disabled in case we're
89  * interrupted by some other code that wants to grab the same read lock */
90 static  __inline__ void arch_read_unlock(arch_rwlock_t *rw)
91 {
92 	unsigned long flags;
93 	local_irq_save(flags);
94 	arch_spin_lock_flags(&rw->lock, flags);
95 	rw->counter--;
96 	arch_spin_unlock(&rw->lock);
97 	local_irq_restore(flags);
98 }
99 
100 /* Note that we have to ensure interrupts are disabled in case we're
101  * interrupted by some other code that wants to grab the same read lock */
102 static __inline__ int arch_read_trylock(arch_rwlock_t *rw)
103 {
104 	unsigned long flags;
105  retry:
106 	local_irq_save(flags);
107 	if (arch_spin_trylock(&rw->lock)) {
108 		rw->counter++;
109 		arch_spin_unlock(&rw->lock);
110 		local_irq_restore(flags);
111 		return 1;
112 	}
113 
114 	local_irq_restore(flags);
115 	/* If write-locked, we fail to acquire the lock */
116 	if (rw->counter < 0)
117 		return 0;
118 
119 	/* Wait until we have a realistic chance at the lock */
120 	while (arch_spin_is_locked(&rw->lock) && rw->counter >= 0)
121 		cpu_relax();
122 
123 	goto retry;
124 }
125 
126 /* Note that we have to ensure interrupts are disabled in case we're
127  * interrupted by some other code that wants to read_trylock() this lock */
128 static __inline__ void arch_write_lock(arch_rwlock_t *rw)
129 {
130 	unsigned long flags;
131 retry:
132 	local_irq_save(flags);
133 	arch_spin_lock_flags(&rw->lock, flags);
134 
135 	if (rw->counter != 0) {
136 		arch_spin_unlock(&rw->lock);
137 		local_irq_restore(flags);
138 
139 		while (rw->counter != 0)
140 			cpu_relax();
141 
142 		goto retry;
143 	}
144 
145 	rw->counter = -1; /* mark as write-locked */
146 	mb();
147 	local_irq_restore(flags);
148 }
149 
150 static __inline__ void arch_write_unlock(arch_rwlock_t *rw)
151 {
152 	rw->counter = 0;
153 	arch_spin_unlock(&rw->lock);
154 }
155 
156 /* Note that we have to ensure interrupts are disabled in case we're
157  * interrupted by some other code that wants to read_trylock() this lock */
158 static __inline__ int arch_write_trylock(arch_rwlock_t *rw)
159 {
160 	unsigned long flags;
161 	int result = 0;
162 
163 	local_irq_save(flags);
164 	if (arch_spin_trylock(&rw->lock)) {
165 		if (rw->counter == 0) {
166 			rw->counter = -1;
167 			result = 1;
168 		} else {
169 			/* Read-locked.  Oh well. */
170 			arch_spin_unlock(&rw->lock);
171 		}
172 	}
173 	local_irq_restore(flags);
174 
175 	return result;
176 }
177 
178 /*
179  * read_can_lock - would read_trylock() succeed?
180  * @lock: the rwlock in question.
181  */
182 static __inline__ int arch_read_can_lock(arch_rwlock_t *rw)
183 {
184 	return rw->counter >= 0;
185 }
186 
187 /*
188  * write_can_lock - would write_trylock() succeed?
189  * @lock: the rwlock in question.
190  */
191 static __inline__ int arch_write_can_lock(arch_rwlock_t *rw)
192 {
193 	return !rw->counter;
194 }
195 
196 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
197 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
198 
199 #endif /* __ASM_SPINLOCK_H */
200