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

61f723
From 229f61f5f54aeb9e1a1756f731dfe7bcedbf148c Mon Sep 17 00:00:00 2001
61f723
From: Mark Reynolds <mreynolds@redhat.com>
61f723
Date: Fri, 13 Oct 2017 07:09:08 -0400
61f723
Subject: [PATCH 06/10] Ticket 48235 - Remove memberOf global lock
61f723
61f723
Bug Description:  The memberOf global lock no longer servers a purpose since
61f723
                  the plugin is BETXN.  This was causing potential deadlocks
61f723
                  when multiple backends are used.
61f723
61f723
Fix Description:  Remove the lock, and rework the fixup/ancestors caches/hashtables.
61f723
                  Instead of reusing a single cache, we create a fresh cache
61f723
                  when we copy the plugin config (which only happens at the start
61f723
                  of an operation).  Then we destroy the caches when we free
61f723
                  the config.
61f723
61f723
https://pagure.io/389-ds-base/issue/48235
61f723
61f723
Reviewed by: tbordaz & firstyear(Thanks!!)
61f723
---
61f723
 ldap/servers/plugins/memberof/memberof.c        | 312 +++---------------------
61f723
 ldap/servers/plugins/memberof/memberof.h        |  17 ++
61f723
 ldap/servers/plugins/memberof/memberof_config.c | 152 +++++++++++-
61f723
 3 files changed, 200 insertions(+), 281 deletions(-)
61f723
61f723
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
61f723
index 9bbe13c9c..bbf47dd49 100644
61f723
--- a/ldap/servers/plugins/memberof/memberof.c
61f723
+++ b/ldap/servers/plugins/memberof/memberof.c
61f723
@@ -49,13 +49,10 @@ static void* _PluginID = NULL;
61f723
 static Slapi_DN* _ConfigAreaDN = NULL;
61f723
 static Slapi_RWLock *config_rwlock = NULL;
61f723
 static Slapi_DN* _pluginDN = NULL;
61f723
-static PRMonitor *memberof_operation_lock = 0;
61f723
 MemberOfConfig *qsortConfig = 0;
61f723
 static int usetxn = 0;
61f723
 static int premodfn = 0;
61f723
-#define MEMBEROF_HASHTABLE_SIZE 1000
61f723
-static PLHashTable *fixup_entry_hashtable = NULL; /* global hash table protected by memberof_lock (memberof_operation_lock) */
61f723
-static PLHashTable *group_ancestors_hashtable = NULL; /* global hash table protected by memberof_lock (memberof_operation_lock) */
61f723
+
61f723
 
61f723
 typedef struct _memberofstringll
61f723
 {
61f723
@@ -73,18 +70,7 @@ typedef struct _memberof_get_groups_data
61f723
         PRBool use_cache;
61f723
 } memberof_get_groups_data;
61f723
 
61f723
-/* The key to access the hash table is the normalized DN
61f723
- * The normalized DN is stored in the value because:
61f723
- *  - It is used in slapi_valueset_find
61f723
- *  - It is used to fill the memberof_get_groups_data.group_norm_vals
61f723
- */
61f723
-typedef struct _memberof_cached_value
61f723
-{
61f723
-	char *key;
61f723
-	char *group_dn_val;
61f723
-        char *group_ndn_val;
61f723
-	int valid;
61f723
-} memberof_cached_value;
61f723
+
61f723
 struct cache_stat
61f723
 {
61f723
 	int total_lookup;
61f723
@@ -189,14 +175,9 @@ static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
61f723
 static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);
61f723
 static int memberof_add_objectclass(char *auto_add_oc, const char *dn);
61f723
 static int memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc);
61f723
-static PLHashTable *hashtable_new();
61f723
-static void fixup_hashtable_empty(char *msg);
61f723
-static PLHashTable *hashtable_new();
61f723
-static void ancestor_hashtable_empty(char *msg);
61f723
-static void ancestor_hashtable_entry_free(memberof_cached_value *entry);
61f723
-static memberof_cached_value *ancestors_cache_lookup(const char *ndn);
61f723
-static PRBool ancestors_cache_remove(const char *ndn);
61f723
-static PLHashEntry *ancestors_cache_add(const void *key, void *value);
61f723
+static memberof_cached_value *ancestors_cache_lookup(MemberOfConfig *config, const char *ndn);
61f723
+static PRBool ancestors_cache_remove(MemberOfConfig *config, const char *ndn);
61f723
+static PLHashEntry *ancestors_cache_add(MemberOfConfig *config, const void *key, void *value);
61f723
 
61f723
 /*** implementation ***/
61f723
 
61f723
@@ -375,12 +356,6 @@ int memberof_postop_start(Slapi_PBlock *pb)
61f723
 	slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
61f723
 		"--> memberof_postop_start\n" );
61f723
 
