Add support for atomic compare_exchange operations.

Add ovs_refcount_try_ref(), which takes a reference only if the
refcount is non-zero and returns true if a reference was taken, false
otherwise.  This can be used in combined RCU/refcount scenarios where
we have an RCU protected reference to an refcounted object, but which
may be unref'ed at any time.  If ovs_refcount_try_ref() fails, the
object may still be safely used until the current thread quiesces.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/ovs-atomic-clang.h    |   14 ++++++++++++
 lib/ovs-atomic-gcc4+.h    |   23 +++++++++++++++++++
 lib/ovs-atomic-gcc4.7+.h  |   14 ++++++++++++
 lib/ovs-atomic-locked.h   |   11 +++++++++
 lib/ovs-atomic-pthreads.h |   10 +++++++++
 lib/ovs-atomic.h          |   54 ++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 125 insertions(+), 1 deletion(-)

diff --git a/lib/ovs-atomic-clang.h b/lib/ovs-atomic-clang.h
index c83afab..34cc2fa 100644
--- a/lib/ovs-atomic-clang.h
+++ b/lib/ovs-atomic-clang.h
@@ -53,6 +53,20 @@ typedef enum {
     (*(DST) = __c11_atomic_load(SRC, ORDER), \
      (void) 0)
 
+#define atomic_compare_exchange_strong(DST, EXP, SRC)                   \
+    atomic_compare_exchange_strong_explicit(DST, EXP, SRC,              \
+                                            memory_order_seq_cst,       \
+                                            memory_order_seq_cst)
+#define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    __c11_atomic_compare_exchange_strong(DST, EXP, SRC, ORD1, ORD2)
+
+#define atomic_compare_exchange_weak(DST, EXP, SRC)              \
+    atomic_compare_exchange_weak_explicit(DST, EXP, SRC,         \
+                                          memory_order_seq_cst,  \
+                                          memory_order_seq_cst)
+#define atomic_compare_exchange_weak_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    __c11_atomic_compare_exchange_weak(DST, EXP, SRC, ORD1, ORD2)
+
 #define atomic_add(RMW, ARG, ORIG) \
     atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
 #define atomic_sub(RMW, ARG, ORIG) \
diff --git a/lib/ovs-atomic-gcc4+.h b/lib/ovs-atomic-gcc4+.h
index 2a1b278..e3dd68e 100644
--- a/lib/ovs-atomic-gcc4+.h
+++ b/lib/ovs-atomic-gcc4+.h
@@ -108,6 +108,29 @@ atomic_signal_fence(memory_order order OVS_UNUSED)
         (void) 0;                                       \
     })
 
