xref: /openbmc/linux/arch/s390/lib/spinlock.c (revision b13de4b7)
1951f22d5SMartin Schwidefsky /*
2951f22d5SMartin Schwidefsky  *    Out of line spinlock code.
3951f22d5SMartin Schwidefsky  *
4a53c8fabSHeiko Carstens  *    Copyright IBM Corp. 2004, 2006
5951f22d5SMartin Schwidefsky  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
6951f22d5SMartin Schwidefsky  */
7951f22d5SMartin Schwidefsky 
8951f22d5SMartin Schwidefsky #include <linux/types.h>
9d3217967SPaul Gortmaker #include <linux/export.h>
10951f22d5SMartin Schwidefsky #include <linux/spinlock.h>
11951f22d5SMartin Schwidefsky #include <linux/init.h>
128b646bd7SMartin Schwidefsky #include <linux/smp.h>
13951f22d5SMartin Schwidefsky #include <asm/io.h>
14951f22d5SMartin Schwidefsky 
152c72a44eSMartin Schwidefsky int spin_retry = -1;
162c72a44eSMartin Schwidefsky 
172c72a44eSMartin Schwidefsky static int __init spin_retry_init(void)
182c72a44eSMartin Schwidefsky {
192c72a44eSMartin Schwidefsky 	if (spin_retry < 0)
20b13de4b7SMartin Schwidefsky 		spin_retry = 1000;
212c72a44eSMartin Schwidefsky 	return 0;
222c72a44eSMartin Schwidefsky }
232c72a44eSMartin Schwidefsky early_initcall(spin_retry_init);
24951f22d5SMartin Schwidefsky 
25951f22d5SMartin Schwidefsky /**
26951f22d5SMartin Schwidefsky  * spin_retry= parameter
27951f22d5SMartin Schwidefsky  */
28951f22d5SMartin Schwidefsky static int __init spin_retry_setup(char *str)
29951f22d5SMartin Schwidefsky {
30951f22d5SMartin Schwidefsky 	spin_retry = simple_strtoul(str, &str, 0);
31951f22d5SMartin Schwidefsky 	return 1;
32951f22d5SMartin Schwidefsky }
33951f22d5SMartin Schwidefsky __setup("spin_retry=", spin_retry_setup);
34951f22d5SMartin Schwidefsky 
350199c4e6SThomas Gleixner void arch_spin_lock_wait(arch_spinlock_t *lp)
36951f22d5SMartin Schwidefsky {
3702c503ffSMartin Schwidefsky 	int cpu = SPINLOCK_LOCKVAL;
3802c503ffSMartin Schwidefsky 	int owner, count, first_diag;
39951f22d5SMartin Schwidefsky 
40db1c4515SMartin Schwidefsky 	first_diag = 1;
41951f22d5SMartin Schwidefsky 	while (1) {
42470ada6bSMartin Schwidefsky 		owner = ACCESS_ONCE(lp->lock);
43470ada6bSMartin Schwidefsky 		/* Try to get the lock if it is free. */
44470ada6bSMartin Schwidefsky 		if (!owner) {
4502c503ffSMartin Schwidefsky 			if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu))
4659b69787SGerald Schaefer 				return;
4759b69787SGerald Schaefer 			continue;
4859b69787SGerald Schaefer 		}
49db1c4515SMartin Schwidefsky 		/* First iteration: check if the lock owner is running. */
50760928c0SChristian Borntraeger 		if (first_diag && arch_vcpu_is_preempted(~owner)) {
518b646bd7SMartin Schwidefsky 			smp_yield_cpu(~owner);
52db1c4515SMartin Schwidefsky 			first_diag = 0;
53470ada6bSMartin Schwidefsky 			continue;
54470ada6bSMartin Schwidefsky 		}
55470ada6bSMartin Schwidefsky 		/* Loop for a while on the lock value. */
56470ada6bSMartin Schwidefsky 		count = spin_retry;
57470ada6bSMartin Schwidefsky 		do {
58470ada6bSMartin Schwidefsky 			owner = ACCESS_ONCE(lp->lock);
59470ada6bSMartin Schwidefsky 		} while (owner && count-- > 0);
60470ada6bSMartin Schwidefsky 		if (!owner)
61470ada6bSMartin Schwidefsky 			continue;
62470ada6bSMartin Schwidefsky 		/*
63470ada6bSMartin Schwidefsky 		 * For multiple layers of hypervisors, e.g. z/VM + LPAR
64db1c4515SMartin Schwidefsky 		 * yield the CPU unconditionally. For LPAR rely on the
65db1c4515SMartin Schwidefsky 		 * sense running status.
66470ada6bSMartin Schwidefsky 		 */
67760928c0SChristian Borntraeger 		if (!MACHINE_IS_LPAR || arch_vcpu_is_preempted(~owner)) {
68470ada6bSMartin Schwidefsky 			smp_yield_cpu(~owner);
69db1c4515SMartin Schwidefsky 			first_diag = 0;
70db1c4515SMartin Schwidefsky 		}
71951f22d5SMartin Schwidefsky 	}
72951f22d5SMartin Schwidefsky }
730199c4e6SThomas Gleixner EXPORT_SYMBOL(arch_spin_lock_wait);
74951f22d5SMartin Schwidefsky 
750199c4e6SThomas Gleixner void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags)
76894cdde2SHisashi Hifumi {
7702c503ffSMartin Schwidefsky 	int cpu = SPINLOCK_LOCKVAL;
7802c503ffSMartin Schwidefsky 	int owner, count, first_diag;
79894cdde2SHisashi Hifumi 
80894cdde2SHisashi Hifumi 	local_irq_restore(flags);
81db1c4515SMartin Schwidefsky 	first_diag = 1;
82894cdde2SHisashi Hifumi 	while (1) {
83470ada6bSMartin Schwidefsky 		owner = ACCESS_ONCE(lp->lock);
84470ada6bSMartin Schwidefsky 		/* Try to get the lock if it is free. */
85470ada6bSMartin Schwidefsky 		if (!owner) {
86894cdde2SHisashi Hifumi 			local_irq_disable();
8702c503ffSMartin Schwidefsky 			if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu))
8859b69787SGerald Schaefer 				return;
8959b69787SGerald Schaefer 			local_irq_restore(flags);
9084976952SHeiko Carstens 			continue;
91470ada6bSMartin Schwidefsky 		}
92470ada6bSMartin Schwidefsky 		/* Check if the lock owner is running. */
93760928c0SChristian Borntraeger 		if (first_diag && arch_vcpu_is_preempted(~owner)) {
94470ada6bSMartin Schwidefsky 			smp_yield_cpu(~owner);
95db1c4515SMartin Schwidefsky 			first_diag = 0;
9659b69787SGerald Schaefer 			continue;
9759b69787SGerald Schaefer 		}
98470ada6bSMartin Schwidefsky 		/* Loop for a while on the lock value. */
99470ada6bSMartin Schwidefsky 		count = spin_retry;
100470ada6bSMartin Schwidefsky 		do {
101470ada6bSMartin Schwidefsky 			owner = ACCESS_ONCE(lp->lock);
102470ada6bSMartin Schwidefsky 		} while (owner && count-- > 0);
103470ada6bSMartin Schwidefsky 		if (!owner)
104470ada6bSMartin Schwidefsky 			continue;
105470ada6bSMartin Schwidefsky 		/*
106470ada6bSMartin Schwidefsky 		 * For multiple layers of hypervisors, e.g. z/VM + LPAR
107db1c4515SMartin Schwidefsky 		 * yield the CPU unconditionally. For LPAR rely on the
108db1c4515SMartin Schwidefsky 		 * sense running status.
109470ada6bSMartin Schwidefsky 		 */
110760928c0SChristian Borntraeger 		if (!MACHINE_IS_LPAR || arch_vcpu_is_preempted(~owner)) {
1118b646bd7SMartin Schwidefsky 			smp_yield_cpu(~owner);
112db1c4515SMartin Schwidefsky 			first_diag = 0;
113db1c4515SMartin Schwidefsky 		}
114894cdde2SHisashi Hifumi 	}
115894cdde2SHisashi Hifumi }
1160199c4e6SThomas Gleixner EXPORT_SYMBOL(arch_spin_lock_wait_flags);
117894cdde2SHisashi Hifumi 
1185b3f683eSPhilipp Hachtmann int arch_spin_trylock_retry(arch_spinlock_t *lp)
1195b3f683eSPhilipp Hachtmann {
12002c503ffSMartin Schwidefsky 	int cpu = SPINLOCK_LOCKVAL;
12102c503ffSMartin Schwidefsky 	int owner, count;
1225b3f683eSPhilipp Hachtmann 
1232c72a44eSMartin Schwidefsky 	for (count = spin_retry; count > 0; count--) {
124187b5f41SChristian Borntraeger 		owner = READ_ONCE(lp->lock);
1252c72a44eSMartin Schwidefsky 		/* Try to get the lock if it is free. */
1262c72a44eSMartin Schwidefsky 		if (!owner) {
12702c503ffSMartin Schwidefsky 			if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu))
1285b3f683eSPhilipp Hachtmann 				return 1;
129b13de4b7SMartin Schwidefsky 		}
1302c72a44eSMartin Schwidefsky 	}
1315b3f683eSPhilipp Hachtmann 	return 0;
1325b3f683eSPhilipp Hachtmann }
1335b3f683eSPhilipp Hachtmann EXPORT_SYMBOL(arch_spin_trylock_retry);
1345b3f683eSPhilipp Hachtmann 
135fb3a6bbcSThomas Gleixner void _raw_read_lock_wait(arch_rwlock_t *rw)
136951f22d5SMartin Schwidefsky {
137951f22d5SMartin Schwidefsky 	int count = spin_retry;
13802c503ffSMartin Schwidefsky 	int owner, old;
139951f22d5SMartin Schwidefsky 
140bbae71bfSMartin Schwidefsky #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
141bbae71bfSMartin Schwidefsky 	__RAW_LOCK(&rw->lock, -1, __RAW_OP_ADD);
142bbae71bfSMartin Schwidefsky #endif
143d59b93daSMartin Schwidefsky 	owner = 0;
144951f22d5SMartin Schwidefsky 	while (1) {
145951f22d5SMartin Schwidefsky 		if (count-- <= 0) {
146760928c0SChristian Borntraeger 			if (owner && arch_vcpu_is_preempted(~owner))
147d59b93daSMartin Schwidefsky 				smp_yield_cpu(~owner);
148951f22d5SMartin Schwidefsky 			count = spin_retry;
149951f22d5SMartin Schwidefsky 		}
150bae8f567SMartin Schwidefsky 		old = ACCESS_ONCE(rw->lock);
151d59b93daSMartin Schwidefsky 		owner = ACCESS_ONCE(rw->owner);
152b13de4b7SMartin Schwidefsky 		if (old < 0)
15396567161SChristian Ehrhardt 			continue;
15402c503ffSMartin Schwidefsky 		if (__atomic_cmpxchg_bool(&rw->lock, old, old + 1))
155951f22d5SMartin Schwidefsky 			return;
156951f22d5SMartin Schwidefsky 	}
157951f22d5SMartin Schwidefsky }
158951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_read_lock_wait);
159951f22d5SMartin Schwidefsky 
160fb3a6bbcSThomas Gleixner int _raw_read_trylock_retry(arch_rwlock_t *rw)
161951f22d5SMartin Schwidefsky {
162951f22d5SMartin Schwidefsky 	int count = spin_retry;
16302c503ffSMartin Schwidefsky 	int old;
164951f22d5SMartin Schwidefsky 
165951f22d5SMartin Schwidefsky 	while (count-- > 0) {
166bae8f567SMartin Schwidefsky 		old = ACCESS_ONCE(rw->lock);
167b13de4b7SMartin Schwidefsky 		if (old < 0)
16896567161SChristian Ehrhardt 			continue;
16902c503ffSMartin Schwidefsky 		if (__atomic_cmpxchg_bool(&rw->lock, old, old + 1))
170951f22d5SMartin Schwidefsky 			return 1;
171951f22d5SMartin Schwidefsky 	}
172951f22d5SMartin Schwidefsky 	return 0;
173951f22d5SMartin Schwidefsky }
174951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_read_trylock_retry);
175951f22d5SMartin Schwidefsky 
176bbae71bfSMartin Schwidefsky #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
177bbae71bfSMartin Schwidefsky 
17802c503ffSMartin Schwidefsky void _raw_write_lock_wait(arch_rwlock_t *rw, int prev)
179bbae71bfSMartin Schwidefsky {
180bbae71bfSMartin Schwidefsky 	int count = spin_retry;
18102c503ffSMartin Schwidefsky 	int owner, old;
182bbae71bfSMartin Schwidefsky 
183bbae71bfSMartin Schwidefsky 	owner = 0;
184bbae71bfSMartin Schwidefsky 	while (1) {
185bbae71bfSMartin Schwidefsky 		if (count-- <= 0) {
186760928c0SChristian Borntraeger 			if (owner && arch_vcpu_is_preempted(~owner))
187bbae71bfSMartin Schwidefsky 				smp_yield_cpu(~owner);
188bbae71bfSMartin Schwidefsky 			count = spin_retry;
189bbae71bfSMartin Schwidefsky 		}
190bbae71bfSMartin Schwidefsky 		old = ACCESS_ONCE(rw->lock);
191bbae71bfSMartin Schwidefsky 		owner = ACCESS_ONCE(rw->owner);
192e0af21c5SChristian Borntraeger 		smp_mb();
19302c503ffSMartin Schwidefsky 		if (old >= 0) {
194bbae71bfSMartin Schwidefsky 			prev = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR);
195bbae71bfSMartin Schwidefsky 			old = prev;
196bbae71bfSMartin Schwidefsky 		}
19702c503ffSMartin Schwidefsky 		if ((old & 0x7fffffff) == 0 && prev >= 0)
198bbae71bfSMartin Schwidefsky 			break;
199bbae71bfSMartin Schwidefsky 	}
200bbae71bfSMartin Schwidefsky }
201bbae71bfSMartin Schwidefsky EXPORT_SYMBOL(_raw_write_lock_wait);
202bbae71bfSMartin Schwidefsky 
203bbae71bfSMartin Schwidefsky #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */
204bbae71bfSMartin Schwidefsky 
205fb3a6bbcSThomas Gleixner void _raw_write_lock_wait(arch_rwlock_t *rw)
206951f22d5SMartin Schwidefsky {
207951f22d5SMartin Schwidefsky 	int count = spin_retry;
20802c503ffSMartin Schwidefsky 	int owner, old, prev;
209951f22d5SMartin Schwidefsky 
21094232a43SMartin Schwidefsky 	prev = 0x80000000;
211d59b93daSMartin Schwidefsky 	owner = 0;
212951f22d5SMartin Schwidefsky 	while (1) {
213951f22d5SMartin Schwidefsky 		if (count-- <= 0) {
214760928c0SChristian Borntraeger 			if (owner && arch_vcpu_is_preempted(~owner))
215d59b93daSMartin Schwidefsky 				smp_yield_cpu(~owner);
216951f22d5SMartin Schwidefsky 			count = spin_retry;
217951f22d5SMartin Schwidefsky 		}
218bae8f567SMartin Schwidefsky 		old = ACCESS_ONCE(rw->lock);
219d59b93daSMartin Schwidefsky 		owner = ACCESS_ONCE(rw->owner);
22002c503ffSMartin Schwidefsky 		if (old >= 0 &&
22102c503ffSMartin Schwidefsky 		    __atomic_cmpxchg_bool(&rw->lock, old, old | 0x80000000))
22294232a43SMartin Schwidefsky 			prev = old;
22394232a43SMartin Schwidefsky 		else
224e0af21c5SChristian Borntraeger 			smp_mb();
22502c503ffSMartin Schwidefsky 		if ((old & 0x7fffffff) == 0 && prev >= 0)
22694232a43SMartin Schwidefsky 			break;
227951f22d5SMartin Schwidefsky 	}
228951f22d5SMartin Schwidefsky }
229951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_write_lock_wait);
230951f22d5SMartin Schwidefsky 
231bbae71bfSMartin Schwidefsky #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
232bbae71bfSMartin Schwidefsky 
233fb3a6bbcSThomas Gleixner int _raw_write_trylock_retry(arch_rwlock_t *rw)
234951f22d5SMartin Schwidefsky {
235951f22d5SMartin Schwidefsky 	int count = spin_retry;
23602c503ffSMartin Schwidefsky 	int old;
237951f22d5SMartin Schwidefsky 
238951f22d5SMartin Schwidefsky 	while (count-- > 0) {
239bae8f567SMartin Schwidefsky 		old = ACCESS_ONCE(rw->lock);
240b13de4b7SMartin Schwidefsky 		if (old)
24196567161SChristian Ehrhardt 			continue;
24202c503ffSMartin Schwidefsky 		if (__atomic_cmpxchg_bool(&rw->lock, 0, 0x80000000))
243951f22d5SMartin Schwidefsky 			return 1;
244951f22d5SMartin Schwidefsky 	}
245951f22d5SMartin Schwidefsky 	return 0;
246951f22d5SMartin Schwidefsky }
247951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_write_trylock_retry);
248d59b93daSMartin Schwidefsky 
24902c503ffSMartin Schwidefsky void arch_lock_relax(int cpu)
250d59b93daSMartin Schwidefsky {
251d59b93daSMartin Schwidefsky 	if (!cpu)
252d59b93daSMartin Schwidefsky 		return;
253760928c0SChristian Borntraeger 	if (MACHINE_IS_LPAR && !arch_vcpu_is_preempted(~cpu))
254d59b93daSMartin Schwidefsky 		return;
255d59b93daSMartin Schwidefsky 	smp_yield_cpu(~cpu);
256d59b93daSMartin Schwidefsky }
257d59b93daSMartin Schwidefsky EXPORT_SYMBOL(arch_lock_relax);
258