61f723
-	memberof_operation_lock = PR_NewMonitor();
61f723
-	if(0 == memberof_operation_lock)
61f723
-	{
61f723
-		rc = -1;
61f723
-		goto bail;
61f723
-	}
61f723
 	if(config_rwlock == NULL){
61f723
 		if((config_rwlock = slapi_new_rwlock()) == NULL){
61f723
 			rc = -1;
61f723
@@ -388,9 +363,6 @@ int memberof_postop_start(Slapi_PBlock *pb)
61f723
 		}
61f723
 	}
61f723
 
61f723
-	fixup_entry_hashtable = hashtable_new();
61f723
-	group_ancestors_hashtable = hashtable_new();
61f723
-
61f723
 	/* Set the alternate config area if one is defined. */
61f723
 	slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);
61f723
 	if (config_area)
61f723
@@ -482,18 +454,7 @@ int memberof_postop_close(Slapi_PBlock *pb)
61f723
 	slapi_sdn_free(&_pluginDN);
61f723
 	slapi_destroy_rwlock(config_rwlock);
61f723
 	config_rwlock = NULL;
61f723
-	PR_DestroyMonitor(memberof_operation_lock);
61f723
-	memberof_operation_lock = NULL;
61f723
-
61f723
-	if (fixup_entry_hashtable) {
61f723
-		fixup_hashtable_empty("memberof_postop_close empty fixup_entry_hastable");
61f723
-		PL_HashTableDestroy(fixup_entry_hashtable);
61f723
-	}
61f723
 
61f723
-	if (group_ancestors_hashtable) {
61f723
-		ancestor_hashtable_empty("memberof_postop_close empty group_ancestors_hashtable");
61f723
-		PL_HashTableDestroy(group_ancestors_hashtable);
61f723
-	}
61f723
 	slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,
61f723
 		     "<-- memberof_postop_close\n" );
61f723
 	return 0;
61f723
@@ -554,7 +515,7 @@ int memberof_postop_del(Slapi_PBlock *pb)
61f723
 {
61f723
 	int ret = SLAPI_PLUGIN_SUCCESS;
61f723
 	MemberOfConfig *mainConfig = NULL;
61f723
-	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+	MemberOfConfig configCopy = {0};
61f723
 	Slapi_DN *sdn;
61f723
 	void *caller_id = NULL;
61f723
 
61f723
@@ -583,9 +544,6 @@ int memberof_postop_del(Slapi_PBlock *pb)
61f723
 		}
61f723
 		memberof_copy_config(&configCopy, memberof_get_config());
61f723
 		memberof_unlock_config();
61f723
-
61f723
-		/* get the memberOf operation lock */
61f723
-		memberof_lock();
61f723
 		
61f723
 		/* remove this DN from the
61f723
 		 * membership lists of groups
61f723
@@ -594,7 +552,6 @@ int memberof_postop_del(Slapi_PBlock *pb)
61f723
 			slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
61f723
 			                "memberof_postop_del - Error deleting dn (%s) from group. Error (%d)\n",
61f723
 			                slapi_sdn_get_dn(sdn),ret);
61f723
-			memberof_unlock();
61f723
 			goto bail;
61f723
 		}
61f723
 
61f723
@@ -618,7 +575,6 @@ int memberof_postop_del(Slapi_PBlock *pb)
61f723
 				}
61f723
 			}
61f723
 		}
61f723
-		memberof_unlock();
61f723
 bail:
61f723
 		memberof_free_config(&configCopy);
61f723
 	}
61f723
@@ -813,7 +769,7 @@ memberof_call_foreach_dn(Slapi_PBlock *pb __attribute__((unused)), Slapi_DN *sdn
61f723
 		memberof_cached_value *ht_grp = NULL;
61f723
 		const char *ndn = slapi_sdn_get_ndn(sdn);
61f723
 		
61f723
-		ht_grp = ancestors_cache_lookup((const void *) ndn);
61f723
+		ht_grp = ancestors_cache_lookup(config, (const void *) ndn);
61f723
 		if (ht_grp) {
61f723
 #if MEMBEROF_CACHE_DEBUG
61f723
 			slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s already cached (%x)\n", ndn, ht_grp);
61f723
@@ -960,7 +916,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
61f723
 	if(memberof_oktodo(pb))
61f723
 	{
61f723
 		MemberOfConfig *mainConfig = 0;
61f723
-		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+		MemberOfConfig configCopy = {0};
61f723
 		struct slapi_entry *pre_e = NULL;
61f723
 		struct slapi_entry *post_e = NULL;
61f723
 		Slapi_DN *pre_sdn = 0;
61f723
@@ -988,8 +944,6 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
61f723
 			goto bail;
61f723
 		}
61f723
 
61f723
-		memberof_lock();
61f723
-
61f723
 		/*  update any downstream members */
61f723
 		if(pre_sdn && post_sdn && configCopy.group_filter &&
61f723
 		   0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
61f723
@@ -1060,7 +1014,6 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
61f723
 				}
61f723
 			}
61f723
 		}
61f723
-		memberof_unlock();
61f723
 bail:
61f723
 		memberof_free_config(&configCopy);
