xref: /linux/include/asm-generic/qrwlock.h (revision 2db34e8b)
170af2f8aSWaiman Long /*
270af2f8aSWaiman Long  * Queue read/write lock
370af2f8aSWaiman Long  *
470af2f8aSWaiman Long  * This program is free software; you can redistribute it and/or modify
570af2f8aSWaiman Long  * it under the terms of the GNU General Public License as published by
670af2f8aSWaiman Long  * the Free Software Foundation; either version 2 of the License, or
770af2f8aSWaiman Long  * (at your option) any later version.
870af2f8aSWaiman Long  *
970af2f8aSWaiman Long  * This program is distributed in the hope that it will be useful,
1070af2f8aSWaiman Long  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1170af2f8aSWaiman Long  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1270af2f8aSWaiman Long  * GNU General Public License for more details.
1370af2f8aSWaiman Long  *
1470af2f8aSWaiman Long  * (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
1570af2f8aSWaiman Long  *
1670af2f8aSWaiman Long  * Authors: Waiman Long <waiman.long@hp.com>
1770af2f8aSWaiman Long  */
1870af2f8aSWaiman Long #ifndef __ASM_GENERIC_QRWLOCK_H
1970af2f8aSWaiman Long #define __ASM_GENERIC_QRWLOCK_H
2070af2f8aSWaiman Long 
2170af2f8aSWaiman Long #include <linux/atomic.h>
2270af2f8aSWaiman Long #include <asm/barrier.h>
2370af2f8aSWaiman Long #include <asm/processor.h>
2470af2f8aSWaiman Long 
2570af2f8aSWaiman Long #include <asm-generic/qrwlock_types.h>
2670af2f8aSWaiman Long 
2770af2f8aSWaiman Long /*
28*2db34e8bSpan xinhui  * Writer states & reader shift and bias.
29*2db34e8bSpan xinhui  *
30*2db34e8bSpan xinhui  *       | +0 | +1 | +2 | +3 |
31*2db34e8bSpan xinhui  *   ----+----+----+----+----+
32*2db34e8bSpan xinhui  *    LE | 78 | 56 | 34 | 12 | 0x12345678
33*2db34e8bSpan xinhui  *   ----+----+----+----+----+
34*2db34e8bSpan xinhui  *       | wr |      rd      |
35*2db34e8bSpan xinhui  *       +----+----+----+----+
36*2db34e8bSpan xinhui  *
37*2db34e8bSpan xinhui  *   ----+----+----+----+----+
38*2db34e8bSpan xinhui  *    BE | 12 | 34 | 56 | 78 | 0x12345678
39*2db34e8bSpan xinhui  *   ----+----+----+----+----+
40*2db34e8bSpan xinhui  *       |      rd      | wr |
41*2db34e8bSpan xinhui  *       +----+----+----+----+
4270af2f8aSWaiman Long  */
4370af2f8aSWaiman Long #define	_QW_WAITING	1		/* A writer is waiting	   */
4470af2f8aSWaiman Long #define	_QW_LOCKED	0xff		/* A writer holds the lock */
4570af2f8aSWaiman Long #define	_QW_WMASK	0xff		/* Writer mask		   */
4670af2f8aSWaiman Long #define	_QR_SHIFT	8		/* Reader count shift	   */
4770af2f8aSWaiman Long #define _QR_BIAS	(1U << _QR_SHIFT)
4870af2f8aSWaiman Long 
4970af2f8aSWaiman Long /*
5070af2f8aSWaiman Long  * External function declarations
5170af2f8aSWaiman Long  */
520e06e5beSWaiman Long extern void queued_read_lock_slowpath(struct qrwlock *lock, u32 cnts);
53f7d71f20SWaiman Long extern void queued_write_lock_slowpath(struct qrwlock *lock);
5470af2f8aSWaiman Long 
5570af2f8aSWaiman Long /**
56f7d71f20SWaiman Long  * queued_read_can_lock- would read_trylock() succeed?
5770af2f8aSWaiman Long  * @lock: Pointer to queue rwlock structure
5870af2f8aSWaiman Long  */
59f7d71f20SWaiman Long static inline int queued_read_can_lock(struct qrwlock *lock)
6070af2f8aSWaiman Long {
6170af2f8aSWaiman Long 	return !(atomic_read(&lock->cnts) & _QW_WMASK);
6270af2f8aSWaiman Long }
6370af2f8aSWaiman Long 
6470af2f8aSWaiman Long /**
65f7d71f20SWaiman Long  * queued_write_can_lock- would write_trylock() succeed?
6670af2f8aSWaiman Long  * @lock: Pointer to queue rwlock structure
6770af2f8aSWaiman Long  */
68f7d71f20SWaiman Long static inline int queued_write_can_lock(struct qrwlock *lock)
6970af2f8aSWaiman Long {
7070af2f8aSWaiman Long 	return !atomic_read(&lock->cnts);
7170af2f8aSWaiman Long }
7270af2f8aSWaiman Long 
7370af2f8aSWaiman Long /**
74f7d71f20SWaiman Long  * queued_read_trylock - try to acquire read lock of a queue rwlock
7570af2f8aSWaiman Long  * @lock : Pointer to queue rwlock structure
7670af2f8aSWaiman Long  * Return: 1 if lock acquired, 0 if failed
7770af2f8aSWaiman Long  */
78f7d71f20SWaiman Long static inline int queued_read_trylock(struct qrwlock *lock)
7970af2f8aSWaiman Long {
8070af2f8aSWaiman Long 	u32 cnts;
8170af2f8aSWaiman Long 
8270af2f8aSWaiman Long 	cnts = atomic_read(&lock->cnts);
8370af2f8aSWaiman Long 	if (likely(!(cnts & _QW_WMASK))) {
8477e430e3SWill Deacon 		cnts = (u32)atomic_add_return_acquire(_QR_BIAS, &lock->cnts);
8570af2f8aSWaiman Long 		if (likely(!(cnts & _QW_WMASK)))
8670af2f8aSWaiman Long 			return 1;
8770af2f8aSWaiman Long 		atomic_sub(_QR_BIAS, &lock->cnts);
8870af2f8aSWaiman Long 	}
8970af2f8aSWaiman Long 	return 0;
9070af2f8aSWaiman Long }
9170af2f8aSWaiman Long 
9270af2f8aSWaiman Long /**
93f7d71f20SWaiman Long  * queued_write_trylock - try to acquire write lock of a queue rwlock
9470af2f8aSWaiman Long  * @lock : Pointer to queue rwlock structure
9570af2f8aSWaiman Long  * Return: 1 if lock acquired, 0 if failed
9670af2f8aSWaiman Long  */
97f7d71f20SWaiman Long static inline int queued_write_trylock(struct qrwlock *lock)
9870af2f8aSWaiman Long {
9970af2f8aSWaiman Long 	u32 cnts;
10070af2f8aSWaiman Long 
10170af2f8aSWaiman Long 	cnts = atomic_read(&lock->cnts);
10270af2f8aSWaiman Long 	if (unlikely(cnts))
10370af2f8aSWaiman Long 		return 0;
10470af2f8aSWaiman Long 
10577e430e3SWill Deacon 	return likely(atomic_cmpxchg_acquire(&lock->cnts,
10670af2f8aSWaiman Long 					     cnts, cnts | _QW_LOCKED) == cnts);
10770af2f8aSWaiman Long }
10870af2f8aSWaiman Long /**
109f7d71f20SWaiman Long  * queued_read_lock - acquire read lock of a queue rwlock
11070af2f8aSWaiman Long  * @lock: Pointer to queue rwlock structure
11170af2f8aSWaiman Long  */
112f7d71f20SWaiman Long static inline void queued_read_lock(struct qrwlock *lock)
11370af2f8aSWaiman Long {
11470af2f8aSWaiman Long 	u32 cnts;
11570af2f8aSWaiman Long 
11677e430e3SWill Deacon 	cnts = atomic_add_return_acquire(_QR_BIAS, &lock->cnts);
11770af2f8aSWaiman Long 	if (likely(!(cnts & _QW_WMASK)))
11870af2f8aSWaiman Long 		return;
11970af2f8aSWaiman Long 
12070af2f8aSWaiman Long 	/* The slowpath will decrement the reader count, if necessary. */
1210e06e5beSWaiman Long 	queued_read_lock_slowpath(lock, cnts);
12270af2f8aSWaiman Long }
12370af2f8aSWaiman Long 
12470af2f8aSWaiman Long /**
125f7d71f20SWaiman Long  * queued_write_lock - acquire write lock of a queue rwlock
12670af2f8aSWaiman Long  * @lock : Pointer to queue rwlock structure
12770af2f8aSWaiman Long  */
128f7d71f20SWaiman Long static inline void queued_write_lock(struct qrwlock *lock)
12970af2f8aSWaiman Long {
13070af2f8aSWaiman Long 	/* Optimize for the unfair lock case where the fair flag is 0. */
13177e430e3SWill Deacon 	if (atomic_cmpxchg_acquire(&lock->cnts, 0, _QW_LOCKED) == 0)
13270af2f8aSWaiman Long 		return;
13370af2f8aSWaiman Long 
134f7d71f20SWaiman Long 	queued_write_lock_slowpath(lock);
13570af2f8aSWaiman Long }
13670af2f8aSWaiman Long 
13770af2f8aSWaiman Long /**
138f7d71f20SWaiman Long  * queued_read_unlock - release read lock of a queue rwlock
13970af2f8aSWaiman Long  * @lock : Pointer to queue rwlock structure
14070af2f8aSWaiman Long  */
141f7d71f20SWaiman Long static inline void queued_read_unlock(struct qrwlock *lock)
14270af2f8aSWaiman Long {
14370af2f8aSWaiman Long 	/*
14470af2f8aSWaiman Long 	 * Atomically decrement the reader count
14570af2f8aSWaiman Long 	 */
14677e430e3SWill Deacon 	(void)atomic_sub_return_release(_QR_BIAS, &lock->cnts);
14770af2f8aSWaiman Long }
14870af2f8aSWaiman Long 
14970af2f8aSWaiman Long /**
150*2db34e8bSpan xinhui  * __qrwlock_write_byte - retrieve the write byte address of a queue rwlock
151*2db34e8bSpan xinhui  * @lock : Pointer to queue rwlock structure
152*2db34e8bSpan xinhui  * Return: the write byte address of a queue rwlock
153*2db34e8bSpan xinhui  */
154*2db34e8bSpan xinhui static inline u8 *__qrwlock_write_byte(struct qrwlock *lock)
155*2db34e8bSpan xinhui {
156*2db34e8bSpan xinhui 	return (u8 *)lock + 3 * IS_BUILTIN(CONFIG_CPU_BIG_ENDIAN);
157*2db34e8bSpan xinhui }
158*2db34e8bSpan xinhui 
159*2db34e8bSpan xinhui /**
160f7d71f20SWaiman Long  * queued_write_unlock - release write lock of a queue rwlock
16170af2f8aSWaiman Long  * @lock : Pointer to queue rwlock structure
16270af2f8aSWaiman Long  */
163f7d71f20SWaiman Long static inline void queued_write_unlock(struct qrwlock *lock)
16470af2f8aSWaiman Long {
165*2db34e8bSpan xinhui 	smp_store_release(__qrwlock_write_byte(lock), 0);
16670af2f8aSWaiman Long }
16770af2f8aSWaiman Long 
16870af2f8aSWaiman Long /*
16970af2f8aSWaiman Long  * Remapping rwlock architecture specific functions to the corresponding
17070af2f8aSWaiman Long  * queue rwlock functions.
17170af2f8aSWaiman Long  */
172f7d71f20SWaiman Long #define arch_read_can_lock(l)	queued_read_can_lock(l)
173f7d71f20SWaiman Long #define arch_write_can_lock(l)	queued_write_can_lock(l)
174f7d71f20SWaiman Long #define arch_read_lock(l)	queued_read_lock(l)
175f7d71f20SWaiman Long #define arch_write_lock(l)	queued_write_lock(l)
176f7d71f20SWaiman Long #define arch_read_trylock(l)	queued_read_trylock(l)
177f7d71f20SWaiman Long #define arch_write_trylock(l)	queued_write_trylock(l)
178f7d71f20SWaiman Long #define arch_read_unlock(l)	queued_read_unlock(l)
179f7d71f20SWaiman Long #define arch_write_unlock(l)	queued_write_unlock(l)
18070af2f8aSWaiman Long 
18170af2f8aSWaiman Long #endif /* __ASM_GENERIC_QRWLOCK_H */
182