Blame SOURCES/0007-Ticket-48235-Remove-memberOf-global-lock.patch

081b2d
From cbe71d7e4901232eaa423b9dc55dba9401c05bec Mon Sep 17 00:00:00 2001
081b2d
From: Mark Reynolds <mreynolds@redhat.com>
081b2d
Date: Fri, 13 Oct 2017 07:09:08 -0400
081b2d
Subject: [PATCH] Ticket 48235 - Remove memberOf global lock
081b2d
081b2d
Bug Description:  The memberOf global lock no longer servers a purpose since
081b2d
                  the plugin is BETXN.  This was causing potential deadlocks
081b2d
                  when multiple backends are used.
081b2d
081b2d
Fix Description:  Remove the lock, and rework the fixup/ancestors caches/hashtables.
081b2d
                  Instead of reusing a single cache, we create a fresh cache
081b2d
                  when we copy the plugin config (which only happens at the start
081b2d
                  of an operation).  Then we destroy the caches when we free
081b2d
                  the config.
081b2d
081b2d
https://pagure.io/389-ds-base/issue/48235
081b2d
081b2d
Reviewed by: firstyear & tbordaz(Thanks!!)
081b2d
081b2d
(cherry picked from commit 184b8a164f4ed456c72d58038aa9a0d512be61fa)
081b2d
---
081b2d
 ldap/servers/plugins/memberof/memberof.c        | 326 +++---------------------
081b2d
 ldap/servers/plugins/memberof/memberof.h        |  17 ++
081b2d
 ldap/servers/plugins/memberof/memberof_config.c | 166 +++++++++++-
081b2d
 3 files changed, 210 insertions(+), 299 deletions(-)
081b2d
081b2d
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
081b2d
index a0f997ddf..a23c52abe 100644
081b2d
--- a/ldap/servers/plugins/memberof/memberof.c
081b2d
+++ b/ldap/servers/plugins/memberof/memberof.c
081b2d
@@ -48,14 +48,11 @@ static Slapi_PluginDesc pdesc = {"memberof", VENDOR,
081b2d
 static void *_PluginID = NULL;
081b2d
 static Slapi_DN *_ConfigAreaDN = NULL;
081b2d
 static Slapi_RWLock *config_rwlock = NULL;
081b2d
-static Slapi_DN *_pluginDN = NULL;
081b2d
-static PRMonitor *memberof_operation_lock = 0;
081b2d
+static Slapi_DN* _pluginDN = NULL;
081b2d
 MemberOfConfig *qsortConfig = 0;
081b2d
 static int usetxn = 0;
081b2d
 static int premodfn = 0;
081b2d
-#define MEMBEROF_HASHTABLE_SIZE 1000
081b2d
-static PLHashTable *fixup_entry_hashtable = NULL;     /* global hash table protected by memberof_lock (memberof_operation_lock) */
081b2d
-static PLHashTable *group_ancestors_hashtable = NULL; /* global hash table protected by memberof_lock (memberof_operation_lock) */
081b2d
+
081b2d
 
081b2d
 typedef struct _memberofstringll
081b2d
 {
081b2d
@@ -73,18 +70,6 @@ typedef struct _memberof_get_groups_data
081b2d
     PRBool use_cache;
081b2d
 } memberof_get_groups_data;
081b2d
 
081b2d
-/* The key to access the hash table is the normalized DN
081b2d
- * The normalized DN is stored in the value because:
081b2d
- *  - It is used in slapi_valueset_find
081b2d
- *  - It is used to fill the memberof_get_groups_data.group_norm_vals
081b2d
- */
081b2d
-typedef struct _memberof_cached_value
081b2d
-{
081b2d
-    char *key;
081b2d
-    char *group_dn_val;
081b2d
-    char *group_ndn_val;
081b2d
-    int valid;
081b2d
-} memberof_cached_value;
081b2d
 struct cache_stat
081b2d
 {
081b2d
     int total_lookup;
081b2d
@@ -164,14 +149,9 @@ static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
081b2d
 static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);
081b2d
 static int memberof_add_objectclass(char *auto_add_oc, const char *dn);
081b2d
 static int memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc);
081b2d
-static PLHashTable *hashtable_new();
081b2d
-static void fixup_hashtable_empty(char *msg);
081b2d
-static PLHashTable *hashtable_new();
081b2d
-static void ancestor_hashtable_empty(char *msg);
081b2d
-static void ancestor_hashtable_entry_free(memberof_cached_value *entry);
081b2d
-static memberof_cached_value *ancestors_cache_lookup(const char *ndn);
081b2d
-static PRBool ancestors_cache_remove(const char *ndn);
081b2d
-static PLHashEntry *ancestors_cache_add(const void *key, void *value);
081b2d
+static memberof_cached_value *ancestors_cache_lookup(MemberOfConfig *config, const char *ndn);
081b2d
+static PRBool ancestors_cache_remove(MemberOfConfig *config, const char *ndn);
081b2d
+static PLHashEntry *ancestors_cache_add(MemberOfConfig *config, const void *key, void *value);
081b2d
 
081b2d
 /*** implementation ***/
081b2d
 
081b2d
@@ -344,11 +324,6 @@ memberof_postop_start(Slapi_PBlock *pb)
081b2d
     slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
081b2d
                   "--> memberof_postop_start\n");
081b2d
 
