sys/dev/drm/include/linux/seqlock.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | /* * Copyright (c) 2017-2020 François Tigeot <ftigeot@wolfpond.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _LINUX_SEQLOCK_H_ #define _LINUX_SEQLOCK_H_ #include <linux/spinlock.h> #include <linux/preempt.h> #include <linux/lockdep.h> #include <linux/compiler.h> #include <asm/processor.h> /* * Seqlock definition from Wikipedia * * A seqlock consists of storage for saving a sequence number in addition to a lock. * The lock is to support synchronization between two writers and the counter is for * indicating consistency in readers. In addition to updating the shared data, the * writer increments the sequence number, both after acquiring the lock and before * releasing the lock. Readers read the sequence number before and after reading the * shared data. If the sequence number is odd on either occasion, a writer had taken * the lock while the data was being read and it may have changed. If the sequence * numbers are different, a writer has changed the data while it was being read. In * either case readers simply retry (using a loop) until they read the same even * sequence number before and after. * */ typedef struct { unsigned sequence; struct spinlock lock; } seqlock_t; static inline void seqlock_init(seqlock_t *sl) { sl->sequence = 0; spin_init(&sl->lock, "lsql"); } /* * Writers always use a spinlock. We still use store barriers * in order to quickly update the state of the sequence variable * for readers. */ static inline void write_seqlock(seqlock_t *sl) { spin_lock(&sl->lock); sl->sequence++; cpu_sfence(); } static inline void write_sequnlock(seqlock_t *sl) { sl->sequence--; spin_unlock(&sl->lock); cpu_sfence(); } /* * Read functions are fully unlocked. * We use load barriers to obtain a reasonably up-to-date state * for the sequence number. */ static inline unsigned read_seqbegin(const seqlock_t *sl) { return READ_ONCE(sl->sequence); } static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start) { cpu_lfence(); return (sl->sequence != start); } typedef struct seqcount { unsigned sequence; } seqcount_t; static inline void __seqcount_init(seqcount_t *s, const char *name, struct lock_class_key *key) { s->sequence = 0; } static inline unsigned int __read_seqcount_begin(const seqcount_t *s) { unsigned int ret; do { ret = READ_ONCE(s->sequence); /* If the sequence number is odd, a writer has taken the lock */ if ((ret & 1) == 0) break; cpu_pause(); } while (1); return ret; } static inline unsigned int read_seqcount_begin(const seqcount_t *s) { unsigned int ret = __read_seqcount_begin(s); cpu_lfence(); return ret; } static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start) { return (s->sequence != start); } static inline int read_seqcount_retry(const seqcount_t *s, unsigned start) { cpu_lfence(); return __read_seqcount_retry(s, start); } static inline void write_seqcount_begin(seqcount_t *s) { s->sequence++; cpu_ccfence(); } static inline void write_seqcount_end(seqcount_t *s) { cpu_ccfence(); s->sequence++; } static inline unsigned int raw_read_seqcount(const seqcount_t *s) { unsigned int value = READ_ONCE(s->sequence); cpu_ccfence(); return value; } static inline void write_seqlock_irqsave(seqlock_t *sl, unsigned long flags) { local_irq_save(flags); spin_lock(&sl->lock); sl->sequence++; } static inline void write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) { sl->sequence--; spin_unlock(&sl->lock); local_irq_restore(flags); } #endif /* _LINUX_SEQLOCK_H_ */ |