61f723
 	}
61f723
@@ -1220,7 +1173,7 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 	{
61f723
 		int config_copied = 0;
61f723
 		MemberOfConfig *mainConfig = 0;
61f723
-		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+		MemberOfConfig configCopy = {0};
61f723
 
61f723
 		/* get the mod set */
61f723
 		slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods;;
61f723
@@ -1267,8 +1220,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 			{
61f723
 				int op = slapi_mod_get_operation(smod);
61f723
 
61f723
-				memberof_lock();
61f723
-
61f723
 				/* the modify op decides the function */
61f723
 				switch(op & ~LDAP_MOD_BVALUES)
61f723
 				{
61f723
@@ -1280,7 +1231,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 								"memberof_postop_modify - Failed to add dn (%s) to target.  "
61f723
 								"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );
61f723
 							slapi_mod_done(next_mod);
61f723
-							memberof_unlock();
61f723
 							goto bail;
61f723
 						}
61f723
 						break;
61f723
@@ -1299,7 +1249,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 									"memberof_postop_modify - Failed to replace list (%s).  "
61f723
 									"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );
61f723
 								slapi_mod_done(next_mod);
61f723
-								memberof_unlock();
61f723
 								goto bail;
61f723
 							}
61f723
 						}
61f723
@@ -1311,7 +1260,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 									"memberof_postop_modify: failed to remove dn (%s).  "
61f723
 									"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );
61f723
 								slapi_mod_done(next_mod);
61f723
-								memberof_unlock();
61f723
 								goto bail;
61f723
 							}
61f723
 						}
61f723
@@ -1326,7 +1274,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 								"memberof_postop_modify - Failed to replace values in  dn (%s).  "
61f723
 								"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );
61f723
 							slapi_mod_done(next_mod);
61f723
-							memberof_unlock();
61f723
 							goto bail;
61f723
 						}
61f723
 						break;
61f723
@@ -1342,8 +1289,6 @@ int memberof_postop_modify(Slapi_PBlock *pb)
61f723
 						break;
61f723
 					}
61f723
 				}
61f723
-
61f723
-				memberof_unlock();
61f723
 			}
61f723
 
61f723
 			slapi_mod_done(next_mod);
61f723
@@ -1398,7 +1343,7 @@ int memberof_postop_add(Slapi_PBlock *pb)
61f723
 	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
61f723
 	{
61f723
 		struct slapi_entry *e = NULL;
61f723
-		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+		MemberOfConfig configCopy = {0};
61f723
 		MemberOfConfig *mainConfig;
61f723
 		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e );
61f723
 
61f723
@@ -1424,8 +1369,6 @@ int memberof_postop_add(Slapi_PBlock *pb)
61f723
 			int i = 0;
61f723
 			Slapi_Attr *attr = 0;
61f723
 
61f723
-			memberof_lock();
61f723
-
61f723
 			for (i = 0; configCopy.groupattrs && configCopy.groupattrs[i]; i++)
61f723
 			{
61f723
 				if(0 == slapi_entry_attr_find(e, configCopy.groupattrs[i], &attr))
61f723
@@ -1438,8 +1381,6 @@ int memberof_postop_add(Slapi_PBlock *pb)
61f723
 					}
61f723
 				}
61f723
 			}
61f723
-
61f723
-			memberof_unlock();
61f723
 			memberof_free_config(&configCopy);
61f723
 		}
61f723
 	}
61f723
@@ -2201,7 +2142,7 @@ dump_cache_entry(memberof_cached_value *double_check, const char *msg)
61f723
  * the firsts elements of the array has 'valid=1' and the dn/ndn of group it belong to
61f723
  */
61f723
 static void
