1 /* 2 * Seqlock implementation for QEMU 3 * 4 * Copyright Red Hat, Inc. 2013 5 * 6 * Author: 7 * Paolo Bonzini <pbonzini@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 * 12 */ 13 14 #ifndef QEMU_SEQLOCK_H 15 #define QEMU_SEQLOCK_H 16 17 #include "qemu/atomic.h" 18 #include "qemu/thread.h" 19 #include "qemu/lockable.h" 20 21 typedef struct QemuSeqLock QemuSeqLock; 22 23 struct QemuSeqLock { 24 unsigned sequence; 25 }; 26 27 static inline void seqlock_init(QemuSeqLock *sl) 28 { 29 sl->sequence = 0; 30 } 31 32 /* Lock out other writers and update the count. */ 33 static inline void seqlock_write_begin(QemuSeqLock *sl) 34 { 35 atomic_set(&sl->sequence, sl->sequence + 1); 36 37 /* Write sequence before updating other fields. */ 38 smp_wmb(); 39 } 40 41 static inline void seqlock_write_end(QemuSeqLock *sl) 42 { 43 /* Write other fields before finalizing sequence. */ 44 smp_wmb(); 45 46 atomic_set(&sl->sequence, sl->sequence + 1); 47 } 48 49 /* Lock out other writers and update the count. */ 50 static inline void seqlock_write_lock_impl(QemuSeqLock *sl, QemuLockable *lock) 51 { 52 qemu_lockable_lock(lock); 53 seqlock_write_begin(sl); 54 } 55 #define seqlock_write_lock(sl, lock) \ 56 seqlock_write_lock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) 57 58 /* Update the count and release the lock. */ 59 static inline void seqlock_write_unlock_impl(QemuSeqLock *sl, QemuLockable *lock) 60 { 61 seqlock_write_end(sl); 62 qemu_lockable_unlock(lock); 63 } 64 #define seqlock_write_unlock(sl, lock) \ 65 seqlock_write_unlock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) 66 67 68 static inline unsigned seqlock_read_begin(const QemuSeqLock *sl) 69 { 70 /* Always fail if a write is in progress. */ 71 unsigned ret = atomic_read(&sl->sequence); 72 73 /* Read sequence before reading other fields. */ 74 smp_rmb(); 75 return ret & ~1; 76 } 77 78 static inline int seqlock_read_retry(const QemuSeqLock *sl, unsigned start) 79 { 80 /* Read other fields before reading final sequence. */ 81 smp_rmb(); 82 return unlikely(atomic_read(&sl->sequence) != start); 83 } 84 85 #endif 86