xref: /openbmc/linux/arch/s390/lib/spinlock.c (revision d2999e1b)
1 /*
2  *    Out of line spinlock code.
3  *
4  *    Copyright IBM Corp. 2004, 2006
5  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
6  */
7 
8 #include <linux/types.h>
9 #include <linux/module.h>
10 #include <linux/spinlock.h>
11 #include <linux/init.h>
12 #include <linux/smp.h>
13 #include <asm/io.h>
14 
15 int spin_retry = 1000;
16 
17 /**
18  * spin_retry= parameter
19  */
20 static int __init spin_retry_setup(char *str)
21 {
22 	spin_retry = simple_strtoul(str, &str, 0);
23 	return 1;
24 }
25 __setup("spin_retry=", spin_retry_setup);
26 
27 void arch_spin_lock_wait(arch_spinlock_t *lp)
28 {
29 	unsigned int cpu = SPINLOCK_LOCKVAL;
30 	unsigned int owner;
31 	int count;
32 
33 	while (1) {
34 		owner = ACCESS_ONCE(lp->lock);
35 		/* Try to get the lock if it is free. */
36 		if (!owner) {
37 			if (_raw_compare_and_swap(&lp->lock, 0, cpu))
38 				return;
39 			continue;
40 		}
41 		/* Check if the lock owner is running. */
42 		if (!smp_vcpu_scheduled(~owner)) {
43 			smp_yield_cpu(~owner);
44 			continue;
45 		}
46 		/* Loop for a while on the lock value. */
47 		count = spin_retry;
48 		do {
49 			owner = ACCESS_ONCE(lp->lock);
50 		} while (owner && count-- > 0);
51 		if (!owner)
52 			continue;
53 		/*
54 		 * For multiple layers of hypervisors, e.g. z/VM + LPAR
55 		 * yield the CPU if the lock is still unavailable.
56 		 */
57 		if (!MACHINE_IS_LPAR)
58 			smp_yield_cpu(~owner);
59 	}
60 }
61 EXPORT_SYMBOL(arch_spin_lock_wait);
62 
63 void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
64 {
65 	unsigned int cpu = SPINLOCK_LOCKVAL;
66 	unsigned int owner;
67 	int count;
68 
69 	local_irq_restore(flags);
70 	while (1) {
71 		owner = ACCESS_ONCE(lp->lock);
72 		/* Try to get the lock if it is free. */
73 		if (!owner) {
74 			local_irq_disable();
75 			if (_raw_compare_and_swap(&lp->lock, 0, cpu))
76 				return;
77 			local_irq_restore(flags);
78 		}
79 		/* Check if the lock owner is running. */
80 		if (!smp_vcpu_scheduled(~owner)) {
81 			smp_yield_cpu(~owner);
82 			continue;
83 		}
84 		/* Loop for a while on the lock value. */
85 		count = spin_retry;
86 		do {
87 			owner = ACCESS_ONCE(lp->lock);
88 		} while (owner && count-- > 0);
89 		if (!owner)
90 			continue;
91 		/*
92 		 * For multiple layers of hypervisors, e.g. z/VM + LPAR
93 		 * yield the CPU if the lock is still unavailable.
94 		 */
95 		if (!MACHINE_IS_LPAR)
96 			smp_yield_cpu(~owner);
97 	}
98 }
99 EXPORT_SYMBOL(arch_spin_lock_wait_flags);
100 
101 void arch_spin_relax(arch_spinlock_t *lp)
102 {
103 	unsigned int cpu = lp->lock;
104 	if (cpu != 0) {
105 		if (MACHINE_IS_VM || MACHINE_IS_KVM ||
106 		    !smp_vcpu_scheduled(~cpu))
107 			smp_yield_cpu(~cpu);
108 	}
109 }
110 EXPORT_SYMBOL(arch_spin_relax);
111 
112 int arch_spin_trylock_retry(arch_spinlock_t *lp)
113 {
114 	int count;
115 
116 	for (count = spin_retry; count > 0; count--)
117 		if (arch_spin_trylock_once(lp))
118 			return 1;
119 	return 0;
120 }
121 EXPORT_SYMBOL(arch_spin_trylock_retry);
122 
123 void _raw_read_lock_wait(arch_rwlock_t *rw)
124 {
125 	unsigned int old;
126 	int count = spin_retry;
127 
128 	while (1) {
129 		if (count-- <= 0) {
130 			smp_yield();
131 			count = spin_retry;
132 		}
133 		old = ACCESS_ONCE(rw->lock);
134 		if ((int) old < 0)
135 			continue;
136 		if (_raw_compare_and_swap(&rw->lock, old, old + 1))
137 			return;
138 	}
139 }
140 EXPORT_SYMBOL(_raw_read_lock_wait);
141 
142 void _raw_read_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags)
143 {
144 	unsigned int old;
145 	int count = spin_retry;
146 
147 	local_irq_restore(flags);
148 	while (1) {
149 		if (count-- <= 0) {
150 			smp_yield();
151 			count = spin_retry;
152 		}
153 		old = ACCESS_ONCE(rw->lock);
154 		if ((int) old < 0)
155 			continue;
156 		local_irq_disable();
157 		if (_raw_compare_and_swap(&rw->lock, old, old + 1))
158 			return;
159 		local_irq_restore(flags);
160 	}
161 }
162 EXPORT_SYMBOL(_raw_read_lock_wait_flags);
163 
164 int _raw_read_trylock_retry(arch_rwlock_t *rw)
165 {
166 	unsigned int old;
167 	int count = spin_retry;
168 
169 	while (count-- > 0) {
170 		old = ACCESS_ONCE(rw->lock);
171 		if ((int) old < 0)
172 			continue;
173 		if (_raw_compare_and_swap(&rw->lock, old, old + 1))
174 			return 1;
175 	}
176 	return 0;
177 }
178 EXPORT_SYMBOL(_raw_read_trylock_retry);
179 
180 void _raw_write_lock_wait(arch_rwlock_t *rw)
181 {
182 	unsigned int old;
183 	int count = spin_retry;
184 
185 	while (1) {
186 		if (count-- <= 0) {
187 			smp_yield();
188 			count = spin_retry;
189 		}
190 		old = ACCESS_ONCE(rw->lock);
191 		if (old)
192 			continue;
193 		if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000))
194 			return;
195 	}
196 }
197 EXPORT_SYMBOL(_raw_write_lock_wait);
198 
199 void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags)
200 {
201 	unsigned int old;
202 	int count = spin_retry;
203 
204 	local_irq_restore(flags);
205 	while (1) {
206 		if (count-- <= 0) {
207 			smp_yield();
208 			count = spin_retry;
209 		}
210 		old = ACCESS_ONCE(rw->lock);
211 		if (old)
212 			continue;
213 		local_irq_disable();
214 		if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000))
215 			return;
216 		local_irq_restore(flags);
217 	}
218 }
219 EXPORT_SYMBOL(_raw_write_lock_wait_flags);
220 
221 int _raw_write_trylock_retry(arch_rwlock_t *rw)
222 {
223 	unsigned int old;
224 	int count = spin_retry;
225 
226 	while (count-- > 0) {
227 		old = ACCESS_ONCE(rw->lock);
228 		if (old)
229 			continue;
230 		if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000))
231 			return 1;
232 	}
233 	return 0;
234 }
235 EXPORT_SYMBOL(_raw_write_trylock_retry);
236