+#define atomic_compare_exchange_strong(DST, EXP, SRC)   \
+    ({                                                  \
+        typeof(DST) dst__ = (DST);                      \
+        typeof(EXP) expp__ = (EXP);                     \
+        typeof(SRC) src__ = (SRC);                      \
+        typeof(SRC) exp__ = *expp__;                    \
+        typeof(SRC) ret__;                              \
+                                                        \
+        ret__ = __sync_val_compare_and_swap(dst__, exp__, src__); \
+        if (ret__ != exp__) {                                     \
+            *expp__ = ret__;                                      \
+        }                                                         \
+        ret__ == exp__;                                           \
+    })
+#define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    ((void) (ORD1), (void) (ORD2), \
+     atomic_compare_exchange_strong(DST, EXP, SRC))
+#define atomic_compare_exchange_weak            \
+    atomic_compare_exchange_strong
+#define atomic_compare_exchange_weak_explicit   \
+    atomic_compare_exchange_strong_explicit
+
+
 #define atomic_op__(RMW, OP, ARG, ORIG)                     \
     ({                                                      \
         typeof(RMW) rmw__ = (RMW);                          \
diff --git a/lib/ovs-atomic-gcc4.7+.h b/lib/ovs-atomic-gcc4.7+.h
index f465e51..4c197eb 100644
--- a/lib/ovs-atomic-gcc4.7+.h
+++ b/lib/ovs-atomic-gcc4.7+.h
@@ -47,6 +47,20 @@ typedef enum {
     (*(DST) = __atomic_load_n(SRC, ORDER),      \
      (void) 0)
 
+#define atomic_compare_exchange_strong(DST, EXP, SRC)              \
+    atomic_compare_exchange_strong_explicit(DST, EXP, SRC,         \
+                                            memory_order_seq_cst,  \
+                                            memory_order_seq_cst)
+#define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    __atomic_compare_exchange_n(DST, EXP, SRC, false, ORD1, ORD2)
+
+#define atomic_compare_exchange_weak(DST, EXP, SRC)              \
+    atomic_compare_exchange_weak_explicit(DST, EXP, SRC,         \
+                                          memory_order_seq_cst,  \
+                                          memory_order_seq_cst)
+#define atomic_compare_exchange_weak_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    __atomic_compare_exchange_n(DST, EXP, SRC, true, ORD1, ORD2)
+
 #define atomic_add(RMW, OPERAND, ORIG) \
         atomic_add_explicit(RMW, OPERAND, ORIG, memory_order_seq_cst)
 #define atomic_sub(RMW, OPERAND, ORIG) \
diff --git a/lib/ovs-atomic-locked.h b/lib/ovs-atomic-locked.h
index 438e78c..f8f0ba2 100644
--- a/lib/ovs-atomic-locked.h
+++ b/lib/ovs-atomic-locked.h
@@ -20,6 +20,17 @@ void atomic_unlock__(void *);
      atomic_unlock__(SRC),                      \
      (void) 0)
 
+/* XXX: Evaluates EXP multiple times. */
+#define atomic_compare_exchange_locked(DST, EXP, SRC)   \
+    (atomic_lock__(DST),                                \
+     (*(DST) == *(EXP)                                  \
+      ? (*(DST) = (SRC),                                \
+         atomic_unlock__(DST),                          \
+         true)                                          \
+      : (*(EXP) = *(DST),                               \
+         atomic_unlock__(DST),                          \
+         false)))
+
 #define atomic_op_locked_add +=
 #define atomic_op_locked_sub -=
 #define atomic_op_locked_or  |=
diff --git a/lib/ovs-atomic-pthreads.h b/lib/ovs-atomic-pthreads.h
index 33270c6..12234e7 100644
--- a/lib/ovs-atomic-pthreads.h
+++ b/lib/ovs-atomic-pthreads.h
@@ -67,6 +67,16 @@ atomic_signal_fence(memory_order order OVS_UNUSED)
 #define atomic_read_explicit(SRC, DST, ORDER)   \
     ((void) (ORDER), atomic_read(SRC, DST))
 
+#define atomic_compare_exchange_strong(DST, EXP, SRC)   \
+    atomic_compare_exchange_locked(DST, EXP, SRC)
+#define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \
+    ((void) (ORD1), (void) (ORD2),                                      \
+     atomic_compare_exchange_strong(DST, EXP, SRC))
+#define atomic_compare_exchange_weak            \
+    atomic_compare_exchange_strong
+#define atomic_compare_exchange_weak_explicit   \
+    atomic_compare_exchange_strong_explicit
+
 #define atomic_add(RMW, ARG, ORIG) atomic_op_locked(RMW, add, ARG, ORIG)
 #define atomic_sub(RMW, ARG, ORIG) atomic_op_locked(RMW, sub, ARG, ORIG)
 #define atomic_or( RMW, ARG, ORIG) atomic_op_locked(RMW, or, ARG, ORIG)
diff --git a/lib/ovs-atomic.h b/lib/ovs-atomic.h
index 1a721ca..bc974c5 100644
--- a/lib/ovs-atomic.h
+++ b/lib/ovs-atomic.h
@@ -161,7 +161,7 @@
  * In this section, A is an atomic type and C is the corresponding non-atomic
  * type.
  *
- * The "store" primitives match C11:
+ * The "store" and "compare_exchange" primitives match C11:
  *
  *     void atomic_store(A *object, C value);
  *     void atomic_store_explicit(A *object, C value, memory_order);
@@ -169,6 +169,32 @@
  *         Atomically stores 'value' into '*object', respecting the given
  *         memory order (or memory_order_seq_cst for atomic_store()).
  *
+ *     bool atomic_compare_exchange_strong(A *object, C *expected, C desired);
+ *     bool atomic_compare_exchange_weak(A *object, C *expected, C desired);
+ *     bool atomic_compare_exchange_strong_explicit(A *object, C *expected,
+ *                                                  C desired,
+ *                                                  memory_order success,
+ *                                                  memory_order failure);
+ *     bool atomic_compare_exchange_weak_explicit(A *object, C *expected,
+ *                                                  C desired,
+ *                                                  memory_order success,
+ *                                                  memory_order failure);
+ *
+ *         Atomically loads '*object' and compares it with '*expected' and if
+ *         equal, stores 'desired' into '*object' (an atomic read-modify-write
+ *         operation) and returns true, and if non-equal, stores the actual
+ *         value of '*object' into '*expected' (an atomic load operation) and
+ *         returns false.  The memory order for the successful case (atomic
+ *         read-modify-write operation) is 'success', and for the unsuccessful
+ *         case (atomic load operation) 'failure'.  'failure' may not be
+ *         stronger than 'success'.
+ *
+ *         The weak forms may fail (returning false) also when '*object' equals
+ *         '*expected'.  The strong form can be implemented by the weak form in
+ *         a loop.  Some platforms can implement the weak form more
+ *         efficiently, so it should be used if the application will need to
+ *         loop anyway.
+ *
  * The following primitives differ from the C11 ones (and have different names)
  * because there does not appear to be a way to implement the standard
  * primitives in standard C:
@@ -358,4 +384,30 @@ ovs_refcount_read(const struct ovs_refcount *refcount_)
     return count;
 }
 
+/* Increments 'refcount', but only if it is non-zero.  Returns false if
+ * the refcount was zero.  It is critical that we never increment a
+ * zero refcount to a non-zero value, as whenever a refcount reaches
+ * the zero value, the protected object may be irrevocably scheduled
+ * for deletion.  If we just used the atomic_add and compared the
+ * returned old value against zero with the intention of undoing the
+ * increment in that case, we could have concurrent threads taking the
+ * reference and never seeing the zero value.  This would eventually
+ * lead to use after free and double free of the protected object. */
+static inline bool
+ovs_refcount_try_ref(struct ovs_refcount *refcount)
+{
+    unsigned int count;
+
+    atomic_read_explicit(&refcount->count, &count, memory_order_relaxed);
+    do {
+        if (count == 0) {
+            return false;
+        }
+    } while (!atomic_compare_exchange_weak_explicit(&refcount->count, &count,
+                                                    count + 1,
+                                                    memory_order_relaxed,
+                                                    memory_order_relaxed));
+    return true;
+}
+
 #endif /* ovs-atomic.h */
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to