61f723
-cache_ancestors(Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
61f723
+cache_ancestors(MemberOfConfig *config, Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
61f723
 {
61f723
 	Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)groups)->groupvals;
61f723
 	Slapi_Value *sval;
61f723
@@ -2298,14 +2239,14 @@ cache_ancestors(Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)
61f723
 #if MEMBEROF_CACHE_DEBUG
61f723
 	dump_cache_entry(cache_entry, key);
61f723
 #endif
61f723
-	if (ancestors_cache_add((const void*) key_copy, (void *) cache_entry) == NULL) {
61f723
+	if (ancestors_cache_add(config, (const void*) key_copy, (void *) cache_entry) == NULL) {
61f723
 		slapi_log_err( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache_ancestors: Failed to cache ancestor of %s\n", key);
61f723
 		ancestor_hashtable_entry_free(cache_entry);
61f723
 		slapi_ch_free ((void**)&cache_entry);
61f723
 		return;
61f723
 	}
61f723
 #if MEMBEROF_CACHE_DEBUG
61f723
-	if (double_check = ancestors_cache_lookup((const void*) key)) {
61f723
+	if (double_check = ancestors_cache_lookup(config, (const void*) key)) {
61f723
 		dump_cache_entry(double_check, "read back");
61f723
 	}
61f723
 #endif
61f723
@@ -2390,9 +2331,9 @@ memberof_get_groups_r(MemberOfConfig *config, Slapi_DN *member_sdn,
61f723
 		memberof_get_groups_callback, &member_data, &cached, member_data.use_cache);
61f723
 
61f723
 	merge_ancestors(&member_ndn_val, &member_data, data);
61f723
-	if (!cached && member_data.use_cache)
61f723
-		cache_ancestors(&member_ndn_val, &member_data);
61f723
-
61f723
+	if (!cached && member_data.use_cache) {
61f723
+		cache_ancestors(config, &member_ndn_val, &member_data);
61f723
+	}
61f723
 
61f723
 	slapi_value_free(&member_ndn_val);
61f723
 	slapi_valueset_free(groupvals);
61f723
@@ -2969,46 +2910,9 @@ int memberof_qsort_compare(const void *a, const void *b)
61f723
 	                                val1, val2);
61f723
 }
61f723
 
61f723
-/* betxn: This locking mechanism is necessary to guarantee the memberof
61f723
- * consistency */
61f723
-void memberof_lock()
61f723
-{
61f723
-	if (usetxn) {
61f723
-		PR_EnterMonitor(memberof_operation_lock);
61f723
-	}
61f723
-	if (fixup_entry_hashtable) {
61f723
-		fixup_hashtable_empty("memberof_lock");
61f723
-	}
61f723
-	if (group_ancestors_hashtable) {
61f723
-		ancestor_hashtable_empty("memberof_lock empty group_ancestors_hashtable");
61f723
-		memset(&cache_stat, 0, sizeof(cache_stat));
61f723
-	}
61f723
-}
61f723
-
61f723
-void memberof_unlock()
61f723
-{
61f723
-	if (group_ancestors_hashtable) {
61f723
-		ancestor_hashtable_empty("memberof_unlock empty group_ancestors_hashtable");
61f723
-#if MEMBEROF_CACHE_DEBUG
61f723
-		slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics: total lookup %d (success %d), add %d, remove %d, enum %d\n",
61f723
-			cache_stat.total_lookup, cache_stat.successfull_lookup,
61f723
-			cache_stat.total_add, cache_stat.total_remove, cache_stat.total_enumerate);
61f723
-		slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics duration: lookup %ld, add %ld, remove %ld, enum %ld\n",
61f723
-			cache_stat.cumul_duration_lookup, cache_stat.cumul_duration_add,
61f723
-			cache_stat.cumul_duration_remove, cache_stat.cumul_duration_enumerate);
61f723
-#endif
61f723
-	}
61f723
-	if (fixup_entry_hashtable) {
61f723
-		fixup_hashtable_empty("memberof_lock");
61f723
-	}
61f723
-	if (usetxn) {
61f723
-		PR_ExitMonitor(memberof_operation_lock);
61f723
-	}
61f723
-}
61f723
-
61f723
 void memberof_fixup_task_thread(void *arg)
61f723
 {
61f723
-	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+	MemberOfConfig configCopy = {0};
61f723
 	Slapi_Task *task = (Slapi_Task *)arg;
61f723
 	task_data *td = NULL;
61f723
 	int rc = 0;
61f723
@@ -3068,14 +2972,8 @@ void memberof_fixup_task_thread(void *arg)
61f723
 		}
61f723
 	}
61f723
 
61f723
-	/* get the memberOf operation lock */
61f723
-	memberof_lock();
61f723
-
61f723
 	/* do real work */
61f723
 	rc = memberof_fix_memberof(&configCopy, task, td);
61f723
- 
61f723
-	/* release the memberOf operation lock */
61f723
-	memberof_unlock();
61f723
 
61f723
 done:
61f723
 	if (usetxn && fixup_pb) {
61f723
@@ -3240,7 +3138,7 @@ int memberof_fix_memberof(MemberOfConfig *config, Slapi_Task *task, task_data *t
61f723
 }
61f723
 
61f723
 static memberof_cached_value *
61f723
-ancestors_cache_lookup(const char *ndn)
61f723
+ancestors_cache_lookup(MemberOfConfig *config, const char *ndn)
61f723
 {
61f723
 	memberof_cached_value *e;
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
@@ -3258,7 +3156,7 @@ ancestors_cache_lookup(const char *ndn)
61f723
 	}
61f723
 #endif
61f723
 
61f723
-	e = (memberof_cached_value *) PL_HashTableLookupConst(group_ancestors_hashtable, (const void *) ndn);
61f723
+	e = (memberof_cached_value *) PL_HashTableLookupConst(config->ancestors_cache, (const void *) ndn);
61f723
 
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
 	if (start) {
61f723
@@ -3274,7 +3172,7 @@ ancestors_cache_lookup(const char *ndn)
61f723
 	
61f723
 }
61f723
 static PRBool
61f723
-ancestors_cache_remove(const char *ndn)
61f723
+ancestors_cache_remove(MemberOfConfig *config, const char *ndn)
61f723
 {
61f723
 	PRBool rc;
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
@@ -3292,7 +3190,7 @@ ancestors_cache_remove(const char *ndn)
61f723
 	}
61f723
 #endif
61f723
 
61f723
-	rc = PL_HashTableRemove(group_ancestors_hashtable, (const void *) ndn);
61f723
+	rc = PL_HashTableRemove(config->ancestors_cache, (const void *) ndn);
61f723
 
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
 	if (start) {
61f723
@@ -3305,7 +3203,7 @@ ancestors_cache_remove(const char *ndn)
61f723
 }
61f723
 
61f723
 static PLHashEntry *
61f723
-ancestors_cache_add(const void *key, void *value)
61f723
+ancestors_cache_add(MemberOfConfig *config, const void *key, void *value)
61f723
 {
61f723
 	PLHashEntry *e;
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
@@ -3322,7 +3220,7 @@ ancestors_cache_add(const void *key, void *value)
61f723
 	}
61f723
 #endif
61f723
 
61f723
-	e = PL_HashTableAdd(group_ancestors_hashtable, key, value);
61f723
+	e = PL_HashTableAdd(config->ancestors_cache, key, value);
61f723
 
61f723
 #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
 	if (start) {
61f723
@@ -3360,10 +3258,11 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
61f723
 		goto bail;
61f723
 	}
61f723
 
61f723
-        /* Check if the entry has not already been fixed */
61f723
+    /* Check if the entry has not already been fixed */
61f723
 	ndn = slapi_sdn_get_ndn(sdn);
61f723
-	if (ndn && fixup_entry_hashtable && PL_HashTableLookupConst(fixup_entry_hashtable, (void*) ndn)) {
61f723
-		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: Entry %s already fixed up\n", ndn);
61f723
+	if (ndn && config->fixup_cache && PL_HashTableLookupConst(config->fixup_cache, (void*) ndn)) {
61f723
+		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,
61f723
+            "memberof_fix_memberof_callback: Entry %s already fixed up\n", ndn);
61f723
 		goto bail;
61f723
 	}
61f723
 
61f723
@@ -3383,9 +3282,9 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
61f723
 #if MEMBEROF_CACHE_DEBUG
61f723
 			slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: This is NOT a group %s\n", ndn);
61f723
 #endif
61f723
-			ht_grp = ancestors_cache_lookup((const void *) ndn);
61f723
+			ht_grp = ancestors_cache_lookup(config, (const void *) ndn);
61f723
 			if (ht_grp) {
61f723
-				if (ancestors_cache_remove((const void *) ndn)) {
61f723
+				if (ancestors_cache_remove(config, (const void *) ndn)) {
61f723
 					slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: free cached values for %s\n", ndn);
61f723
 					ancestor_hashtable_entry_free(ht_grp);
61f723
 					slapi_ch_free((void **) &ht_grp);
61f723
@@ -3400,6 +3299,7 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
61f723
 			}
61f723
 		}
61f723
 	}
61f723
+
61f723
 	/* If we found some groups, replace the existing memberOf attribute
61f723
 	 * with the found values.  */
61f723
 	if (groups && slapi_valueset_count(groups))
61f723
@@ -3439,9 +3339,9 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data)
61f723
 	slapi_valueset_free(groups);
61f723
 
61f723
 	/* records that this entry has been fixed up */