081b2d
-    memberof_operation_lock = PR_NewMonitor();
081b2d
-    if (0 == memberof_operation_lock) {
081b2d
-        rc = -1;
081b2d
-        goto bail;
081b2d
-    }
081b2d
     if (config_rwlock == NULL) {
081b2d
         if ((config_rwlock = slapi_new_rwlock()) == NULL) {
081b2d
             rc = -1;
081b2d
@@ -356,9 +331,6 @@ memberof_postop_start(Slapi_PBlock *pb)
081b2d
         }
081b2d
     }
081b2d
 
081b2d
-    fixup_entry_hashtable = hashtable_new();
081b2d
-    group_ancestors_hashtable = hashtable_new();
081b2d
-
081b2d
     /* Set the alternate config area if one is defined. */
081b2d
     slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);
081b2d
     if (config_area) {
081b2d
@@ -413,13 +385,13 @@ memberof_postop_start(Slapi_PBlock *pb)
081b2d
         goto bail;
081b2d
     }
081b2d
 
081b2d
-/*
081b2d
+    /*
081b2d
      * TODO: start up operation actor thread
081b2d
      * need to get to a point where server failure
081b2d
-         * or shutdown doesn't hose our operations
081b2d
-         * so we should create a task entry that contains
081b2d
+     * or shutdown doesn't hose our operations
081b2d
+     * so we should create a task entry that contains
081b2d
      * all required information to complete the operation
081b2d
-         * then the tasks can be restarted safely if
081b2d
+     * then the tasks can be restarted safely if
081b2d
      * interrupted
081b2d
      */
081b2d
 
081b2d
@@ -451,18 +423,7 @@ memberof_postop_close(Slapi_PBlock *pb __attribute__((unused)))
081b2d
     slapi_sdn_free(&_pluginDN);
081b2d
     slapi_destroy_rwlock(config_rwlock);
081b2d
     config_rwlock = NULL;
081b2d
-    PR_DestroyMonitor(memberof_operation_lock);
081b2d
-    memberof_operation_lock = NULL;
081b2d
-
081b2d
-    if (fixup_entry_hashtable) {
081b2d
-        fixup_hashtable_empty("memberof_postop_close empty fixup_entry_hastable");
081b2d
-        PL_HashTableDestroy(fixup_entry_hashtable);
081b2d
-    }
081b2d
 
081b2d
-    if (group_ancestors_hashtable) {
081b2d
-        ancestor_hashtable_empty("memberof_postop_close empty group_ancestors_hashtable");
081b2d
-        PL_HashTableDestroy(group_ancestors_hashtable);
081b2d
-    }
081b2d
     slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
081b2d
                   "<-- memberof_postop_close\n");
081b2d
     return 0;
081b2d
@@ -524,7 +485,7 @@ memberof_postop_del(Slapi_PBlock *pb)
081b2d
 {
081b2d
     int ret = SLAPI_PLUGIN_SUCCESS;
081b2d
     MemberOfConfig *mainConfig = NULL;
081b2d
-    MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+    MemberOfConfig configCopy = {0};
081b2d
     Slapi_DN *sdn;
081b2d
     void *caller_id = NULL;
081b2d
 
081b2d
@@ -553,9 +514,6 @@ memberof_postop_del(Slapi_PBlock *pb)
081b2d
         memberof_copy_config(&configCopy, memberof_get_config());
081b2d
         memberof_unlock_config();
081b2d
 
081b2d
-        /* get the memberOf operation lock */
081b2d
-        memberof_lock();
081b2d
-
081b2d
         /* remove this DN from the
081b2d
          * membership lists of groups
081b2d
          */
081b2d
@@ -563,7 +521,6 @@ memberof_postop_del(Slapi_PBlock *pb)
081b2d
             slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
081b2d
                           "memberof_postop_del - Error deleting dn (%s) from group. Error (%d)\n",
081b2d
                           slapi_sdn_get_dn(sdn), ret);
081b2d
-            memberof_unlock();
081b2d
             goto bail;
081b2d
         }
081b2d
 
081b2d
@@ -583,7 +540,6 @@ memberof_postop_del(Slapi_PBlock *pb)
081b2d
                 }
081b2d
             }
081b2d
         }
081b2d
-        memberof_unlock();
081b2d
     bail:
081b2d
         memberof_free_config(&configCopy);
081b2d
     }
081b2d
@@ -776,7 +732,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn
081b2d
         memberof_cached_value *ht_grp = NULL;
081b2d
         const char *ndn = slapi_sdn_get_ndn(sdn);
081b2d
 
081b2d
-        ht_grp = ancestors_cache_lookup((const void *)ndn);
081b2d
+        ht_grp = ancestors_cache_lookup(config, (const void *)ndn);
081b2d
         if (ht_grp) {
081b2d
 #if MEMBEROF_CACHE_DEBUG
081b2d
             slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s already cached (%x)\n", ndn, ht_grp);
081b2d
@@ -918,7 +874,7 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
081b2d
 
081b2d
     if (memberof_oktodo(pb)) {
081b2d
         MemberOfConfig *mainConfig = 0;
081b2d
-        MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+        MemberOfConfig configCopy = {0};
081b2d
         struct slapi_entry *pre_e = NULL;
081b2d
         struct slapi_entry *post_e = NULL;
081b2d
         Slapi_DN *pre_sdn = 0;
081b2d
@@ -944,8 +900,6 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
081b2d
             goto bail;
081b2d
         }
081b2d
 
081b2d
-        memberof_lock();
081b2d
-
081b2d
         /*  update any downstream members */
081b2d
         if (pre_sdn && post_sdn && configCopy.group_filter &&
081b2d
             0 == slapi_filter_test_simple(post_e, configCopy.group_filter)) {
081b2d
@@ -1010,7 +964,6 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
081b2d
                 }
081b2d
             }
