xref: /dflybsd-src/sys/dev/drm/include/linux/seqlock.h (revision a85cb24f18e3804e75ab8bcda7692564d0563317)
139cfddd2SFrançois Tigeot /*
2ec5b6af4SFrançois Tigeot  * Copyright (c) 2017-2020 François Tigeot <ftigeot@wolfpond.org>
339cfddd2SFrançois Tigeot  * All rights reserved.
439cfddd2SFrançois Tigeot  *
539cfddd2SFrançois Tigeot  * Redistribution and use in source and binary forms, with or without
639cfddd2SFrançois Tigeot  * modification, are permitted provided that the following conditions
739cfddd2SFrançois Tigeot  * are met:
839cfddd2SFrançois Tigeot  * 1. Redistributions of source code must retain the above copyright
939cfddd2SFrançois Tigeot  *    notice unmodified, this list of conditions, and the following
1039cfddd2SFrançois Tigeot  *    disclaimer.
1139cfddd2SFrançois Tigeot  * 2. Redistributions in binary form must reproduce the above copyright
1239cfddd2SFrançois Tigeot  *    notice, this list of conditions and the following disclaimer in the
1339cfddd2SFrançois Tigeot  *    documentation and/or other materials provided with the distribution.
1439cfddd2SFrançois Tigeot  *
1539cfddd2SFrançois Tigeot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1639cfddd2SFrançois Tigeot  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1739cfddd2SFrançois Tigeot  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1839cfddd2SFrançois Tigeot  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1939cfddd2SFrançois Tigeot  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2039cfddd2SFrançois Tigeot  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2139cfddd2SFrançois Tigeot  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2239cfddd2SFrançois Tigeot  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2339cfddd2SFrançois Tigeot  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2439cfddd2SFrançois Tigeot  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2539cfddd2SFrançois Tigeot  */
2639cfddd2SFrançois Tigeot 
2739cfddd2SFrançois Tigeot #ifndef _LINUX_SEQLOCK_H_
2839cfddd2SFrançois Tigeot #define _LINUX_SEQLOCK_H_
2939cfddd2SFrançois Tigeot 
3039cfddd2SFrançois Tigeot #include <linux/spinlock.h>
31237ffc69SFrançois Tigeot #include <linux/preempt.h>
3239cfddd2SFrançois Tigeot #include <linux/lockdep.h>
3339cfddd2SFrançois Tigeot #include <linux/compiler.h>
34c6002f72SFrançois Tigeot #include <asm/processor.h>
3539cfddd2SFrançois Tigeot 
36dcbb2408SFrançois Tigeot /*
37dcbb2408SFrançois Tigeot  * Seqlock definition from Wikipedia
38dcbb2408SFrançois Tigeot  *
39dcbb2408SFrançois Tigeot  * A seqlock consists of storage for saving a sequence number in addition to a lock.
40dcbb2408SFrançois Tigeot  * The lock is to support synchronization between two writers and the counter is for
41dcbb2408SFrançois Tigeot  * indicating consistency in readers. In addition to updating the shared data, the
42dcbb2408SFrançois Tigeot  * writer increments the sequence number, both after acquiring the lock and before
43dcbb2408SFrançois Tigeot  * releasing the lock. Readers read the sequence number before and after reading the
44dcbb2408SFrançois Tigeot  * shared data. If the sequence number is odd on either occasion, a writer had taken
45dcbb2408SFrançois Tigeot  * the lock while the data was being read and it may have changed. If the sequence
46dcbb2408SFrançois Tigeot  * numbers are different, a writer has changed the data while it was being read. In
47dcbb2408SFrançois Tigeot  * either case readers simply retry (using a loop) until they read the same even
48dcbb2408SFrançois Tigeot  * sequence number before and after.
49dcbb2408SFrançois Tigeot  *
50dcbb2408SFrançois Tigeot  */
51dcbb2408SFrançois Tigeot 
5239cfddd2SFrançois Tigeot typedef struct {
5339cfddd2SFrançois Tigeot 	unsigned sequence;
54ec5b6af4SFrançois Tigeot 	struct lock lock;
5539cfddd2SFrançois Tigeot } seqlock_t;
5639cfddd2SFrançois Tigeot 
5739cfddd2SFrançois Tigeot static inline void
seqlock_init(seqlock_t * sl)5839cfddd2SFrançois Tigeot seqlock_init(seqlock_t *sl)
5939cfddd2SFrançois Tigeot {
6039cfddd2SFrançois Tigeot 	sl->sequence = 0;
619a49c39cSFrançois Tigeot 	lockinit(&sl->lock, "lsql", 0, 0);
6239cfddd2SFrançois Tigeot }
6339cfddd2SFrançois Tigeot 
6439cfddd2SFrançois Tigeot /*
6539cfddd2SFrançois Tigeot  * Writers always use a spinlock. We still use store barriers
6639cfddd2SFrançois Tigeot  * in order to quickly update the state of the sequence variable
6739cfddd2SFrançois Tigeot  * for readers.
6839cfddd2SFrançois Tigeot  */
6939cfddd2SFrançois Tigeot static inline void
write_seqlock(seqlock_t * sl)7039cfddd2SFrançois Tigeot write_seqlock(seqlock_t *sl)
7139cfddd2SFrançois Tigeot {
72ec5b6af4SFrançois Tigeot 	lockmgr(&sl->lock, LK_EXCLUSIVE);
7339cfddd2SFrançois Tigeot 	sl->sequence++;
7439cfddd2SFrançois Tigeot 	cpu_sfence();
7539cfddd2SFrançois Tigeot }
7639cfddd2SFrançois Tigeot 
7739cfddd2SFrançois Tigeot static inline void
write_sequnlock(seqlock_t * sl)7839cfddd2SFrançois Tigeot write_sequnlock(seqlock_t *sl)
7939cfddd2SFrançois Tigeot {
8039cfddd2SFrançois Tigeot 	sl->sequence--;
81ec5b6af4SFrançois Tigeot 	lockmgr(&sl->lock, LK_RELEASE);
8239cfddd2SFrançois Tigeot 	cpu_sfence();
8339cfddd2SFrançois Tigeot }
8439cfddd2SFrançois Tigeot 
8539cfddd2SFrançois Tigeot /*
8639cfddd2SFrançois Tigeot  * Read functions are fully unlocked.
8739cfddd2SFrançois Tigeot  * We use load barriers to obtain a reasonably up-to-date state
8839cfddd2SFrançois Tigeot  * for the sequence number.
8939cfddd2SFrançois Tigeot  */
9039cfddd2SFrançois Tigeot static inline unsigned
read_seqbegin(const seqlock_t * sl)9139cfddd2SFrançois Tigeot read_seqbegin(const seqlock_t *sl)
9239cfddd2SFrançois Tigeot {
9339cfddd2SFrançois Tigeot 	return READ_ONCE(sl->sequence);
9439cfddd2SFrançois Tigeot }
9539cfddd2SFrançois Tigeot 
9639cfddd2SFrançois Tigeot static inline unsigned
read_seqretry(const seqlock_t * sl,unsigned start)9739cfddd2SFrançois Tigeot read_seqretry(const seqlock_t *sl, unsigned start)
9839cfddd2SFrançois Tigeot {
9939cfddd2SFrançois Tigeot 	cpu_lfence();
10039cfddd2SFrançois Tigeot 	return (sl->sequence != start);
10139cfddd2SFrançois Tigeot }
10239cfddd2SFrançois Tigeot 
1033306aed3SFrançois Tigeot typedef struct seqcount {
1043306aed3SFrançois Tigeot 	unsigned sequence;
1053306aed3SFrançois Tigeot } seqcount_t;
1063306aed3SFrançois Tigeot 
1073306aed3SFrançois Tigeot static inline void
__seqcount_init(seqcount_t * s,const char * name,struct lock_class_key * key)1083306aed3SFrançois Tigeot __seqcount_init(seqcount_t *s, const char *name, struct lock_class_key *key)
1093306aed3SFrançois Tigeot {
1103306aed3SFrançois Tigeot 	s->sequence = 0;
1113306aed3SFrançois Tigeot }
1123306aed3SFrançois Tigeot 
113dcbb2408SFrançois Tigeot static inline unsigned int
__read_seqcount_begin(const seqcount_t * s)114*a85cb24fSFrançois Tigeot __read_seqcount_begin(const seqcount_t *s)
115dcbb2408SFrançois Tigeot {
116dcbb2408SFrançois Tigeot 	unsigned int ret;
117dcbb2408SFrançois Tigeot 
118dcbb2408SFrançois Tigeot 	do {
119dcbb2408SFrançois Tigeot 		ret = READ_ONCE(s->sequence);
120dcbb2408SFrançois Tigeot 		/* If the sequence number is odd, a writer has taken the lock */
121dcbb2408SFrançois Tigeot 		if ((ret & 1) == 0)
122dcbb2408SFrançois Tigeot 			break;
123dcbb2408SFrançois Tigeot 		cpu_pause();
124dcbb2408SFrançois Tigeot 	} while (1);
125dcbb2408SFrançois Tigeot 
126dcbb2408SFrançois Tigeot 	return ret;
127dcbb2408SFrançois Tigeot }
128dcbb2408SFrançois Tigeot 
129*a85cb24fSFrançois Tigeot static inline unsigned int
read_seqcount_begin(const seqcount_t * s)130*a85cb24fSFrançois Tigeot read_seqcount_begin(const seqcount_t *s)
131*a85cb24fSFrançois Tigeot {
132*a85cb24fSFrançois Tigeot 	unsigned int ret = __read_seqcount_begin(s);
133*a85cb24fSFrançois Tigeot 
134*a85cb24fSFrançois Tigeot 	cpu_lfence();
135*a85cb24fSFrançois Tigeot 
136*a85cb24fSFrançois Tigeot 	return ret;
137*a85cb24fSFrançois Tigeot }
138*a85cb24fSFrançois Tigeot 
139*a85cb24fSFrançois Tigeot static inline int
__read_seqcount_retry(const seqcount_t * s,unsigned start)140*a85cb24fSFrançois Tigeot __read_seqcount_retry(const seqcount_t *s, unsigned start)
141*a85cb24fSFrançois Tigeot {
142*a85cb24fSFrançois Tigeot 	return (s->sequence != start);
143*a85cb24fSFrançois Tigeot }
144*a85cb24fSFrançois Tigeot 
145*a85cb24fSFrançois Tigeot static inline int
read_seqcount_retry(const seqcount_t * s,unsigned start)146*a85cb24fSFrançois Tigeot read_seqcount_retry(const seqcount_t *s, unsigned start)
147dcbb2408SFrançois Tigeot {
148dcbb2408SFrançois Tigeot 	cpu_lfence();
149*a85cb24fSFrançois Tigeot 	return __read_seqcount_retry(s, start);
150dcbb2408SFrançois Tigeot }
151dcbb2408SFrançois Tigeot 
write_seqcount_begin(seqcount_t * s)152dcbb2408SFrançois Tigeot static inline void write_seqcount_begin(seqcount_t *s)
153dcbb2408SFrançois Tigeot {
154dcbb2408SFrançois Tigeot 	s->sequence++;
155dcbb2408SFrançois Tigeot 	cpu_ccfence();
156dcbb2408SFrançois Tigeot }
157dcbb2408SFrançois Tigeot 
write_seqcount_end(seqcount_t * s)158dcbb2408SFrançois Tigeot static inline void write_seqcount_end(seqcount_t *s)
159dcbb2408SFrançois Tigeot {
160dcbb2408SFrançois Tigeot 	cpu_ccfence();
161dcbb2408SFrançois Tigeot 	s->sequence++;
162dcbb2408SFrançois Tigeot }
163dcbb2408SFrançois Tigeot 
1642096a39aSFrançois Tigeot static inline unsigned int
raw_read_seqcount(const seqcount_t * s)1652096a39aSFrançois Tigeot raw_read_seqcount(const seqcount_t *s)
1662096a39aSFrançois Tigeot {
1672096a39aSFrançois Tigeot 	unsigned int value = READ_ONCE(s->sequence);
1682096a39aSFrançois Tigeot 
1692096a39aSFrançois Tigeot 	cpu_ccfence();
1702096a39aSFrançois Tigeot 
1712096a39aSFrançois Tigeot 	return value;
1722096a39aSFrançois Tigeot }
1732096a39aSFrançois Tigeot 
17439cfddd2SFrançois Tigeot #endif	/* _LINUX_SEQLOCK_H_ */
175