61f723
-	if (fixup_entry_hashtable) {
61f723
+	if (config->fixup_cache) {
61f723
 		dn_copy = slapi_ch_strdup(ndn);
61f723
-		if (PL_HashTableAdd(fixup_entry_hashtable, dn_copy, dn_copy) == NULL) {
61f723
+		if (PL_HashTableAdd(config->fixup_cache, dn_copy, dn_copy) == NULL) {
61f723
 			slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: "
61f723
 				"failed to add dn (%s) in the fixup hashtable; NSPR error - %d\n",
61f723
 				dn_copy, PR_GetError());
61f723
@@ -3539,150 +3439,8 @@ memberof_add_objectclass(char *auto_add_oc, const char *dn)
61f723
 	return rc;
61f723
 }
61f723
 
61f723
-static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2)
61f723
-{
61f723
-	PRIntn rc;
61f723
-	if (0 == strcasecmp((const char *) v1, (const char *) v2)) {
61f723
-		rc = 1;
61f723
-	} else {
61f723
-		rc = 0;
61f723
-	}
61f723
-	return rc;
61f723
-}
61f723
-
61f723
-static PRIntn memberof_hash_compare_values(const void *v1, const void *v2)
61f723
-{
61f723
-	PRIntn rc;
61f723
-	if ((char *) v1 == (char *) v2) {
61f723
-		rc = 1;
61f723
-	} else {
61f723
-		rc = 0;
61f723
-	}
61f723
-	return rc;
61f723
-}
61f723
-
61f723
-/*
61f723
- *  Hashing function using Bernstein's method
61f723
- */
61f723
-static PLHashNumber memberof_hash_fn(const void *key)
61f723
-{
61f723
-    PLHashNumber hash = 5381;
61f723
-    unsigned char *x = (unsigned char *)key;
61f723
-    int c;
61f723
-
61f723
-    while ((c = *x++)){
61f723
-        hash = ((hash << 5) + hash) ^ c;
61f723
-    }
61f723
-    return hash;
61f723
-}
61f723
-
61f723
-/* allocates the plugin hashtable
61f723
- * This hash table is used by operation and is protected from
61f723
- * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
61f723
- * is not implemented and the hash table will be not used.
61f723
- *
61f723
- * The hash table contains all the DN of the entries for which the memberof
61f723
- * attribute has been computed/updated during the current operation
61f723
- *
61f723
- * hash table should be empty at the beginning and end of the plugin callback
61f723
- */
61f723
-static PLHashTable *hashtable_new()
61f723
-{
61f723
-	if (!usetxn) {
61f723
-		return NULL;
61f723
-	}
61f723
-
61f723
-	return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,
61f723
-		memberof_hash_fn,
61f723
-		memberof_hash_compare_keys,
61f723
-		memberof_hash_compare_values, NULL, NULL);
61f723
-}
61f723
-/* this function called for each hash node during hash destruction */
61f723
-static PRIntn fixup_hashtable_remove(PLHashEntry *he, PRIntn index, void *arg)
61f723
-{
61f723
-	char *dn_copy;
61f723
-
61f723
-	if (he == NULL) {
61f723
-		return HT_ENUMERATE_NEXT;
61f723
-	}
61f723
-	dn_copy = (char*) he->value;
61f723
-	slapi_ch_free_string(&dn_copy);
61f723
-
61f723
-	return HT_ENUMERATE_REMOVE;
61f723
-}
61f723
-
61f723
-static void fixup_hashtable_empty(char *msg)
61f723
-{
61f723
-	if (fixup_entry_hashtable) {
61f723
-		PL_HashTableEnumerateEntries(fixup_entry_hashtable, fixup_hashtable_remove, msg);
61f723
-	}
61f723
-}
61f723
-
61f723
-
61f723
-/* allocates the plugin hashtable
61f723
- * This hash table is used by operation and is protected from
61f723
- * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
61f723
- * is not implemented and the hash table will be not used.
61f723
- *
61f723
- * The hash table contains all the DN of the entries for which the memberof
61f723
- * attribute has been computed/updated during the current operation
61f723
- *
61f723
- * hash table should be empty at the beginning and end of the plugin callback
61f723
- */
61f723
-
61f723
-static
61f723
-void ancestor_hashtable_entry_free(memberof_cached_value *entry)
61f723
-{
61f723
-	int i;
61f723
-	for (i = 0; entry[i].valid; i++) {
61f723
-		slapi_ch_free((void **) &entry[i].group_dn_val);
61f723
-		slapi_ch_free((void **) &entry[i].group_ndn_val);
61f723
-	}
61f723
-	/* Here we are at the ending element containing the key */
61f723
-	slapi_ch_free((void**) &entry[i].key);
61f723
-}
61f723
-/* this function called for each hash node during hash destruction */
61f723
-static PRIntn ancestor_hashtable_remove(PLHashEntry *he, PRIntn index, void *arg)
61f723
+int
61f723
+memberof_use_txn()
61f723
 {
61f723
-	memberof_cached_value *group_ancestor_array;
61f723
-
61f723
-	if (he == NULL)
61f723
-		return HT_ENUMERATE_NEXT;
61f723
-
61f723
-
61f723
-	group_ancestor_array = (memberof_cached_value *) he->value;
61f723
-	ancestor_hashtable_entry_free(group_ancestor_array);
61f723
-	slapi_ch_free((void **)&group_ancestor_array);
61f723
-
61f723
-	return HT_ENUMERATE_REMOVE;
61f723
+    return usetxn;
61f723
 }
61f723
-
61f723
-static void ancestor_hashtable_empty(char *msg)
61f723
-{
61f723
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
-	long int start;
61f723
-	struct timespec tsnow;
61f723
-#endif
61f723
-
61f723
-	if (group_ancestors_hashtable) {
61f723
-		cache_stat.total_enumerate++;
61f723
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
-		if (clock_gettime(CLOCK_REALTIME, &tsnow) != 0) {
61f723
-			start = 0;
61f723
-		} else {
61f723
-			start = tsnow.tv_nsec;
61f723
-		}
61f723
-#endif
61f723
-		PL_HashTableEnumerateEntries(group_ancestors_hashtable, ancestor_hashtable_remove, msg);
61f723
-
61f723
-#if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
61f723
-		if (start) {
61f723
-			if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) {
61f723
-				cache_stat.cumul_duration_enumerate += (tsnow.tv_nsec - start);
61f723
-			}
61f723
-		}
61f723
-#endif
61f723
-	}
61f723
-
61f723
-}
61f723
-
61f723
diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h
61f723
index 9a3a6a25d..a01c4d247 100644
61f723
--- a/ldap/servers/plugins/memberof/memberof.h
61f723
+++ b/ldap/servers/plugins/memberof/memberof.h
61f723
@@ -62,8 +62,22 @@ typedef struct memberofconfig {
61f723
 	int skip_nested;
61f723
 	int fixup_task;
61f723
 	char *auto_add_oc;
61f723
+	PLHashTable *ancestors_cache;
61f723
+	PLHashTable *fixup_cache;
61f723
 } MemberOfConfig;