081b2d
         }
081b2d
-        memberof_unlock();
081b2d
     bail:
081b2d
         memberof_free_config(&configCopy);
081b2d
     }
081b2d
@@ -1166,7 +1119,7 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
     if (memberof_oktodo(pb)) {
081b2d
         int config_copied = 0;
081b2d
         MemberOfConfig *mainConfig = 0;
081b2d
-        MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+        MemberOfConfig configCopy = {0};
081b2d
 
081b2d
         /* get the mod set */
081b2d
         slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods;;
081b2d
@@ -1209,8 +1162,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
             if (interested) {
081b2d
                 int op = slapi_mod_get_operation(smod);
081b2d
 
081b2d
-                memberof_lock();
081b2d
-
081b2d
                 /* the modify op decides the function */
081b2d
                 switch (op & ~LDAP_MOD_BVALUES) {
081b2d
                 case LDAP_MOD_ADD: {
081b2d
@@ -1221,7 +1172,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
                                       "Error (%d)\n",
081b2d
                                       slapi_sdn_get_dn(sdn), ret);
081b2d
                         slapi_mod_done(next_mod);
081b2d
-                        memberof_unlock();
081b2d
                         goto bail;
081b2d
                     }
081b2d
                     break;
081b2d
@@ -1239,7 +1189,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
                                           "Error (%d)\n",
081b2d
                                           slapi_sdn_get_dn(sdn), ret);
081b2d
                             slapi_mod_done(next_mod);
081b2d
-                            memberof_unlock();
081b2d
                             goto bail;
081b2d
                         }
081b2d
                     } else {
081b2d
@@ -1250,7 +1199,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
                                           "Error (%d)\n",
081b2d
                                           slapi_sdn_get_dn(sdn), ret);
081b2d
                             slapi_mod_done(next_mod);
081b2d
-                            memberof_unlock();
081b2d
                             goto bail;
081b2d
                         }
081b2d
                     }
081b2d
@@ -1265,7 +1213,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
                                       "Error (%d)\n",
081b2d
                                       slapi_sdn_get_dn(sdn), ret);
081b2d
                         slapi_mod_done(next_mod);
081b2d
-                        memberof_unlock();
081b2d
                         goto bail;
081b2d
                     }
081b2d
                     break;
081b2d
@@ -1280,8 +1227,6 @@ memberof_postop_modify(Slapi_PBlock *pb)
081b2d
                     break;
081b2d
                 }
081b2d
                 }
081b2d
-
081b2d
-                memberof_unlock();
081b2d
             }
081b2d
 
081b2d
             slapi_mod_done(next_mod);
081b2d
@@ -1336,7 +1281,7 @@ memberof_postop_add(Slapi_PBlock *pb)
081b2d
 
081b2d
     if (memberof_oktodo(pb) && (sdn = memberof_getsdn(pb))) {
081b2d
         struct slapi_entry *e = NULL;
081b2d
-        MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+        MemberOfConfig configCopy = {0};
081b2d
         MemberOfConfig *mainConfig;
081b2d
         slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
081b2d
 
081b2d
@@ -1361,8 +1306,6 @@ memberof_postop_add(Slapi_PBlock *pb)
081b2d
             int i = 0;
081b2d
             Slapi_Attr *attr = 0;
081b2d
 
081b2d
-            memberof_lock();
081b2d
-
081b2d
             for (i = 0; configCopy.groupattrs && configCopy.groupattrs[i]; i++) {
081b2d
                 if (0 == slapi_entry_attr_find(e, configCopy.groupattrs[i], &attr)) {
081b2d
                     if ((ret = memberof_add_attr_list(pb, &configCopy, sdn, attr))) {
081b2d
@@ -1373,8 +1316,6 @@ memberof_postop_add(Slapi_PBlock *pb)
081b2d
                     }
081b2d
                 }
081b2d
             }
081b2d
-
081b2d
-            memberof_unlock();
081b2d
             memberof_free_config(&configCopy);
081b2d
         }
081b2d
     }
081b2d
@@ -2094,7 +2035,7 @@ dump_cache_entry(memberof_cached_value *double_check, const char *msg)
081b2d
  * the firsts elements of the array has 'valid=1' and the dn/ndn of group it belong to
081b2d
  */
081b2d
 static void
