The documentation of the memory order argument of atomic operations
states that memory loads after an atomic load with a
memory_order_acquire cannot be moved before the atomic operation.
This still leaves open the possibility that non-atomic loads before
the atomic load could be moved after it, hence breaking down the
synchronization used for cmap_find().

This patch fixes this my using atomics (with appropriate memory_order)
also for the struct cmap_node pointer and hash values.

struct cmap_node itself is used for the pointer, since it already
contains an RCU pointer (which is atomic) for a struct cmap_node.
This also helps simplify some of the code previously dealing
separately with the cmap_node pointer in the bucket v.s. a cmap_node.

Hash values are also accessed using atomics.  Otherwise it might be
legal for a compiler to read the hash values once, and keep using the
same values through all the retries.

These should have no effect on performance.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/cmap.c |  127 +++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 75 insertions(+), 52 deletions(-)

diff --git a/lib/cmap.c b/lib/cmap.c
index 6c23a62..ae2b132 100644
--- a/lib/cmap.c
+++ b/lib/cmap.c
@@ -134,8 +134,8 @@ struct cmap_bucket {
      * The slots are in no particular order.  A null pointer indicates that a
      * pair is unused.  In-use slots are not necessarily in the earliest
      * slots. */
-    uint32_t hashes[CMAP_K];
-    struct cmap_node *nodes[CMAP_K];
+    atomic_uint32_t hashes[CMAP_K];
+    struct cmap_node nodes[CMAP_K];
 
     /* Padding to make cmap_bucket exactly one cache line long. */
 #if CMAP_PADDING > 0
@@ -163,6 +163,20 @@ struct cmap_impl {
 };
 BUILD_ASSERT_DECL(sizeof(struct cmap_impl) == CACHE_LINE_SIZE);
 
+#define cmap_get_hash__(HASH, ORDER)                                   \
+    ({                                                                 \
+        uint32_t hash__;                                               \
+                                                                       \
+        atomic_read_explicit(CONST_CAST(ATOMIC(uint32_t) *, (HASH)),   \
+                             &hash__, ORDER);                          \
+                                                                       \
+        hash__;                                                        \
+    })
+#define cmap_get_hash(HASH) \
+    cmap_get_hash__(HASH, memory_order_acquire)
+#define cmap_get_hash_protected(HASH) \
+    cmap_get_hash__(HASH, memory_order_relaxed)
+
 static struct cmap_impl *cmap_rehash(struct cmap *, uint32_t mask);
 
 /* Given a rehashed value 'hash', returns the other hash for that rehashed
@@ -295,8 +309,9 @@ retry:
     b1 = &impl->buckets[h1 & impl->mask];
     c1 = read_even_counter(b1);
     for (i = 0; i < CMAP_K; i++) {
-        struct cmap_node *node = b1->nodes[i];
-        if (node && b1->hashes[i] == hash) {
+        struct cmap_node *node = cmap_node_next(&b1->nodes[i]);
+
+        if (node && cmap_get_hash(&b1->hashes[i]) == hash) {
             if (counter_changed(b1, c1)) {
                 goto retry;
             }
@@ -307,8 +322,9 @@ retry:
     b2 = &impl->buckets[h2 & impl->mask];
     c2 = read_even_counter(b2);
     for (i = 0; i < CMAP_K; i++) {
-        struct cmap_node *node = b2->nodes[i];
-        if (node && b2->hashes[i] == hash) {
+        struct cmap_node *node = cmap_node_next(&b2->nodes[i]);
+
+        if (node && cmap_get_hash(&b2->hashes[i]) == hash) {
             if (counter_changed(b2, c2)) {
                 goto retry;
             }
@@ -323,13 +339,14 @@ retry:
 }
 
 static int
-cmap_find_slot(struct cmap_bucket *b, uint32_t hash)
+cmap_find_slot_protected(struct cmap_bucket *b, uint32_t hash)
 {
     int i;
 
     for (i = 0; i < CMAP_K; i++) {
-        struct cmap_node *node = b->nodes[i];
-        if (node && b->hashes[i] == hash) {
+        struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]);
+
+        if (node && cmap_get_hash_protected(&b->hashes[i]) == hash) {
             return i;
         }
     }
@@ -343,8 +360,9 @@ cmap_find_bucket_protected(struct cmap_impl *impl, uint32_t 
hash, uint32_t h)
     int i;
 
     for (i = 0; i < CMAP_K; i++) {
-        struct cmap_node *node = b->nodes[i];
-        if (node && b->hashes[i] == hash) {
+        struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]);
+
+        if (node && cmap_get_hash_protected(&b->hashes[i]) == hash) {
             return node;
         }
     }
@@ -370,12 +388,12 @@ cmap_find_protected(const struct cmap *cmap, uint32_t 
hash)
 }
 
 static int
-cmap_find_empty_slot(const struct cmap_bucket *b)
+cmap_find_empty_slot_protected(const struct cmap_bucket *b)
 {
     int i;
 
     for (i = 0; i < CMAP_K; i++) {
-        if (!b->nodes[i]) {
+        if (!cmap_node_next_protected(&b->nodes[i])) {
             return i;
         }
     }
@@ -390,8 +408,8 @@ cmap_set_bucket(struct cmap_bucket *b, int i,
 
     atomic_read_explicit(&b->counter, &c, memory_order_acquire);
     atomic_store_explicit(&b->counter, c + 1, memory_order_release);
-    b->nodes[i] = node;
-    b->hashes[i] = hash;
+    ovsrcu_set(&b->nodes[i].next, node); /* Also atomic. */
+    atomic_store_explicit(&b->hashes[i], hash, memory_order_release);
     atomic_store_explicit(&b->counter, c + 2, memory_order_release);
 }
 