61f723
 
61f723
+/* The key to access the hash table is the normalized DN
61f723
+ * The normalized DN is stored in the value because:
61f723
+ *  - It is used in slapi_valueset_find
61f723
+ *  - It is used to fill the memberof_get_groups_data.group_norm_vals
61f723
+ */
61f723
+typedef struct _memberof_cached_value
61f723
+{
61f723
+    char *key;
61f723
+    char *group_dn_val;
61f723
+    char *group_ndn_val;
61f723
+    int valid;
61f723
+} memberof_cached_value;
61f723
 
61f723
 /*
61f723
  * functions
61f723
@@ -88,5 +102,8 @@ int memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Ent
61f723
 void *memberof_get_plugin_id(void);
61f723
 void memberof_release_config(void);
61f723
 PRUint64 get_plugin_started(void);
61f723
+void ancestor_hashtable_entry_free(memberof_cached_value *entry);
61f723
+PLHashTable *hashtable_new();
61f723
+int memberof_use_txn();
61f723
 
61f723
 #endif	/* _MEMBEROF_H_ */
61f723
diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c
61f723
index c3474bf2c..3cc7c4d9c 100644
61f723
--- a/ldap/servers/plugins/memberof/memberof_config.c
61f723
+++ b/ldap/servers/plugins/memberof/memberof_config.c
61f723
@@ -14,12 +14,12 @@
61f723
  * memberof_config.c - configuration-related code for memberOf plug-in
61f723
  *
61f723
  */
61f723
-
61f723
+#include "plhash.h"
61f723
 #include <plstr.h>
61f723
-
61f723
 #include "memberof.h"
61f723
 
61f723
 #define MEMBEROF_CONFIG_FILTER "(objectclass=*)"
61f723
+#define MEMBEROF_HASHTABLE_SIZE 1000
61f723
 
61f723
 /*
61f723
  * The configuration attributes are contained in the plugin entry e.g.
61f723
@@ -33,7 +33,9 @@
61f723
 
61f723
 /*
61f723
  * function prototypes
61f723
- */ 
61f723
+ */
61f723
+static void fixup_hashtable_empty( MemberOfConfig *config, char *msg);
61f723
+static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg);
61f723
 static int memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
61f723
 										 int *returncode, char *returntext, void *arg);
61f723
 static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
61f723
@@ -48,7 +50,7 @@ static int memberof_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_En
61f723
 /* This is the main configuration which is updated from dse.ldif.  The
61f723
  * config will be copied when it is used by the plug-in to prevent it
61f723
  * being changed out from under a running memberOf operation. */
61f723
-static MemberOfConfig theConfig = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
61f723
+static MemberOfConfig theConfig = {0};
61f723
 static Slapi_RWLock *memberof_config_lock = 0;
61f723
 static int inited = 0;
61f723
 
61f723
@@ -696,6 +698,12 @@ memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
61f723
 {
61f723
 	if (dest && src)
61f723
 	{
61f723
+        /* Allocate our caches here since we only copy the config at the start of an op */
61f723
+        if (memberof_use_txn() == 1){
61f723
+            dest->ancestors_cache = hashtable_new();
61f723
+            dest->fixup_cache = hashtable_new();
61f723
+        }
61f723
+
61f723
 		/* Check if the copy is already up to date */
61f723
 		if (src->groupattrs)
61f723
 		{
61f723
@@ -799,6 +807,14 @@ memberof_free_config(MemberOfConfig *config)
61f723
 		slapi_ch_free_string(&config->memberof_attr);
61f723
 		memberof_free_scope(config->entryScopes, &config->entryScopeCount);
61f723
 		memberof_free_scope(config->entryScopeExcludeSubtrees, &config->entryExcludeScopeCount);
61f723
+		if (config->fixup_cache) {
61f723
+			fixup_hashtable_empty(config, "memberof_free_config empty fixup_entry_hastable");
61f723
+			PL_HashTableDestroy(config->fixup_cache);
61f723
+		}
61f723
+		if (config->ancestors_cache) {
61f723
+			ancestor_hashtable_empty(config, "memberof_free_config empty group_ancestors_hashtable");
61f723
+			PL_HashTableDestroy(config->ancestors_cache);
61f723
+		}
61f723
 	}
61f723
 }
61f723
 
61f723
@@ -1001,3 +1017,131 @@ bail:
61f723
 
61f723
 	return ret;
61f723
 }
