| /* |
| * Seqlock implementation for QEMU |
| * |
| * Copyright Red Hat, Inc. 2013 |
| * |
| * Author: |
| * Paolo Bonzini <pbonzini@redhat.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| * |
| */ |
| #ifndef QEMU_SEQLOCK_H |
| #define QEMU_SEQLOCK_H 1 |
| |
| #include <qemu/atomic.h> |
| #include <qemu/thread.h> |
| |
| typedef struct QemuSeqLock QemuSeqLock; |
| |
| struct QemuSeqLock { |
| QemuMutex *mutex; |
| unsigned sequence; |
| }; |
| |
| static inline void seqlock_init(QemuSeqLock *sl, QemuMutex *mutex) |
| { |
| sl->mutex = mutex; |
| sl->sequence = 0; |
| } |
| |
| /* Lock out other writers and update the count. */ |
| static inline void seqlock_write_lock(QemuSeqLock *sl) |
| { |
| if (sl->mutex) { |
| qemu_mutex_lock(sl->mutex); |
| } |
| ++sl->sequence; |
| |
| /* Write sequence before updating other fields. */ |
| smp_wmb(); |
| } |
| |
| static inline void seqlock_write_unlock(QemuSeqLock *sl) |
| { |
| /* Write other fields before finalizing sequence. */ |
| smp_wmb(); |
| |
| ++sl->sequence; |
| if (sl->mutex) { |
| qemu_mutex_unlock(sl->mutex); |
| } |
| } |
| |
| static inline unsigned seqlock_read_begin(QemuSeqLock *sl) |
| { |
| /* Always fail if a write is in progress. */ |
| unsigned ret = sl->sequence & ~1; |
| |
| /* Read sequence before reading other fields. */ |
| smp_rmb(); |
| return ret; |
| } |
| |
| static int seqlock_read_retry(const QemuSeqLock *sl, unsigned start) |
| { |
| /* Read other fields before reading final sequence. */ |
| smp_rmb(); |
| return unlikely(sl->sequence != start); |
| } |
| |
| #endif |