ofpbuf: Make offset calculation more consistent.
[cascardo/ovs.git] / lib / fat-rwlock.c
index 3866dda..89cff1d 100644 (file)
@@ -30,7 +30,7 @@ struct fat_rwlock_slot {
      *
      * fat_rwlock_destroy() sets 'rwlock' to NULL to indicate that this
      * slot may be destroyed. */
-    struct list list_node;      /* In struct rwlock's 'threads' list. */
+    struct ovs_list list_node;  /* In struct rwlock's 'threads' list. */
     struct fat_rwlock *rwlock;  /* Owner. */
 
     /* Mutex.
@@ -57,16 +57,6 @@ struct fat_rwlock_slot {
      * Accessed only by the slot's own thread, so no synchronization is
      * needed. */
     unsigned int depth;
-
-    /* To prevent two of these structures from accidentally occupying the same
-     * cache line (causing "false sharing"), we cache-align each of these data
-     * structures.  That requires malloc()ing extra space and throwing away
-     * some space at the beginning, which means that the pointer to this struct
-     * isn't necessarily the pointer to the beginning of the block, and so we
-     * need to retain the original pointer to free later.
-     *
-     * Accessed only by a single thread, so no synchronization is needed. */
-    void *base;                /* Pointer to pass to free() for this block.  */
 };
 
 static void
@@ -77,7 +67,7 @@ free_slot(struct fat_rwlock_slot *slot)
     }
 
     list_remove(&slot->list_node);
-    free(slot->base);
+    free_cacheline(slot);
 }
 
 static void
@@ -124,7 +114,6 @@ static struct fat_rwlock_slot *
 fat_rwlock_get_slot__(struct fat_rwlock *rwlock)
 {
     struct fat_rwlock_slot *slot;
-    void *base;
 
     /* Fast path. */
     slot = ovsthread_getspecific(rwlock->key);
@@ -134,21 +123,7 @@ fat_rwlock_get_slot__(struct fat_rwlock *rwlock)
 
     /* Slow path: create a new slot for 'rwlock' in this thread. */
 
-    /* Allocate room for:
-     *
-     *     - Up to CACHE_LINE_SIZE - 1 bytes before the per-thread, so that
-     *       the start of the slot doesn't potentially share a cache line.
-     *
-     *     - The slot itself.
-     *
-     *     - Space following the slot up to the end of the cache line, so
-     *       that the end of the slot doesn't potentially share a cache
-     *       line. */
-    base = xmalloc((CACHE_LINE_SIZE - 1)
-                   + ROUND_UP(sizeof *slot, CACHE_LINE_SIZE));
-    slot = (void *) ROUND_UP((uintptr_t) base, CACHE_LINE_SIZE);
-
-    slot->base = base;
+    slot = xmalloc_cacheline(sizeof *slot);
     slot->rwlock = rwlock;
     ovs_mutex_init(&slot->mutex);
     slot->depth = 0;
@@ -187,6 +162,33 @@ fat_rwlock_rdlock(const struct fat_rwlock *rwlock_)
     }
 }
 
+static struct fat_rwlock_slot *
+fat_rwlock_try_get_slot__(struct fat_rwlock *rwlock)
+{
+    struct fat_rwlock_slot *slot;
+
+    /* Fast path. */
+    slot = ovsthread_getspecific(rwlock->key);
+    if (slot) {
+        return slot;
+    }
+
+    /* Slow path: create a new slot for 'rwlock' in this thread. */
+
+    if (!ovs_mutex_trylock(&rwlock->mutex)) {
+        slot = xmalloc_cacheline(sizeof *slot);
+        slot->rwlock = rwlock;
+        ovs_mutex_init(&slot->mutex);
+        slot->depth = 0;
+
+        list_push_back(&rwlock->threads, &slot->list_node);
+        ovs_mutex_unlock(&rwlock->mutex);
+        ovsthread_setspecific(rwlock->key, slot);
+    }
+
+    return slot;
+}
+
 /* Tries to lock 'rwlock' for reading.  If successful, returns 0.  If taking
  * the lock would require blocking, returns EBUSY (without blocking). */
 int
@@ -195,9 +197,13 @@ fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock_)
     OVS_NO_THREAD_SAFETY_ANALYSIS
 {
     struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_);
-    struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock);
+    struct fat_rwlock_slot *this = fat_rwlock_try_get_slot__(rwlock);
     int error;
 
+    if (!this) {
+        return EBUSY;
+    }
+
     switch (this->depth) {
     case UINT_MAX:
         return EBUSY;