[ovs-dev] [PATCH v3 1/3] fat-rwlock: Make fat-rwlock upgradable.

Ilya Maximets i.maximets at samsung.com
Mon Jul 11 15:15:29 UTC 2016


New functions 'fat_rwlock_{up,down}grade()' introduced to allow
upgrading read-lock to write-lock and downgrading it back.

Signed-off-by: Ilya Maximets <i.maximets at samsung.com>
---
 lib/fat-rwlock.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 lib/fat-rwlock.h | 11 +++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/lib/fat-rwlock.c b/lib/fat-rwlock.c
index 2f42b05..86a4693 100644
--- a/lib/fat-rwlock.c
+++ b/lib/fat-rwlock.c
@@ -53,10 +53,13 @@ struct fat_rwlock_slot {
      *
      *     - UINT_MAX: This thread has the write-lock on 'rwlock' and holds
      *       'mutex' (plus the 'mutex' of all of 'rwlock''s other slots).
+     *       'upgrade_depth' means the depth of read-lock on which it was
+     *       upgraded to write-lock.
      *
      * Accessed only by the slot's own thread, so no synchronization is
      * needed. */
     unsigned int depth;
+    unsigned int upgrade_depth;
 };
 
 static void
@@ -127,6 +130,7 @@ fat_rwlock_get_slot__(struct fat_rwlock *rwlock)
     slot->rwlock = rwlock;
     ovs_mutex_init(&slot->mutex);
     slot->depth = 0;
+    slot->upgrade_depth = 0;
 
     ovs_mutex_lock(&rwlock->mutex);
     ovs_list_push_back(&rwlock->threads, &slot->list_node);
@@ -236,6 +240,7 @@ fat_rwlock_wrlock(const struct fat_rwlock *rwlock_)
 
     ovs_assert(!this->depth);
     this->depth = UINT_MAX;
+    this->upgrade_depth = 1;
 
     ovs_mutex_lock(&rwlock->mutex);
     LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
@@ -257,11 +262,13 @@ fat_rwlock_unlock(const struct fat_rwlock *rwlock_)
 
     switch (this->depth) {
     case UINT_MAX:
+        this->depth = this->upgrade_depth - 1;
         LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
-            ovs_mutex_unlock(&slot->mutex);
+            if (slot != this || this->depth == 0) {
+                ovs_mutex_unlock(&slot->mutex);
+            }
         }
         ovs_mutex_unlock(&rwlock->mutex);
-        this->depth = 0;
         break;
 
     case 0:
@@ -275,3 +282,48 @@ fat_rwlock_unlock(const struct fat_rwlock *rwlock_)
         break;
     }
 }
+
+/* Upgrades last taken read-lock to write-lock.
+ * Not thread-safe with 'fat_rwlock_wrlock' and concurrent upgrades. */
+void
+fat_rwlock_upgrade(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 *slot;
+
+    ovs_assert(this->depth && this->depth != UINT_MAX);
+
+    this->upgrade_depth = this->depth;
+    this->depth = UINT_MAX;
+
+    ovs_mutex_lock(&rwlock->mutex);
+    LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
+        if (slot != this) {
+            ovs_mutex_lock(&slot->mutex);
+        }
+    }
+}
+
+/* Downgrades write-lock to read-lock. */
+void
+fat_rwlock_downgrade(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 *slot;
+
+    ovs_assert(this->depth == UINT_MAX);
+
+    this->depth = this->upgrade_depth;
+    this->upgrade_depth = 0;
+
+    LIST_FOR_EACH (slot, list_node, &rwlock->threads) {
+        if (slot != this) {
+            ovs_mutex_unlock(&slot->mutex);
+        }
+    }
+    ovs_mutex_unlock(&rwlock->mutex);
+}
diff --git a/lib/fat-rwlock.h b/lib/fat-rwlock.h
index 181fa92..70d5e95 100644
--- a/lib/fat-rwlock.h
+++ b/lib/fat-rwlock.h
@@ -46,4 +46,15 @@ int fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock)
 void fat_rwlock_wrlock(const struct fat_rwlock *rwlock) OVS_ACQ_WRLOCK(rwlock);
 void fat_rwlock_unlock(const struct fat_rwlock *rwlock) OVS_RELEASES(rwlock);
 
+/*
+ * Following functions used to upgrade last taken read-lock to write-lock and
+ * downgrade it back to read-lock. Upgrading/downgrading doesn't change depth
+ * of recursive locking.
+ *
+ * Upgrading is NOT thread-safe operation, so, the caller must be sure that
+ * it is the only thread that wants to acquire write-lock.
+ */
+void fat_rwlock_upgrade(const struct fat_rwlock *rwlock);
+void fat_rwlock_downgrade(const struct fat_rwlock *rwlock);
+
 #endif /* fat-rwlock.h */
-- 
2.7.4




More information about the dev mailing list