de47d7
From 28e3a979dbe3aa1d08ae32056b772a6a59fae581 Mon Sep 17 00:00:00 2001
26b369
From: Thierry Bordaz <tbordaz@redhat.com>
26b369
Date: Tue, 5 May 2020 17:44:01 +0200
de47d7
Subject: [PATCH 1/2] Ticket 51068 - deadlock when updating the schema
26b369
26b369
Bug Description:
26b369
        It exists a 3 threads deadlock scenario. It involves state change plugins when it
26b369
        calls schema_changed_callback. So the trigger is a change of schema (direct or via
26b369
        replication). The scenario is
26b369
      MOD(cn=schema)    hold StateChange lock   wait for vattr lock
26b369
      SRCH              hold vattr lock         wait for DB page
26b369
      MOD               hold DB page            wait for StateChange lock
26b369
26b369
Fix Description:
26b369
        Statechange lock protects the list of registered callbacks.
26b369
        lock is a mutex where actually registration of callback is only done
26b369
        at startup. Later the list is only lookup.
26b369
        Making statechange lock a rwlock suppresses the deadlock scenario
26b369
        as MODs will only acquire in read StateChange lock.
26b369
        It should also improve performance as at the moment all MODs are serialized
26b369
        on that lock
26b369
	In order to prevent writer starvation a new slapi_new_rwlock_prio
26b369
        create rwlock with priority to writers.
26b369
26b369
https://pagure.io/389-ds-base/issue/51068
26b369
26b369
Reviewed by: Mark Reynolds, William Brown
26b369
26b369
Platforms tested: 30
26b369
26b369
Flag Day: no
26b369
26b369
Doc impact: no
26b369
---
26b369
 .../servers/plugins/statechange/statechange.c | 24 ++++++++++--------
26b369
 ldap/servers/slapd/slapi-plugin.h             | 16 ++++++++++++
26b369
 ldap/servers/slapd/slapi2nspr.c               | 25 +++++++++++++++++++
26b369
 ldap/servers/slapd/vattr.c                    |  2 +-
26b369
 4 files changed, 55 insertions(+), 12 deletions(-)
26b369
26b369
diff --git a/ldap/servers/plugins/statechange/statechange.c b/ldap/servers/plugins/statechange/statechange.c
26b369
index f89b394c6..0a3838b5e 100644
26b369
--- a/ldap/servers/plugins/statechange/statechange.c
26b369
+++ b/ldap/servers/plugins/statechange/statechange.c
26b369
@@ -40,7 +40,7 @@ static SCNotify *head; /* a place to start in the list */
26b369
 #define SCN_PLUGIN_SUBSYSTEM "statechange-plugin" /* used for logging */
26b369
 
26b369
 static void *api[5];
26b369
-static Slapi_Mutex *buffer_lock = 0;
26b369
+static Slapi_RWLock *buffer_lock = 0;
26b369
 static PRUint64 g_plugin_started = 0;
26b369
 
26b369
 /*
26b369
@@ -140,7 +140,7 @@ statechange_start(Slapi_PBlock *pb __attribute__((unused)))
26b369
     api[3] = (void *)_statechange_unregister_all;
26b369
     api[4] = (void *)_statechange_vattr_cache_invalidator_callback;
26b369
 
26b369
-    if (0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
26b369
+    if (0 == (buffer_lock = slapi_new_rwlock()))
26b369
     {
26b369
         /* badness */
26b369
         slapi_log_err(SLAPI_LOG_ERR, SCN_PLUGIN_SUBSYSTEM, "statechange_start - Failed to create lock\n");
26b369
@@ -180,7 +180,9 @@ statechange_close(Slapi_PBlock *pb __attribute__((unused)))
26b369
     slapi_counter_destroy(&op_counter);
26b369
 
26b369
     slapi_apib_unregister(StateChange_v1_0_GUID);
26b369
-    slapi_destroy_mutex(buffer_lock);
26b369
+    if (buffer_lock) {
26b369
+        slapi_destroy_rwlock(buffer_lock);
26b369
+    }
26b369
     buffer_lock = NULL;
26b369
 
26b369
     slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_close\n");
26b369
@@ -240,7 +242,7 @@ statechange_post_op(Slapi_PBlock *pb, int modtype)
26b369
     slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_post_op\n");
26b369
 
26b369
     /* evaluate this operation against the notification entries */
26b369
-    slapi_lock_mutex(buffer_lock);
26b369
+    slapi_rwlock_rdlock(buffer_lock);
26b369
     if (head) {
26b369
         slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn;;
26b369
         if (NULL == sdn) {
26b369
@@ -290,7 +292,7 @@ statechange_post_op(Slapi_PBlock *pb, int modtype)
26b369
         } while (notify && notify != head);
26b369
     }
26b369
 bail:
26b369
-    slapi_unlock_mutex(buffer_lock);
26b369
+    slapi_rwlock_unlock(buffer_lock);
26b369
     slapi_log_err(SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_post_op\n");
26b369
 
26b369
     return SLAPI_PLUGIN_SUCCESS; /* always succeed */
26b369
@@ -338,7 +340,7 @@ _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data
26b369
         }
26b369
         item->func = func;
26b369
 
26b369
-        slapi_lock_mutex(buffer_lock);
26b369
+        slapi_rwlock_wrlock(buffer_lock);
26b369
         if (head == NULL) {
26b369
             head = item;
26b369
             head->next = head;
26b369
@@ -349,7 +351,7 @@ _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data
26b369
             head->prev = item;
26b369
             item->prev->next = item;
26b369
         }
26b369
-        slapi_unlock_mutex(buffer_lock);
26b369
+        slapi_rwlock_unlock(buffer_lock);
26b369
         slapi_ch_free_string(&writable_filter);