081b2d
-cache_ancestors(Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
081b2d
+cache_ancestors(MemberOfConfig *config, Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
081b2d
 {
081b2d
     Slapi_ValueSet *groupvals = *((memberof_get_groups_data *)groups)->groupvals;
081b2d
     Slapi_Value *sval;
081b2d
@@ -2191,14 +2132,14 @@ cache_ancestors(Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
081b2d
 #if MEMBEROF_CACHE_DEBUG
081b2d
     dump_cache_entry(cache_entry, key);
081b2d
 #endif
081b2d
-    if (ancestors_cache_add((const void *)key_copy, (void *)cache_entry) == NULL) {
081b2d
-        slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache_ancestors: Failed to cache ancestor of %s\n", key);
081b2d
+    if (ancestors_cache_add(config, (const void*) key_copy, (void *) cache_entry) == NULL) {
081b2d
+        slapi_log_err( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache_ancestors: Failed to cache ancestor of %s\n", key);
081b2d
         ancestor_hashtable_entry_free(cache_entry);
081b2d
-        slapi_ch_free((void **)&cache_entry);
081b2d
+        slapi_ch_free ((void**)&cache_entry);
081b2d
         return;
081b2d
     }
081b2d
 #if MEMBEROF_CACHE_DEBUG
081b2d
-    if (double_check = ancestors_cache_lookup((const void *)key)) {
081b2d
+    if (double_check = ancestors_cache_lookup(config, (const void*) key)) {
081b2d
         dump_cache_entry(double_check, "read back");
081b2d
     }
081b2d
 #endif
081b2d
@@ -2283,8 +2224,7 @@ memberof_get_groups_r(MemberOfConfig *config, Slapi_DN *member_sdn, memberof_get
081b2d
 
081b2d
     merge_ancestors(&member_ndn_val, &member_data, data);
081b2d
     if (!cached && member_data.use_cache)
081b2d
-        cache_ancestors(&member_ndn_val, &member_data);
081b2d
-
081b2d
+        cache_ancestors(config, &member_ndn_val, &member_data);
081b2d
 
081b2d
     slapi_value_free(&member_ndn_val);
081b2d
     slapi_valueset_free(groupvals);
081b2d
@@ -2825,49 +2765,10 @@ memberof_qsort_compare(const void *a, const void *b)
081b2d
                                     val1, val2);
081b2d
 }
081b2d
 
081b2d
-/* betxn: This locking mechanism is necessary to guarantee the memberof
081b2d
- * consistency */
081b2d
-void
081b2d
-memberof_lock()
081b2d
-{
081b2d
-    if (usetxn) {
081b2d
-        PR_EnterMonitor(memberof_operation_lock);
081b2d
-    }
081b2d
-    if (fixup_entry_hashtable) {
081b2d
-        fixup_hashtable_empty("memberof_lock");
081b2d
-    }
081b2d
-    if (group_ancestors_hashtable) {
081b2d
-        ancestor_hashtable_empty("memberof_lock empty group_ancestors_hashtable");
081b2d
-        memset(&cache_stat, 0, sizeof(cache_stat));
081b2d
-    }
081b2d
-}
081b2d
-
081b2d
-void
081b2d
-memberof_unlock()
081b2d
-{
081b2d
-    if (group_ancestors_hashtable) {
081b2d
-        ancestor_hashtable_empty("memberof_unlock empty group_ancestors_hashtable");
081b2d
-#if MEMBEROF_CACHE_DEBUG
081b2d
-        slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics: total lookup %d (success %d), add %d, remove %d, enum %d\n",
081b2d
-                      cache_stat.total_lookup, cache_stat.successfull_lookup,
081b2d
-                      cache_stat.total_add, cache_stat.total_remove, cache_stat.total_enumerate);
081b2d
-        slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics duration: lookup %ld, add %ld, remove %ld, enum %ld\n",
081b2d
-                      cache_stat.cumul_duration_lookup, cache_stat.cumul_duration_add,
081b2d
-                      cache_stat.cumul_duration_remove, cache_stat.cumul_duration_enumerate);
081b2d
-#endif
081b2d
-    }
081b2d
-    if (fixup_entry_hashtable) {
081b2d
-        fixup_hashtable_empty("memberof_lock");
081b2d
-    }
081b2d
-    if (usetxn) {
081b2d
-        PR_ExitMonitor(memberof_operation_lock);
081b2d
-    }
081b2d
-}
081b2d
-
081b2d
 void
081b2d
 memberof_fixup_task_thread(void *arg)
081b2d
 {
081b2d
-    MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+    MemberOfConfig configCopy = {0};
081b2d
     Slapi_Task *task = (Slapi_Task *)arg;
081b2d
     task_data *td = NULL;
081b2d
     int rc = 0;
081b2d
@@ -2933,9 +2834,6 @@ memberof_fixup_task_thread(void *arg)
081b2d
     /* do real work */
081b2d
     rc = memberof_fix_memberof(&configCopy, task, td);
081b2d
 
081b2d
-    /* release the memberOf operation lock */
081b2d
-    memberof_unlock();
081b2d
-
081b2d
 done:
081b2d
     if (usetxn && fixup_pb) {
081b2d
         if (rc) { /* failed */
081b2d
@@ -3100,7 +2998,7 @@ memberof_fix_memberof(MemberOfConfig *config, Slapi_Task *task, task_data *td)
081b2d
 }
081b2d
 
081b2d
 static memberof_cached_value *
081b2d
-ancestors_cache_lookup(const char *ndn)
081b2d
+ancestors_cache_lookup(MemberOfConfig *config, const char *ndn)
081b2d
 {
081b2d
     memberof_cached_value *e;
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
@@ -3118,7 +3016,7 @@ ancestors_cache_lookup(const char *ndn)
081b2d
     }
081b2d
 #endif
081b2d
 
081b2d
-    e = (memberof_cached_value *)PL_HashTableLookupConst(group_ancestors_hashtable, (const void *)ndn);
081b2d
+    e = (memberof_cached_value *) PL_HashTableLookupConst(config->ancestors_cache, (const void *) ndn);
081b2d
 
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
     if (start) {
081b2d
@@ -3133,7 +3031,7 @@ ancestors_cache_lookup(const char *ndn)
081b2d
     return e;
081b2d
 }
081b2d
 static PRBool
081b2d
-ancestors_cache_remove(const char *ndn)
081b2d
+ancestors_cache_remove(MemberOfConfig *config, const char *ndn)
081b2d
 {
081b2d
     PRBool rc;
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
@@ -3151,7 +3049,8 @@ ancestors_cache_remove(const char *ndn)
081b2d
     }
081b2d
 #endif
081b2d
 
081b2d
-    rc = PL_HashTableRemove(group_ancestors_hashtable, (const void *)ndn);
081b2d
+
081b2d
+    rc = PL_HashTableRemove(config->ancestors_cache, (const void *)ndn);
081b2d
 
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
     if (start) {
081b2d
@@ -3164,7 +3063,7 @@ ancestors_cache_remove(const char *ndn)
081b2d
 }
081b2d
 
081b2d
 static PLHashEntry *
081b2d
-ancestors_cache_add(const void *key, void *value)
081b2d
+ancestors_cache_add(MemberOfConfig *config, const void *key, void *value)
081b2d
 {
081b2d
     PLHashEntry *e;
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
@@ -3181,7 +3080,7 @@ ancestors_cache_add(const void *key, void *value)
081b2d
     }
081b2d
 #endif
081b2d
 
081b2d
-    e = PL_HashTableAdd(group_ancestors_hashtable, key, value);
081b2d
+    e = PL_HashTableAdd(config->ancestors_cache, key, value);
081b2d
 
081b2d
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
     if (start) {
081b2d
@@ -3211,7 +3110,6 @@ memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
081b2d
     const char *ndn;
081b2d
     char *dn_copy;
081b2d
 
081b2d
-
081b2d
     /*
081b2d
      * If the server is ordered to shutdown, stop the fixup and return an error.
081b2d
      */
081b2d
@@ -3222,7 +3120,7 @@ memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
081b2d
 
081b2d
     /* Check if the entry has not already been fixed */
081b2d
     ndn = slapi_sdn_get_ndn(sdn);
081b2d
-    if (ndn && fixup_entry_hashtable && PL_HashTableLookupConst(fixup_entry_hashtable, (void *)ndn)) {
081b2d
+    if (ndn && config->fixup_cache && PL_HashTableLookupConst(config->fixup_cache, (void *)ndn)) {
081b2d
         slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: Entry %s already fixed up\n", ndn);
081b2d
         goto bail;
081b2d
     }
081b2d
@@ -3240,12 +3138,13 @@ memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
081b2d
              * so free this memory
081b2d
              */
081b2d
             ndn = slapi_sdn_get_ndn(sdn);
081b2d
+
081b2d
 #if MEMBEROF_CACHE_DEBUG
081b2d
             slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: This is NOT a group %s\n", ndn);
081b2d
 #endif
081b2d
-            ht_grp = ancestors_cache_lookup((const void *)ndn);
081b2d
+            ht_grp = ancestors_cache_lookup(config, (const void *)ndn);
081b2d
             if (ht_grp) {
081b2d
-                if (ancestors_cache_remove((const void *)ndn)) {
081b2d
+                if (ancestors_cache_remove(config, (const void *)ndn)) {
081b2d
                     slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: free cached values for %s\n", ndn);
081b2d
                     ancestor_hashtable_entry_free(ht_grp);
081b2d
                     slapi_ch_free((void **)&ht_grp);
081b2d
@@ -3297,11 +3196,11 @@ memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
081b2d
     slapi_valueset_free(groups);
081b2d
 
081b2d
     /* records that this entry has been fixed up */
081b2d
-    if (fixup_entry_hashtable) {
081b2d
+    if (config->fixup_cache) {
081b2d
         dn_copy = slapi_ch_strdup(ndn);
081b2d
-        if (PL_HashTableAdd(fixup_entry_hashtable, dn_copy, dn_copy) == NULL) {
081b2d
+        if (PL_HashTableAdd(config->fixup_cache, dn_copy, dn_copy) == NULL) {
081b2d
             slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: "
081b2d
-                                                                      "failed to add dn (%s) in the fixup hashtable; NSPR error - %d\n",
081b2d
+                          "failed to add dn (%s) in the fixup hashtable; NSPR error - %d\n",
081b2d
                           dn_copy, PR_GetError());
081b2d
             slapi_ch_free((void **)&dn_copy);
081b2d
             /* let consider this as not a fatal error, it just skip an optimization */
081b2d
@@ -3397,157 +3296,8 @@ memberof_add_objectclass(char *auto_add_oc, const char *dn)
081b2d
     return rc;
081b2d
 }
081b2d
 
081b2d
-static PRIntn
081b2d
-memberof_hash_compare_keys(const void *v1, const void *v2)
081b2d
-{
081b2d
-    PRIntn rc;
081b2d
-    if (0 == strcasecmp((const char *)v1, (const char *)v2)) {
081b2d
-        rc = 1;
081b2d
-    } else {
081b2d
-        rc = 0;
081b2d
-    }
081b2d
-    return rc;
081b2d
-}
081b2d
-
081b2d
-static PRIntn
081b2d
-memberof_hash_compare_values(const void *v1, const void *v2)
081b2d
-{
081b2d
-    PRIntn rc;
081b2d
-    if ((char *)v1 == (char *)v2) {
081b2d
-        rc = 1;
081b2d
-    } else {
081b2d
-        rc = 0;
081b2d
-    }
081b2d
-    return rc;
081b2d
-}
081b2d
-
081b2d
-/*
081b2d
- *  Hashing function using Bernstein's method
081b2d
- */
081b2d
-static PLHashNumber
081b2d
-memberof_hash_fn(const void *key)
081b2d
-{
081b2d
-    PLHashNumber hash = 5381;
081b2d
-    unsigned char *x = (unsigned char *)key;
081b2d
-    int c;
081b2d
-
081b2d
-    while ((c = *x++)) {
081b2d
-        hash = ((hash << 5) + hash) ^ c;
081b2d
-    }
081b2d
-    return hash;
081b2d
-}
081b2d
-
081b2d
-/* allocates the plugin hashtable
081b2d
- * This hash table is used by operation and is protected from
081b2d
- * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
081b2d
- * is not implemented and the hash table will be not used.
081b2d
- *
081b2d
- * The hash table contains all the DN of the entries for which the memberof
081b2d
- * attribute has been computed/updated during the current operation
081b2d
- *
081b2d
- * hash table should be empty at the beginning and end of the plugin callback
081b2d
- */
081b2d
-static PLHashTable *
081b2d
-hashtable_new()
081b2d
-{
081b2d
-    if (!usetxn) {
081b2d
-        return NULL;
081b2d
-    }
081b2d
-
081b2d
-    return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,
081b2d
-                           memberof_hash_fn,
081b2d
-                           memberof_hash_compare_keys,
081b2d
-                           memberof_hash_compare_values, NULL, NULL);
081b2d
-}
081b2d
-/* this function called for each hash node during hash destruction */
081b2d
-static PRIntn
081b2d
-fixup_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
081b2d
-{
081b2d
-    char *dn_copy;
081b2d
-
081b2d
-    if (he == NULL) {
081b2d
-        return HT_ENUMERATE_NEXT;
081b2d
-    }
081b2d
-    dn_copy = (char *)he->value;
081b2d
-    slapi_ch_free_string(&dn_copy);
081b2d
-
081b2d
-    return HT_ENUMERATE_REMOVE;
081b2d
-}
081b2d
-
081b2d
-static void
081b2d
-fixup_hashtable_empty(char *msg)
081b2d
-{
081b2d
-    if (fixup_entry_hashtable) {
081b2d
-        PL_HashTableEnumerateEntries(fixup_entry_hashtable, fixup_hashtable_remove, msg);
081b2d
-    }
081b2d
-}
081b2d
-
081b2d
-
081b2d
-/* allocates the plugin hashtable
081b2d
- * This hash table is used by operation and is protected from
081b2d
- * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
081b2d
- * is not implemented and the hash table will be not used.
081b2d
- *
081b2d
- * The hash table contains all the DN of the entries for which the memberof
081b2d
- * attribute has been computed/updated during the current operation
081b2d
- *
081b2d
- * hash table should be empty at the beginning and end of the plugin callback
081b2d
- */
081b2d
-
081b2d
-static void
081b2d
-ancestor_hashtable_entry_free(memberof_cached_value *entry)
081b2d
-{
081b2d
-    int i;
081b2d
-    for (i = 0; entry[i].valid; i++) {
081b2d
-        slapi_ch_free((void **)&entry[i].group_dn_val);
081b2d
-        slapi_ch_free((void **)&entry[i].group_ndn_val);
081b2d
-    }
081b2d
-    /* Here we are at the ending element containing the key */
081b2d
-    slapi_ch_free((void **)&entry[i].key);
081b2d
-}
081b2d
-/* this function called for each hash node during hash destruction */
081b2d
-static PRIntn
081b2d
-ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
081b2d
-{
081b2d
-    memberof_cached_value *group_ancestor_array;
081b2d
-
081b2d
-    if (he == NULL) {
081b2d
-        return HT_ENUMERATE_NEXT;
081b2d
-    }
081b2d
-
081b2d
-
081b2d
-    group_ancestor_array = (memberof_cached_value *)he->value;
081b2d
-    ancestor_hashtable_entry_free(group_ancestor_array);
081b2d
-    slapi_ch_free((void **)&group_ancestor_array);
081b2d
-
081b2d
-    return HT_ENUMERATE_REMOVE;
081b2d
-}
081b2d
-
081b2d
-static void
081b2d
-ancestor_hashtable_empty(char *msg)
081b2d
+int
081b2d
+memberof_use_txn()
081b2d
 {
081b2d
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
-    long int start;
081b2d
-    struct timespec tsnow;
081b2d
-#endif
081b2d
-
081b2d
-    if (group_ancestors_hashtable) {
081b2d
-        cache_stat.total_enumerate++;
081b2d
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
-        if (clock_gettime(CLOCK_REALTIME, &tsnow) != 0) {
081b2d
-            start = 0;
081b2d
-        } else {
081b2d
-            start = tsnow.tv_nsec;
081b2d
-        }
081b2d
-#endif
081b2d
-        PL_HashTableEnumerateEntries(group_ancestors_hashtable, ancestor_hashtable_remove, msg);
081b2d
-
081b2d
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
081b2d
-        if (start) {
081b2d
-            if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) {
081b2d
-                cache_stat.cumul_duration_enumerate += (tsnow.tv_nsec - start);
081b2d
-            }
081b2d
-        }
081b2d
-#endif
081b2d
-    }
081b2d
+    return usetxn;
081b2d
 }
081b2d
diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h
081b2d
index 4833ce221..ba64e9dfa 100644
081b2d
--- a/ldap/servers/plugins/memberof/memberof.h
081b2d
+++ b/ldap/servers/plugins/memberof/memberof.h
081b2d
@@ -64,8 +64,22 @@ typedef struct memberofconfig
081b2d
     int skip_nested;
081b2d
     int fixup_task;
081b2d
     char *auto_add_oc;
081b2d
+    PLHashTable *ancestors_cache;
081b2d
+    PLHashTable *fixup_cache;
081b2d
 } MemberOfConfig;
081b2d
 
081b2d
+/* The key to access the hash table is the normalized DN
081b2d
+ * The normalized DN is stored in the value because:
081b2d
+ *  - It is used in slapi_valueset_find
081b2d
+ *  - It is used to fill the memberof_get_groups_data.group_norm_vals
081b2d
+ */
081b2d
+typedef struct _memberof_cached_value
081b2d
+{
081b2d
+    char *key;
081b2d
+    char *group_dn_val;
081b2d
+    char *group_ndn_val;
081b2d
+    int valid;
081b2d
+} memberof_cached_value;
081b2d
 
081b2d
 /*
081b2d
  * functions
081b2d
@@ -89,5 +103,8 @@ int memberof_apply_config(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entr
081b2d
 void *memberof_get_plugin_id(void);
081b2d
 void memberof_release_config(void);
081b2d
 PRUint64 get_plugin_started(void);
081b2d
+void ancestor_hashtable_entry_free(memberof_cached_value *entry);
081b2d
+PLHashTable *hashtable_new();
081b2d
+int memberof_use_txn();
081b2d
 
081b2d
 #endif /* _MEMBEROF_H_ */
081b2d
diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c
081b2d
index c5ca4b137..3f22d95d6 100644
081b2d
--- a/ldap/servers/plugins/memberof/memberof_config.c
081b2d
+++ b/ldap/servers/plugins/memberof/memberof_config.c
081b2d
@@ -14,12 +14,12 @@
081b2d
  * memberof_config.c - configuration-related code for memberOf plug-in
081b2d
  *
081b2d
  */
081b2d
-
081b2d
+#include "plhash.h"
081b2d
 #include <plstr.h>
081b2d
-
081b2d
 #include "memberof.h"
081b2d
 
081b2d
 #define MEMBEROF_CONFIG_FILTER "(objectclass=*)"
081b2d
+#define MEMBEROF_HASHTABLE_SIZE 1000
081b2d
 
081b2d
 /*
081b2d
  * The configuration attributes are contained in the plugin entry e.g.
081b2d
@@ -34,14 +34,16 @@
081b2d
 /*
081b2d
  * function prototypes
081b2d
  */
081b2d
-static int memberof_validate_config(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e, int *returncode, char *returntext, void *arg);
081b2d
-static int
081b2d
-memberof_search(Slapi_PBlock *pb __attribute__((unused)),
081b2d
-                Slapi_Entry *entryBefore __attribute__((unused)),
081b2d
-                Slapi_Entry *e __attribute__((unused)),
081b2d
-                int *returncode __attribute__((unused)),
081b2d
-                char *returntext __attribute__((unused)),
081b2d
-                void *arg __attribute__((unused)))
081b2d
+static void fixup_hashtable_empty( MemberOfConfig *config, char *msg);
081b2d
+static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg);
081b2d
+static int memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
081b2d
+										 int *returncode, char *returntext, void *arg);
081b2d
+static int memberof_search (Slapi_PBlock *pb __attribute__((unused)),
081b2d
+                            Slapi_Entry* entryBefore __attribute__((unused)),
081b2d
+                            Slapi_Entry* e __attribute__((unused)),
081b2d
+                            int *returncode __attribute__((unused)),
081b2d
+                            char *returntext __attribute__((unused)),
081b2d
+                            void *arg __attribute__((unused)))
081b2d
 {
081b2d
     return SLAPI_DSE_CALLBACK_OK;
081b2d
 }
081b2d
@@ -52,7 +54,7 @@ memberof_search(Slapi_PBlock *pb __attribute__((unused)),
081b2d
 /* This is the main configuration which is updated from dse.ldif.  The
081b2d
  * config will be copied when it is used by the plug-in to prevent it
081b2d
  * being changed out from under a running memberOf operation. */
081b2d
-static MemberOfConfig theConfig = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
081b2d
+static MemberOfConfig theConfig = {0};
081b2d
 static Slapi_RWLock *memberof_config_lock = 0;
081b2d
 static int inited = 0;
081b2d
 
081b2d
@@ -693,6 +695,13 @@ void
081b2d
 memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
081b2d
 {
081b2d
     if (dest && src) {
081b2d
+
081b2d
+        /* Allocate our caches here since we only copy the config at the start of an op */
081b2d
+        if (memberof_use_txn() == 1){
081b2d
+            dest->ancestors_cache = hashtable_new();
081b2d
+            dest->fixup_cache = hashtable_new();
081b2d
+        }
081b2d
+
081b2d
         /* Check if the copy is already up to date */
081b2d
         if (src->groupattrs) {
081b2d
             int i = 0, j = 0;
081b2d
@@ -787,6 +796,14 @@ memberof_free_config(MemberOfConfig *config)
081b2d
         slapi_ch_free_string(&config->memberof_attr);
081b2d
         memberof_free_scope(&(config->entryScopes), &config->entryScopeCount);
081b2d
         memberof_free_scope(&(config->entryScopeExcludeSubtrees), &config->entryExcludeScopeCount);
081b2d
+        if (config->fixup_cache) {
081b2d
+            fixup_hashtable_empty(config, "memberof_free_config empty fixup_entry_hastable");
081b2d
+            PL_HashTableDestroy(config->fixup_cache);
081b2d
+        }
081b2d
+        if (config->ancestors_cache) {
081b2d
+            ancestor_hashtable_empty(config, "memberof_free_config empty group_ancestors_hashtable");
081b2d
+            PL_HashTableDestroy(config->ancestors_cache);
081b2d
+        }
081b2d
     }
081b2d
 }
081b2d
 
081b2d
@@ -982,3 +999,130 @@ bail:
081b2d
 
081b2d
     return ret;
081b2d
 }
081b2d
+
081b2d
+
081b2d
+static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2)
081b2d
+{
081b2d
+    PRIntn rc;
081b2d
+    if (0 == strcasecmp((const char *) v1, (const char *) v2)) {
081b2d
+        rc = 1;
081b2d
+    } else {
081b2d
+        rc = 0;
081b2d
+    }
081b2d
+    return rc;
081b2d
+}
081b2d
+
081b2d
+static PRIntn memberof_hash_compare_values(const void *v1, const void *v2)
081b2d
+{
081b2d
+    PRIntn rc;
081b2d
+    if ((char *) v1 == (char *) v2) {
081b2d
+        rc = 1;
081b2d
+    } else {
081b2d
+        rc = 0;
081b2d
+    }
081b2d
+    return rc;
081b2d
+}
081b2d
+
081b2d
+/*
081b2d
+ *  Hashing function using Bernstein's method
081b2d
+ */
081b2d
+static PLHashNumber memberof_hash_fn(const void *key)
081b2d
+{
081b2d
+    PLHashNumber hash = 5381;
081b2d
+    unsigned char *x = (unsigned char *)key;
081b2d
+    int c;
081b2d
+
081b2d
+    while ((c = *x++)){
081b2d
+        hash = ((hash << 5) + hash) ^ c;
081b2d
+    }
081b2d
+    return hash;
081b2d
+}
081b2d
+
081b2d
+/* allocates the plugin hashtable
081b2d
+ * This hash table is used by operation and is protected from
081b2d
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
081b2d
+ * is not implemented and the hash table will be not used.
081b2d
+ *
081b2d
+ * The hash table contains all the DN of the entries for which the memberof
081b2d
+ * attribute has been computed/updated during the current operation
081b2d
+ *
081b2d
+ * hash table should be empty at the beginning and end of the plugin callback
081b2d
+ */
081b2d
+PLHashTable *hashtable_new(int usetxn)
081b2d
+{
081b2d
+    if (!usetxn) {
081b2d
+        return NULL;
081b2d
+    }
081b2d
+
081b2d
+    return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,
081b2d
+        memberof_hash_fn,
081b2d
+        memberof_hash_compare_keys,
081b2d
+        memberof_hash_compare_values, NULL, NULL);
081b2d
+}
081b2d
+
081b2d
+/* this function called for each hash node during hash destruction */
081b2d
+static PRIntn fixup_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
081b2d
+{
081b2d
+	char *dn_copy;
081b2d
+
081b2d
+	if (he == NULL) {
081b2d
+		return HT_ENUMERATE_NEXT;
081b2d
+	}
081b2d
+	dn_copy = (char*) he->value;
081b2d
+	slapi_ch_free_string(&dn_copy);
081b2d
+
081b2d
+	return HT_ENUMERATE_REMOVE;
081b2d
+}
081b2d
+
081b2d
+static void fixup_hashtable_empty(MemberOfConfig *config, char *msg)
081b2d
+{
081b2d
+    if (config->fixup_cache) {
081b2d
+        PL_HashTableEnumerateEntries(config->fixup_cache, fixup_hashtable_remove, msg);
081b2d
+    }
081b2d
+}
081b2d
+
081b2d
+
081b2d
+/* allocates the plugin hashtable
081b2d
+ * This hash table is used by operation and is protected from
081b2d
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
081b2d
+ * is not implemented and the hash table will be not used.
081b2d
+ *
081b2d
+ * The hash table contains all the DN of the entries for which the memberof
081b2d
+ * attribute has been computed/updated during the current operation
081b2d
+ *
081b2d
+ * hash table should be empty at the beginning and end of the plugin callback
081b2d
+ */
081b2d
+
081b2d
+void ancestor_hashtable_entry_free(memberof_cached_value *entry)
081b2d
+{
081b2d
+    int i;
081b2d
+
081b2d
+    for (i = 0; entry[i].valid; i++) {
081b2d
+        slapi_ch_free((void **) &entry[i].group_dn_val);
081b2d
+        slapi_ch_free((void **) &entry[i].group_ndn_val);
081b2d
+    }
081b2d
+    /* Here we are at the ending element containing the key */
081b2d
+    slapi_ch_free((void**) &entry[i].key);
081b2d
+}
081b2d
+
081b2d
+/* this function called for each hash node during hash destruction */
081b2d
+static PRIntn ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
081b2d
+{
081b2d
+    memberof_cached_value *group_ancestor_array;
081b2d
+
081b2d
+    if (he == NULL) {
081b2d
+        return HT_ENUMERATE_NEXT;
081b2d
+    }
081b2d
+    group_ancestor_array = (memberof_cached_value *) he->value;
081b2d
+    ancestor_hashtable_entry_free(group_ancestor_array);
081b2d
+    slapi_ch_free((void **)&group_ancestor_array);
081b2d
+
081b2d
+    return HT_ENUMERATE_REMOVE;
081b2d
+}
081b2d
+
081b2d
+static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg)
081b2d
+{
081b2d
+    if (config->ancestors_cache) {
081b2d
+        PL_HashTableEnumerateEntries(config->ancestors_cache, ancestor_hashtable_remove, msg);
081b2d
+    }
081b2d
+}
081b2d
-- 
081b2d
2.13.6
081b2d