61f723
+
61f723
+
61f723
+static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2)
61f723
+{
61f723
+	PRIntn rc;
61f723
+	if (0 == strcasecmp((const char *) v1, (const char *) v2)) {
61f723
+		rc = 1;
61f723
+	} else {
61f723
+		rc = 0;
61f723
+	}
61f723
+	return rc;
61f723
+}
61f723
+
61f723
+static PRIntn memberof_hash_compare_values(const void *v1, const void *v2)
61f723
+{
61f723
+	PRIntn rc;
61f723
+	if ((char *) v1 == (char *) v2) {
61f723
+		rc = 1;
61f723
+	} else {
61f723
+		rc = 0;
61f723
+	}
61f723
+	return rc;
61f723
+}
61f723
+
61f723
+/*
61f723
+ *  Hashing function using Bernstein's method
61f723
+ */
61f723
+static PLHashNumber memberof_hash_fn(const void *key)
61f723
+{
61f723
+    PLHashNumber hash = 5381;
61f723
+    unsigned char *x = (unsigned char *)key;
61f723
+    int c;
61f723
+
61f723
+    while ((c = *x++)){
61f723
+        hash = ((hash << 5) + hash) ^ c;
61f723
+    }
61f723
+    return hash;
61f723
+}
61f723
+
61f723
+/* allocates the plugin hashtable
61f723
+ * This hash table is used by operation and is protected from
61f723
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
61f723
+ * is not implemented and the hash table will be not used.
61f723
+ *
61f723
+ * The hash table contains all the DN of the entries for which the memberof
61f723
+ * attribute has been computed/updated during the current operation
61f723
+ *
61f723
+ * hash table should be empty at the beginning and end of the plugin callback
61f723
+ */
61f723
+PLHashTable *hashtable_new(int usetxn)
61f723
+{
61f723
+	if (!usetxn) {
61f723
+		return NULL;
61f723
+	}
61f723
+
61f723
+	return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,
61f723
+		memberof_hash_fn,
61f723
+		memberof_hash_compare_keys,
61f723
+		memberof_hash_compare_values, NULL, NULL);
61f723
+}
61f723
+
61f723
+/* this function called for each hash node during hash destruction */
61f723
+static PRIntn fixup_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
61f723
+{
61f723
+	char *dn_copy;
61f723
+
61f723
+	if (he == NULL) {
61f723
+		return HT_ENUMERATE_NEXT;
61f723
+	}
61f723
+	dn_copy = (char*) he->value;
61f723
+	slapi_ch_free_string(&dn_copy);
61f723
+
61f723
+	return HT_ENUMERATE_REMOVE;
61f723
+}
61f723
+
61f723
+static void fixup_hashtable_empty(MemberOfConfig *config, char *msg)
61f723
+{
61f723
+	if (config->fixup_cache) {
61f723
+		PL_HashTableEnumerateEntries(config->fixup_cache, fixup_hashtable_remove, msg);
61f723
+	}
61f723
+}
61f723
+
61f723
+
61f723
+/* allocates the plugin hashtable
61f723
+ * This hash table is used by operation and is protected from
61f723
+ * concurrent operations with the memberof_lock (if not usetxn, memberof_lock
61f723
+ * is not implemented and the hash table will be not used.
61f723
+ *
61f723
+ * The hash table contains all the DN of the entries for which the memberof
61f723
+ * attribute has been computed/updated during the current operation
61f723
+ *
61f723
+ * hash table should be empty at the beginning and end of the plugin callback
61f723
+ */
61f723
+
61f723
+void ancestor_hashtable_entry_free(memberof_cached_value *entry)
61f723
+{
61f723
+	int i;
61f723
+
61f723
+	for (i = 0; entry[i].valid; i++) {
61f723
+		slapi_ch_free((void **) &entry[i].group_dn_val);
61f723
+		slapi_ch_free((void **) &entry[i].group_ndn_val);
61f723
+	}
61f723
+	/* Here we are at the ending element containing the key */
61f723
+	slapi_ch_free((void**) &entry[i].key);
61f723
+}
61f723
+
61f723
+/* this function called for each hash node during hash destruction */
61f723
+static PRIntn ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))
61f723
+{
61f723
+    memberof_cached_value *group_ancestor_array;
61f723
+
61f723
+    if (he == NULL) {
61f723
+        return HT_ENUMERATE_NEXT;
61f723
+    }
61f723
+    group_ancestor_array = (memberof_cached_value *) he->value;
61f723
+    ancestor_hashtable_entry_free(group_ancestor_array);
61f723
+    slapi_ch_free((void **)&group_ancestor_array);
61f723
+
61f723
+    return HT_ENUMERATE_REMOVE;
61f723
+}
61f723
+
61f723
+static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg)
61f723
+{
61f723
+	if (config->ancestors_cache) {
61f723
+		PL_HashTableEnumerateEntries(config->ancestors_cache, ancestor_hashtable_remove, msg);
61f723
+	}
61f723
+
61f723
+}
61f723
-- 
61f723
2.13.6
61f723