@@ -405,24 +423,32 @@ cmap_insert_dup(struct cmap_node *new_node, uint32_t hash,
     int i;
 
     for (i = 0; i < CMAP_K; i++) {
-        struct cmap_node *node = b->nodes[i];
-        if (b->hashes[i] == hash) {
+        struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]);
+
+        if (cmap_get_hash_protected(&b->hashes[i]) == hash) {
             if (node) {
                 struct cmap_node *p;
 
-                /* The common case is that 'new_node' is a singleton, with a
-                 * null 'next' pointer, but rehashing can add a longer chain.
-                 * Find the end of the chain starting at 'new_node', then
-                 * splice 'node' to the end of that chain. */
+                /* The common case is that 'new_node' is a singleton,
+                 * with a null 'next' pointer.  Rehashing can add a
+                 * longer chain, but due to our invariant of always
+                 * having all nodes with the same (user) hash value at
+                 * a single chain, rehashing will always insert the
+                 * chain to an empty node.  The only way we can end up
+                 * here is by the user inserting a chain of nodes at
+                 * once.  Find the end of the chain starting at
+                 * 'new_node', then splice 'node' to the end of that
+                 * chain. */
                 p = new_node;
                 for (;;) {
                     struct cmap_node *next = cmap_node_next_protected(p);
+
                     if (!next) {
                         break;
                     }
                     p = next;
                 }
-                ovsrcu_set(&p->next, node);
+                ovsrcu_init(&p->next, node);
             } else {
                 /* The hash value is there from some previous insertion, but
                  * the associated node has been removed.  We're not really
@@ -434,8 +460,7 @@ cmap_insert_dup(struct cmap_node *new_node, uint32_t hash,
              * form of cmap_set_bucket() that doesn't update the counter since
              * we're only touching one field and in a way that doesn't change
              * the bucket's meaning for readers. */
-            b->nodes[i] = new_node;
-            atomic_thread_fence(memory_order_release);
+            ovsrcu_set(&b->nodes[i].next, new_node);
 
             return true;
         }
@@ -452,7 +477,7 @@ cmap_insert_bucket(struct cmap_node *node, uint32_t hash,
     int i;
 
     for (i = 0; i < CMAP_K; i++) {
-        if (!b->nodes[i]) {
+        if (!cmap_node_next_protected(&b->nodes[i])) {
             cmap_set_bucket(b, i, node, hash);
             return true;
         }
@@ -463,9 +488,9 @@ cmap_insert_bucket(struct cmap_node *node, uint32_t hash,
 /* Returns the other bucket that b->nodes[slot] could occupy in 'impl'.  (This
  * might be the same as 'b'.) */
 static struct cmap_bucket *
-other_bucket(struct cmap_impl *impl, struct cmap_bucket *b, int slot)
+other_bucket_protected(struct cmap_impl *impl, struct cmap_bucket *b, int slot)
 {
-    uint32_t h1 = rehash(impl, b->hashes[slot]);
+    uint32_t h1 = rehash(impl, cmap_get_hash_protected(&b->hashes[slot]));
     uint32_t h2 = other_hash(h1);
     uint32_t b_idx = b - impl->buckets;
     uint32_t other_h = (h1 & impl->mask) == b_idx ? h2 : h1;
@@ -502,7 +527,7 @@ cmap_insert_bfs(struct cmap_impl *impl, struct cmap_node 
*new_node,
      *
      *     b = path->start;
      *     for (i = 0; i < path->n; i++) {
-     *         b = other_bucket(impl, b, path->slots[i]);
+     *         b = other_bucket_protected(impl, b, path->slots[i]);
      *     }
      *     ovs_assert(b == path->end);
      */
@@ -542,14 +567,14 @@ cmap_insert_bfs(struct cmap_impl *impl, struct cmap_node 
*new_node,
         int i;
 
         for (i = 0; i < CMAP_K; i++) {
-            struct cmap_bucket *next = other_bucket(impl, this, i);
+            struct cmap_bucket *next = other_bucket_protected(impl, this, i);
             int j;
 
             if (this == next) {
                 continue;
             }
 
-            j = cmap_find_empty_slot(next);
+            j = cmap_find_empty_slot_protected(next);
             if (j >= 0) {
                 /* We've found a path along which we can rearrange the hash
                  * table:  Start at path->start, follow all the slots in
@@ -569,7 +594,7 @@ cmap_insert_bfs(struct cmap_impl *impl, struct cmap_node 
*new_node,
                 /* Figure out the full sequence of buckets. */
                 buckets[0] = path->start;
                 for (k = 0; k <= path->n; k++) {
-                    buckets[k + 1] = other_bucket(impl, buckets[k], slots[k]);
+                    buckets[k + 1] = other_bucket_protected(impl, buckets[k], 
slots[k]);
                 }
 
                 /* Now the path is fully expressed.  One can start from
@@ -583,8 +608,8 @@ cmap_insert_bfs(struct cmap_impl *impl, struct cmap_node 
*new_node,
                     int slot = slots[k - 1];
 
                     cmap_set_bucket(buckets[k], slots[k],
-                                    buckets[k - 1]->nodes[slot],
-                                    buckets[k - 1]->hashes[slot]);
+                                    cmap_node_next_protected(&buckets[k - 
1]->nodes[slot]),
+                                    cmap_get_hash_protected(&buckets[k - 
1]->hashes[slot]));
                 }
 
                 /* Finally, replace the first node on the path by
@@ -635,7 +660,7 @@ cmap_insert(struct cmap *cmap, struct cmap_node *node, 
uint32_t hash)
 {
     struct cmap_impl *impl = cmap_get_impl(cmap);
 
-    ovsrcu_set(&node->next, NULL);
+    ovsrcu_init(&node->next, NULL);
 
     if (OVS_UNLIKELY(impl->n >= impl->max_n)) {
         impl = cmap_rehash(cmap, (impl->mask << 1) | 1);
@@ -654,7 +679,7 @@ cmap_replace__(struct cmap_impl *impl, struct cmap_node 
*node,
     struct cmap_bucket *b = &impl->buckets[h & impl->mask];
     int slot;
 
-    slot = cmap_find_slot(b, hash);
+    slot = cmap_find_slot_protected(b, hash);
     if (slot < 0) {
         return false;
     }
@@ -668,21 +693,15 @@ cmap_replace__(struct cmap_impl *impl, struct cmap_node 
*node,
         ovsrcu_init(&replacement->next, cmap_node_next_protected(node));
     }
 
-    if (b->nodes[slot] == node) {
-        b->nodes[slot] = replacement;
-        atomic_thread_fence(memory_order_release);
-    } else {
-        struct cmap_node *iter = b->nodes[slot];
-        for (;;) {
-            struct cmap_node *next = cmap_node_next_protected(iter);
-            if (next == node) {
-                break;
-            }
-            iter = next;
+    for (struct cmap_node *iter = &b->nodes[slot];;) {
+        struct cmap_node *next = cmap_node_next_protected(iter);
+
+        if (next == node) {
+            ovsrcu_set(&iter->next, replacement);
+            return true;
         }
-        ovsrcu_set(&iter->next, replacement);
+        iter = next;
     }
-    return true;
 }
 
 /* Removes 'node' from 'cmap'.  The caller must ensure that 'cmap' cannot
@@ -731,9 +750,11 @@ cmap_try_rehash(const struct cmap_impl *old, struct 
cmap_impl *new)
         for (i = 0; i < CMAP_K; i++) {
             /* possible optimization here because we know the hashes are
              * unique */
-            struct cmap_node *node = b->nodes[i];
+            struct cmap_node *node = cmap_node_next_protected(&b->nodes[i]);
 
-            if (node && !cmap_try_insert(new, node, b->hashes[i])) {
+            if (node &&
+                !cmap_try_insert(new, node,
+                                 cmap_get_hash_protected(&b->hashes[i]))) {
                 return false;
             }
         }
@@ -784,6 +805,7 @@ cmap_cursor_next(struct cmap_cursor *cursor, const struct 
cmap_node *node)
 
     if (node) {
         struct cmap_node *next = cmap_node_next(node);
+
         if (next) {
             return next;
         }
@@ -793,7 +815,8 @@ cmap_cursor_next(struct cmap_cursor *cursor, const struct 
cmap_node *node)
         const struct cmap_bucket *b = &impl->buckets[cursor->bucket_idx];
 
         while (cursor->entry_idx < CMAP_K) {
-            struct cmap_node *node = b->nodes[cursor->entry_idx++];
+            struct cmap_node *node = 
cmap_node_next(&b->nodes[cursor->entry_idx++]);
+
             if (node) {
                 return node;
             }
@@ -827,7 +850,7 @@ cmap_next_position(const struct cmap *cmap,
         const struct cmap_bucket *b = &impl->buckets[bucket];
 
         while (entry < CMAP_K) {
-            const struct cmap_node *node = b->nodes[entry];
+            const struct cmap_node *node = cmap_node_next(&b->nodes[entry]);
             unsigned int i;
 
             for (i = 0; node; i++) {
-- 
1.7.10.4

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

Reply via email to