26b369
 
26b369
         ret = SLAPI_PLUGIN_SUCCESS;
26b369
@@ -371,7 +373,7 @@ _statechange_unregister(char *dn, char *filter, notify_callback thefunc)
26b369
         return ret;
26b369
     }
26b369
 
26b369
-    slapi_lock_mutex(buffer_lock);
26b369
+    slapi_rwlock_wrlock(buffer_lock);
26b369
 
26b369
     if ((func = statechange_find_notify(dn, filter, thefunc))) {
26b369
         func->prev->next = func->next;
26b369
@@ -392,7 +394,7 @@ _statechange_unregister(char *dn, char *filter, notify_callback thefunc)
26b369
         slapi_ch_free((void **)&func);
26b369
     }
26b369
 
26b369
-    slapi_unlock_mutex(buffer_lock);
26b369
+    slapi_rwlock_unlock(buffer_lock);
26b369
     slapi_counter_decrement(op_counter);
26b369
 
26b369
     return ret;
26b369
@@ -410,7 +412,7 @@ _statechange_unregister_all(char *caller_id, caller_data_free_callback callback)
26b369
         return;
26b369
     }
26b369
 
26b369
-    slapi_lock_mutex(buffer_lock);
26b369
+    slapi_rwlock_wrlock(buffer_lock);
26b369
 
26b369
     if (notify) {
26b369
         do {
26b369
@@ -441,7 +443,7 @@ _statechange_unregister_all(char *caller_id, caller_data_free_callback callback)
26b369
         } while (notify != start_notify && notify != NULL);
26b369
     }
26b369
 
26b369
-    slapi_unlock_mutex(buffer_lock);
26b369
+    slapi_rwlock_unlock(buffer_lock);
26b369
     slapi_counter_decrement(op_counter);
26b369
 }
26b369
 
26b369
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
26b369
index 679bdbb5c..865d83b9b 100644
26b369
--- a/ldap/servers/slapd/slapi-plugin.h
26b369
+++ b/ldap/servers/slapd/slapi-plugin.h
26b369
@@ -6126,6 +6126,22 @@ void slapi_destroy_condvar(Slapi_CondVar *cvar);
26b369
 int slapi_wait_condvar(Slapi_CondVar *cvar, struct timeval *timeout);
26b369
 int slapi_notify_condvar(Slapi_CondVar *cvar, int notify_all);
26b369
 
26b369
+/**
26b369
+ * Creates a new read/write lock
26b369
+ * If prio_writer the rwlock gives priority on writers
26b369
+ * else it give priority on readers (default)
26b369
+ *
26b369
+ * \return A pointer to a \c Slapi_RWLock
26b369
+ *
26b369
+ * \note Free the returned lock by calling slapi_destroy_rwlock() when finished
26b369
+ *
26b369
+ * \see slapi_destroy_rwlock()
26b369
+ * \see slapi_rwlock_rdlock()
26b369
+ * \see slapi_rwlock_wrlock()
26b369
+ * \see slapi_rwlock_unlock()
26b369
+ */
26b369
+Slapi_RWLock *slapi_new_rwlock_prio(int32_t prio_writer);
26b369
+
26b369
 /**
26b369
  * Creates a new read/write lock.
26b369
  *
26b369
diff --git a/ldap/servers/slapd/slapi2nspr.c b/ldap/servers/slapd/slapi2nspr.c
26b369
index b3e6d94c2..232d1599e 100644
26b369
--- a/ldap/servers/slapd/slapi2nspr.c
26b369
+++ b/ldap/servers/slapd/slapi2nspr.c
26b369
@@ -181,6 +181,31 @@ slapi_notify_condvar(Slapi_CondVar *cvar, int notify_all)
26b369
     return (prrc == PR_SUCCESS ? 1 : 0);
26b369
 }
26b369
 
26b369
+Slapi_RWLock *
26b369
+slapi_new_rwlock_prio(int32_t prio_writer)
26b369
+{
26b369
+#ifdef USE_POSIX_RWLOCKS
26b369
+    pthread_rwlock_t *rwlock = NULL;
26b369
+    pthread_rwlockattr_t attr;
26b369
+
26b369
+    pthread_rwlockattr_init(&attr);
26b369
+    if (prio_writer) {
26b369
+        pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
26b369
+    } else {
26b369
+        pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_READER_NP);
26b369
+    }
26b369
+
26b369
+    rwlock = (pthread_rwlock_t *)slapi_ch_malloc(sizeof(pthread_rwlock_t));
26b369
+    if (rwlock) {
26b369
+        pthread_rwlock_init(rwlock, &attr);
26b369
+    }
26b369
+
26b369
+    return ((Slapi_RWLock *)rwlock);
26b369
+#else
26b369
+    return ((Slapi_RWLock *)PR_NewRWLock(PR_RWLOCK_RANK_NONE, "slapi_rwlock"));
26b369
+#endif
26b369
+}
26b369
+
26b369
 Slapi_RWLock *
26b369
 slapi_new_rwlock(void)
26b369
 {
26b369
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
26b369
index 852a887ce..eef444270 100644
26b369
--- a/ldap/servers/slapd/vattr.c
26b369
+++ b/ldap/servers/slapd/vattr.c
26b369
@@ -1996,7 +1996,7 @@ vattr_map_create(void)
26b369
         return ENOMEM;
26b369
     }
26b369
 
26b369
-    the_map->lock = slapi_new_rwlock();
26b369
+    the_map->lock = slapi_new_rwlock_prio(1 /* priority on writers */);
26b369
     if (NULL == the_map) {
26b369
         slapd_nasty(sourcefile, 3, 0);
26b369
         return ENOMEM;
26b369
-- 
de47d7
2.25.4
26b369