summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2013-09-25 14:20:57 +0800
committerPaolo Bonzini <pbonzini@redhat.com>2013-10-17 17:24:15 +0200
commitea753d81e8b085d679f13e4a6023e003e9854d51 (patch)
tree072bf3d6c7a3bd8726fe767cbf90470b7172ebce /include
parentc46860ea53854a96b11af0d6e23b623ce199e95e (diff)
downloadqemu-ea753d81e8b085d679f13e4a6023e003e9854d51.tar.gz
qemu-ea753d81e8b085d679f13e4a6023e003e9854d51.tar.bz2
qemu-ea753d81e8b085d679f13e4a6023e003e9854d51.zip
seqlock: introduce read-write seqlock
Seqlock implementation for QEMU. Usage idiom reader: do { start = seqlock_read_begin(&sl); ... } while (seqlock_read_retry(&sl, start)); writer: seqlock_write_lock(&sl); ... seqlock_write_unlock(&sl); initialization: seqlock_init(QemuSeqLock *sl, QemuMutex *mutex) mutex could be NULL if the caller will provide its own protection for concurrent write sides (typically using the BQL). Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'include')
-rw-r--r--include/qemu/seqlock.h72
1 files changed, 72 insertions, 0 deletions
diff --git a/include/qemu/seqlock.h b/include/qemu/seqlock.h
new file mode 100644
index 0000000000..3ff118a1a1
--- /dev/null
+++ b/include/qemu/seqlock.h
@@ -0,0 +1,72 @@
+/*
+ * 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