#include "ovs-thread.h"
#include "random.h"
-/* This system's cache line size, in bytes.
- * Being wrong hurts performance but not correctness. */
-#define CACHE_LINE_SIZE 64 /* Correct for most CPUs. */
-BUILD_ASSERT_DECL(IS_POW2(CACHE_LINE_SIZE));
-
struct fat_rwlock_slot {
/* Membership in rwlock's list of "struct fat_rwlock_slot"s.
*
* 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.
* 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
}
list_remove(&slot->list_node);
- free(slot->base);
+ free_cacheline(slot);
}
static void
fat_rwlock_get_slot__(struct fat_rwlock *rwlock)
{
struct fat_rwlock_slot *slot;
- void *base;
/* Fast path. */
slot = ovsthread_getspecific(rwlock->key);
/* 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;
}
}
+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
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;