andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone
Blob Blame History Raw
From e1775dcabc1cc88be12703ad62bbaf5341b8f8a4 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Fri, 19 Dec 2014 16:36:11 -0500
Subject: [PATCH 286/305] Ticket 408 - Backport of Normalized DN Cache

https://fedorahosted.org/389/ticket/408
(cherry picked from commit 2a8da7ea76d15906fdb98b47534fc3447f12c752)
---
 ldap/ldif/template-dse.ldif.in         |   3 +-
 ldap/servers/slapd/attrsyntax.c        |  23 --
 ldap/servers/slapd/back-ldbm/monitor.c |  26 ++-
 ldap/servers/slapd/dn.c                | 401 +++++++++++++++++++++++++++++++++
 ldap/servers/slapd/libglobs.c          |  76 ++++++-
 ldap/servers/slapd/main.c              |   4 +
 ldap/servers/slapd/proto-slap.h        |  12 +
 ldap/servers/slapd/schema.c            |  17 --
 ldap/servers/slapd/slap.h              |  10 +
 ldap/servers/slapd/slapi-private.h     |   8 +-
 10 files changed, 532 insertions(+), 48 deletions(-)

diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index ddf2b35..c626726 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -53,9 +53,10 @@ nsslapd-auditlog-maxlogsize: 100
 nsslapd-auditlog-logrotationtime: 1
 nsslapd-auditlog-logrotationtimeunit: day
 nsslapd-rootdn: %rootdn%
+nsslapd-rootpw: %ds_passwd%
 nsslapd-maxdescriptors: 1024
 nsslapd-max-filter-nest-level: 40
-nsslapd-rootpw: %ds_passwd%
+nsslapd-ndn-cache-enabled: off
 
 dn: cn=features,cn=config
 objectclass: top
diff --git a/ldap/servers/slapd/attrsyntax.c b/ldap/servers/slapd/attrsyntax.c
index 79736b5..75114e0 100644
--- a/ldap/servers/slapd/attrsyntax.c
+++ b/ldap/servers/slapd/attrsyntax.c
@@ -219,29 +219,6 @@ attr_syntax_new()
 }
 
 /*
- * hashNocaseString - used for case insensitive hash lookups
- */
-static PLHashNumber
-hashNocaseString(const void *key)
-{
-    PLHashNumber h = 0;
-    const unsigned char *s;
- 
-    for (s = key; *s; s++)
-        h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
-    return h;
-}
-
-/*
- * hashNocaseCompare - used for case insensitive hash key comparisons
- */
-static PRIntn
-hashNocaseCompare(const void *v1, const void *v2)
-{
-	return (strcasecmp((char *)v1, (char *)v2) == 0);
-}
-
-/*
  * Given an OID, return the syntax info.  If there is more than one
  * attribute syntax with the same OID (i.e. aliases), the first one
  * will be returned.  This is usually the "canonical" one, but it may
diff --git a/ldap/servers/slapd/back-ldbm/monitor.c b/ldap/servers/slapd/back-ldbm/monitor.c
index e3e1fb5..52a8ace 100644
--- a/ldap/servers/slapd/back-ldbm/monitor.c
+++ b/ldap/servers/slapd/back-ldbm/monitor.c
@@ -70,8 +70,8 @@ int ldbm_back_monitor_instance_search(Slapi_PBlock *pb, Slapi_Entry *e,
     struct berval *vals[2];
     char buf[BUFSIZ];
     PRUint64 hits, tries;
-    long nentries,maxentries;
-    size_t size,maxsize;
+    long nentries, maxentries, count;
+    size_t size, maxsize;
 /* NPCTE fix for bugid 544365, esc 0. <P.R> <04-Jul-2001> */
     struct stat astat;
 /* end of NPCTE fix for bugid 544365 */
@@ -145,6 +145,28 @@ int ldbm_back_monitor_instance_search(Slapi_PBlock *pb, Slapi_Entry *e,
         sprintf(buf, "%ld", maxentries);
         MSET("maxDnCacheCount");
     }
+    /* normalized dn cache stats */
+    if(ndn_cache_started()){
+        ndn_cache_get_stats(&hits, &tries, &size, &maxsize, &count);
+        sprintf(buf, "%" NSPRIu64, tries);
+        MSET("normalizedDnCacheTries");
+        sprintf(buf, "%" NSPRIu64, hits);
+        MSET("normalizedDnCacheHits");
+        sprintf(buf, "%" NSPRIu64, tries - hits);
+        MSET("normalizedDnCacheMisses");
+        sprintf(buf, "%lu", (unsigned long)(100.0*(double)hits / (double)(tries > 0 ? tries : 1)));
+        MSET("normalizedDnCacheHitRatio");
+        sprintf(buf, "%lu", size);
+        MSET("currentNormalizedDnCacheSize");
+        if(maxsize == 0){
+            sprintf(buf, "%d", -1);
+        } else {
+            sprintf(buf, "%lu", maxsize);
+        }
+        MSET("maxNormalizedDnCacheSize");
+        sprintf(buf, "%ld", count);
+        MSET("currentNormalizedDnCacheCount");
+    }
 
 #ifdef DEBUG
     {
diff --git a/ldap/servers/slapd/dn.c b/ldap/servers/slapd/dn.c
index 804d56e..283f265 100644
--- a/ldap/servers/slapd/dn.c
+++ b/ldap/servers/slapd/dn.c
@@ -51,6 +51,7 @@
 #include <sys/socket.h>
 #endif
 #include "slap.h"
+#include <plhash.h>
 
 #undef SDN_DEBUG
 
@@ -61,6 +62,53 @@ static void sort_rdn_avs( struct berval *avs, int count, int escape );
 static int rdn_av_cmp( struct berval *av1, struct berval *av2 );
 static void rdn_av_swap( struct berval *av1, struct berval *av2, int escape );
 
+/* normalized dn cache related definitions*/
+struct
+ndn_cache_lru
+{
+    struct ndn_cache_lru *prev;
+    struct ndn_cache_lru *next;
+    char *key;
+};
+
+struct
+ndn_cache_ctx
+{
+    struct ndn_cache_lru *head;
+    struct ndn_cache_lru *tail;
+    Slapi_Counter *cache_hits;
+    Slapi_Counter *cache_tries;
+    Slapi_Counter *cache_misses;
+    size_t cache_size;
+    size_t cache_max_size;
+    long cache_count;
+};
+
+struct
+ndn_hash_val
+{
+    char *ndn;
+    size_t len;
+    int size;
+    struct ndn_cache_lru *lru_node; /* used to speed up lru shuffling */
+};
+
+#define NDN_FLUSH_COUNT 10000 /* number of DN's to remove when cache fills up */
+#define NDN_MIN_COUNT 1000 /* the minimum number of DN's to keep in the cache */
+#define NDN_CACHE_BUCKETS 2053 /* prime number */
+
+static PLHashNumber ndn_hash_string(const void *key);
+static int ndn_cache_lookup(char *dn, size_t dn_len, char **result, char **udn, int *rc);
+static void ndn_cache_update_lru(struct ndn_cache_lru **node);
+static void ndn_cache_add(char *dn, size_t dn_len, char *ndn, size_t ndn_len);
+static void ndn_cache_delete(char *dn);
+static void ndn_cache_flush();
+static void ndn_cache_free();
+static int ndn_started = 0;
+static PRLock *lru_lock = NULL;
+static Slapi_RWLock *ndn_cache_lock = NULL;
+static struct ndn_cache_ctx *ndn_cache = NULL;
+static PLHashTable *ndn_cache_hashtable = NULL;
 
 #define ISBLANK(c)	((c) == ' ')
 #define ISBLANKSTR(s)	(((*(s)) == '2') && (*((s)+1) == '0'))
@@ -487,6 +535,7 @@ slapi_dn_normalize_ext(char *src, size_t src_len, char **dest, size_t *dest_len)
     char *ends = NULL;
     char *endd = NULL;
     char *lastesc = NULL;
+    char *udn;
     /* rdn avs for the main DN */
     char *typestart = NULL;
     int rdn_av_count = 0;
@@ -511,6 +560,14 @@ slapi_dn_normalize_ext(char *src, size_t src_len, char **dest, size_t *dest_len)
     if (0 == src_len) {
         src_len = strlen(src);
     }
+    /*
+     *  Check the normalized dn cache
+     */
+    if(ndn_cache_lookup(src, src_len, dest, &udn, &rc)){
+        *dest_len = strlen(*dest);
+        return rc;
+    }
+
     s = PL_strnchr(src, '\\', src_len);
     if (s) {
         *dest_len = src_len * 3;
@@ -1072,6 +1129,10 @@ bail:
         /* We terminate the str with NULL only when we allocate the str */
         *d = '\0';
     }
+    /* add this dn to the normalized dn cache */
+    if(*dest)
+        ndn_cache_add(udn, src_len, *dest, *dest_len);
+
     return rc;
 }
 
@@ -2622,3 +2683,343 @@ slapi_sdn_get_size(const Slapi_DN *sdn)
     return sz;
 }
 
+/*
+ *
+ *  Normalized DN Cache
+ *
+ */
+
+/*
+ *  Hashing function using Bernstein's method
+ */
+static PLHashNumber
+ndn_hash_string(const void *key)
+{
+    PLHashNumber hash = 5381;
+    unsigned char *x = (unsigned char *)key;
+    int c;
+
+    while ((c = *x++)){
+        hash = ((hash << 5) + hash) ^ c;
+    }
+    return hash;
+}
+
+void
+ndn_cache_init()
+{
+    if(!config_get_ndn_cache_enabled() || ndn_started){
+        return;
+    }
+    ndn_cache_hashtable = PL_NewHashTable( NDN_CACHE_BUCKETS, ndn_hash_string, PL_CompareStrings, PL_CompareValues, 0, 0);
+    ndn_cache = (struct ndn_cache_ctx *)slapi_ch_malloc(sizeof(struct ndn_cache_ctx));
+    ndn_cache->cache_max_size = config_get_ndn_cache_size();
+    ndn_cache->cache_hits = slapi_counter_new();
+    ndn_cache->cache_tries = slapi_counter_new();
+    ndn_cache->cache_misses = slapi_counter_new();
+    ndn_cache->cache_count = 0;
+    ndn_cache->cache_size = sizeof(struct ndn_cache_ctx) + sizeof(PLHashTable) + sizeof(PLHashTable);
+    ndn_cache->head = NULL;
+    ndn_cache->tail = NULL;
+    ndn_started = 1;
+    if ( NULL == ( lru_lock = PR_NewLock()) ||  NULL == ( ndn_cache_lock = slapi_new_rwlock())) {
+        ndn_cache_destroy();
+        slapi_log_error( SLAPI_LOG_FATAL, "ndn_cache_init", "Failed to create locks.  Disabling cache.\n" );
+    }
+}
+
+void
+ndn_cache_destroy()
+{
+    char *errorbuf = NULL;
+
+    if(!ndn_started){
+        return;
+    }
+    if(lru_lock){
+        PR_DestroyLock(lru_lock);
+        lru_lock = NULL;
+    }
+    if(ndn_cache_lock){
+        slapi_destroy_rwlock(ndn_cache_lock);
+        ndn_cache_lock = NULL;
+    }
+    if(ndn_cache_hashtable){
+        ndn_cache_free();
+        PL_HashTableDestroy(ndn_cache_hashtable);
+        ndn_cache_hashtable = NULL;
+    }
+    config_set_ndn_cache_enabled(CONFIG_NDN_CACHE, "off", errorbuf, 1 );
+    slapi_counter_destroy(&ndn_cache->cache_hits);
+    slapi_counter_destroy(&ndn_cache->cache_tries);
+    slapi_counter_destroy(&ndn_cache->cache_misses);
+    slapi_ch_free((void **)&ndn_cache);
+
+    ndn_started = 0;
+}
+
+int
+ndn_cache_started()
+{
+    return ndn_started;
+}
+
+/*
+ *  Look up this dn in the ndn cache
+ */
+static int
+ndn_cache_lookup(char *dn, size_t dn_len, char **result, char **udn, int *rc)
+{
+    struct ndn_hash_val *ndn_ht_val = NULL;
+    char *ndn, *key;
+    int rv = 0;
+
+    if(ndn_started == 0){
+        return rv;
+    }
+    if(dn_len == 0){
+        *result = dn;
+        *rc = 0;
+        return 1;
+    }
+    slapi_counter_increment(ndn_cache->cache_tries);
+    slapi_rwlock_rdlock(ndn_cache_lock);
+    ndn_ht_val = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);
+    if(ndn_ht_val){
+        ndn_cache_update_lru(&ndn_ht_val->lru_node);
+        slapi_counter_increment(ndn_cache->cache_hits);
+        if(ndn_ht_val->len == dn_len ){
+            /* the dn was already normalized, just return the dn as the result */
+            *result = dn;
+            *rc = 0;
+        } else {
+            *rc = 1; /* free result */
+            ndn = slapi_ch_malloc(ndn_ht_val->len + 1);
+            memcpy(ndn, ndn_ht_val->ndn, ndn_ht_val->len);
+            ndn[ndn_ht_val->len] = '\0';
+            *result = ndn;
+        }
+        rv = 1;
+    } else {
+        /* copy/preserve the udn, so we can use it as the key when we add dn's to the hashtable */
+        key = slapi_ch_malloc(dn_len + 1);
+        memcpy(key, dn, dn_len);
+        key[dn_len] = '\0';
+        *udn = key;
+    }
+    slapi_rwlock_unlock(ndn_cache_lock);
+
+    return rv;
+}
+
+/*
+ *  Move this lru node to the top of the list
+ */
+static void
+ndn_cache_update_lru(struct ndn_cache_lru **node)
+{
+    struct ndn_cache_lru *prev, *next, *curr_node = *node;
+
+    if(curr_node == NULL){
+        return;
+    }
+    PR_Lock(lru_lock);
+    if(curr_node->prev == NULL){
+        /* already the top node */
+        PR_Unlock(lru_lock);
+        return;
+    }
+    prev = curr_node->prev;
+    next = curr_node->next;
+    if(next){
+        next->prev = prev;
+        prev->next = next;
+    } else {
+        /* this was the tail, so reset the tail */
+        ndn_cache->tail = prev;
+        prev->next = NULL;
+    }
+    curr_node->prev = NULL;
+    curr_node->next = ndn_cache->head;
+    ndn_cache->head->prev = curr_node;
+    ndn_cache->head = curr_node;
+    PR_Unlock(lru_lock);
+}
+
+/*
+ *  Add a ndn to the cache.  Try and do as much as possible before taking the write lock.
+ */
+static void
+ndn_cache_add(char *dn, size_t dn_len, char *ndn, size_t ndn_len)
+{
+    struct ndn_hash_val *ht_entry;
+    struct ndn_cache_lru *new_node = NULL;
+    PLHashEntry *he;
+    int size;
+
+    if(ndn_started == 0 || dn_len == 0){
+        return;
+    }
+    if(strlen(ndn) > ndn_len){
+        /* we need to null terminate the ndn */
+        *(ndn + ndn_len) = '\0';
+    }
+    /*
+     *  Calculate the approximate memory footprint of the hash entry, key, and lru entry.
+     */
+    size = (dn_len * 2) + ndn_len + sizeof(PLHashEntry) + sizeof(struct ndn_hash_val) + sizeof(struct ndn_cache_lru);
+    /*
+     *  Create our LRU node
+     */
+    new_node = (struct ndn_cache_lru *)slapi_ch_malloc(sizeof(struct ndn_cache_lru));
+    if(new_node == NULL){
+        slapi_log_error( SLAPI_LOG_FATAL, "ndn_cache_add", "Failed to allocate new lru node.\n");
+        return;
+    }
+    new_node->prev = NULL;
+    new_node->key = dn; /* dn has already been allocated */
+    /*
+     *  Its possible this dn was added to the hash by another thread.
+     */
+    slapi_rwlock_wrlock(ndn_cache_lock);
+    ht_entry = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);
+    if(ht_entry){
+        /* already exists, free the node and return */
+        slapi_rwlock_unlock(ndn_cache_lock);
+        slapi_ch_free_string(&new_node->key);
+        slapi_ch_free((void **)&new_node);
+        return;
+    }
+    /*
+     *  Create the hash entry
+     */
+    ht_entry = (struct ndn_hash_val *)slapi_ch_malloc(sizeof(struct ndn_hash_val));
+    if(ht_entry == NULL){
+        slapi_rwlock_unlock(ndn_cache_lock);
+        slapi_log_error( SLAPI_LOG_FATAL, "ndn_cache_add", "Failed to allocate new hash entry.\n");
+        slapi_ch_free_string(&new_node->key);
+        slapi_ch_free((void **)&new_node);
+        return;
+    }
+    ht_entry->ndn = slapi_ch_malloc(ndn_len + 1);
+    memcpy(ht_entry->ndn, ndn, ndn_len);
+    ht_entry->ndn[ndn_len] = '\0';
+    ht_entry->len = ndn_len;
+    ht_entry->size = size;
+    ht_entry->lru_node = new_node;
+    /*
+     *  Check if our cache is full
+     */
+    PR_Lock(lru_lock); /* grab the lru lock now, as ndn_cache_flush needs it */
+    if(ndn_cache->cache_max_size != 0 && ((ndn_cache->cache_size + size) > ndn_cache->cache_max_size)){
+        ndn_cache_flush();
+    }
+    /*
+     * Set the ndn cache lru nodes
+     */
+    if(ndn_cache->head == NULL && ndn_cache->tail == NULL){
+        /* this is the first node */
+        ndn_cache->head = new_node;
+        ndn_cache->tail = new_node;
+        new_node->next = NULL;
+    } else {
+        new_node->next = ndn_cache->head;
+        ndn_cache->head->prev = new_node;
+    }
+    ndn_cache->head = new_node;
+    PR_Unlock(lru_lock);
+    /*
+     *  Add the new object to the hashtable, and update our stats
+     */
+    he = PL_HashTableAdd(ndn_cache_hashtable, new_node->key, (void *)ht_entry);
+    if(he == NULL){
+        slapi_log_error( SLAPI_LOG_FATAL, "ndn_cache_add", "Failed to add new entry to hash(%s)\n",dn);
+    } else {
+        ndn_cache->cache_count++;
+        ndn_cache->cache_size += size;
+    }
+    slapi_rwlock_unlock(ndn_cache_lock);
+}
+
+/*
+ *  cache is full, remove the least used dn's.  lru_lock/ndn_cache write lock are already taken
+ */
+static void
+ndn_cache_flush()
+{
+    struct ndn_cache_lru *node, *next, *flush_node;
+    int i;
+
+    node = ndn_cache->tail;
+    for(i = 0; node && i < NDN_FLUSH_COUNT && ndn_cache->cache_count > NDN_MIN_COUNT; i++){
+        flush_node = node;
+        /* update the lru */
+        next = node->prev;
+        next->next = NULL;
+        ndn_cache->tail = next;
+        node = next;
+        /* now update the hash */
+        ndn_cache->cache_count--;
+        ndn_cache_delete(flush_node->key);
+        slapi_ch_free_string(&flush_node->key);
+        slapi_ch_free((void **)&flush_node);
+    }
+
+    slapi_log_error( SLAPI_LOG_CACHE, "ndn_cache_flush","Flushed cache.\n");
+}
+
+static void
+ndn_cache_free()
+{
+    struct ndn_cache_lru *node, *next, *flush_node;
+
+    if(!ndn_cache){
+        return;
+    }
+
+    node = ndn_cache->tail;
+    while(node && ndn_cache->cache_count){
+        flush_node = node;
+        /* update the lru */
+        next = node->prev;
+        if(next){
+            next->next = NULL;
+        }
+        ndn_cache->tail = next;
+        node = next;
+        /* now update the hash */
+        ndn_cache->cache_count--;
+        ndn_cache_delete(flush_node->key);
+        slapi_ch_free_string(&flush_node->key);
+        slapi_ch_free((void **)&flush_node);
+    }
+}
+
+/* this is already "write" locked from ndn_cache_add */
+static void
+ndn_cache_delete(char *dn)
+{
+    struct ndn_hash_val *ht_entry;
+
+    ht_entry = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);
+    if(ht_entry){
+        ndn_cache->cache_size -= ht_entry->size;
+        slapi_ch_free_string(&ht_entry->ndn);
+        slapi_ch_free((void **)&ht_entry);
+        PL_HashTableRemove(ndn_cache_hashtable, dn);
+    }
+}
+
+/* stats for monitor */
+void
+ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, long *count)
+{
+    slapi_rwlock_rdlock(ndn_cache_lock);
+    *hits = slapi_counter_get_value(ndn_cache->cache_hits);
+    *tries = slapi_counter_get_value(ndn_cache->cache_tries);
+    *size = ndn_cache->cache_size;
+    *max_size = ndn_cache->cache_max_size;
+    *count = ndn_cache->cache_count;
+    slapi_rwlock_unlock(ndn_cache_lock);
+}
+
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
index 79ca2bd..3c0c9f4 100644
--- a/ldap/servers/slapd/libglobs.c
+++ b/ldap/servers/slapd/libglobs.c
@@ -1035,7 +1035,15 @@ static struct config_get_and_set {
 	{CONFIG_IGNORE_TIME_SKEW, config_set_ignore_time_skew,
 		NULL, 0,
 		(void**)&global_slapdFrontendConfig.ignore_time_skew,
-		CONFIG_ON_OFF, (ConfigGetFunc)config_get_ignore_time_skew, &init_ignore_time_skew}
+		CONFIG_ON_OFF, (ConfigGetFunc)config_get_ignore_time_skew, &init_ignore_time_skew},
+	{CONFIG_NDN_CACHE, config_set_ndn_cache_enabled,
+		NULL, 0,
+		(void**)&global_slapdFrontendConfig.ndn_cache_enabled, CONFIG_INT,
+		(ConfigGetFunc)config_get_ndn_cache_enabled},
+	{CONFIG_NDN_CACHE_SIZE, config_set_ndn_cache_max_size,
+		NULL, 0,
+		(void**)&global_slapdFrontendConfig.ndn_cache_max_size,
+		CONFIG_INT, (ConfigGetFunc)config_get_ndn_cache_size},
 #ifdef MEMPOOL_EXPERIMENTAL
 	,{CONFIG_MEMPOOL_SWITCH_ATTRIBUTE, config_set_mempool_switch,
 		NULL, 0,
@@ -1053,7 +1061,7 @@ static struct config_get_and_set {
 /*
  * hashNocaseString - used for case insensitive hash lookups
  */
-static PLHashNumber
+PLHashNumber
 hashNocaseString(const void *key)
 {
     PLHashNumber h = 0;
@@ -1067,7 +1075,7 @@ hashNocaseString(const void *key)
 /*
  * hashNocaseCompare - used for case insensitive hash key comparisons
  */
-static PRIntn
+PRIntn
 hashNocaseCompare(const void *v1, const void *v2)
 {
 	return (strcasecmp((char *)v1, (char *)v2) == 0);
@@ -1463,6 +1471,11 @@ FrontendConfig_init () {
   init_malloc_mmap_threshold = cfg->malloc_mmap_threshold = DEFAULT_MALLOC_UNSET;
 #endif
 
+  cfg->disk_logging_critical = LDAP_OFF;
+  cfg->ndn_cache_enabled = LDAP_OFF;
+  cfg->ndn_cache_max_size = NDN_DEFAULT_SIZE;
+
+
 #ifdef MEMPOOL_EXPERIMENTAL
   init_mempool_switch = cfg->mempool_switch = LDAP_ON;
   cfg->mempool_maxfreelist = 1024;
@@ -1694,6 +1707,42 @@ config_set_sasl_maxbufsize(const char *attrname, char *value, char *errorbuf, in
     return retVal;
 }
 
+int
+config_set_ndn_cache_enabled(const char *attrname, char *value, char *errorbuf, int apply )
+{
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+    int retVal;
+
+    retVal = config_set_onoff ( attrname, value, &(slapdFrontendConfig->ndn_cache_enabled), errorbuf, apply);
+
+    return retVal;
+}
+
+int
+config_set_ndn_cache_max_size(const char *attrname, char *value, char *errorbuf, int apply )
+{
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+    long size;
+    int retVal = LDAP_SUCCESS;
+
+    size = atol(value);
+    if(size < 0){
+        size = 0; /* same as -1 */
+    }
+    if(size > 0 && size < 1024000){
+        PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "ndn_cache_max_size too low(%d), changing to "
+            "%d bytes.\n",(int)size, NDN_DEFAULT_SIZE);
+        size = NDN_DEFAULT_SIZE;
+    }
+    if(apply){
+        CFG_LOCK_WRITE(slapdFrontendConfig);
+        slapdFrontendConfig->ndn_cache_max_size = size;
+        CFG_UNLOCK_WRITE(slapdFrontendConfig);
+    }
+
+    return retVal;
+}
+
 int 
 config_set_port( const char *attrname, char *port, char *errorbuf, int apply ) {
   long nPort;
@@ -5626,6 +5675,27 @@ config_get_max_filter_nest_level()
 	return retVal;
 }
 
+size_t
+config_get_ndn_cache_size(){
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+    size_t retVal;
+
+    CFG_LOCK_READ(slapdFrontendConfig);
+    retVal = slapdFrontendConfig->ndn_cache_max_size;
+    CFG_UNLOCK_READ(slapdFrontendConfig);
+    return retVal;
+}
+
+int
+config_get_ndn_cache_enabled(){
+    slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+    int retVal;
+
+    CFG_LOCK_READ(slapdFrontendConfig);
+    retVal = slapdFrontendConfig->ndn_cache_enabled;
+    CFG_UNLOCK_READ(slapdFrontendConfig);
+    return retVal;
+}
 
 char *
 config_get_basedn() {
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
index bc07cbb..5d3e7e7 100644
--- a/ldap/servers/slapd/main.c
+++ b/ldap/servers/slapd/main.c
@@ -1046,6 +1046,9 @@ main( int argc, char **argv)
 		}
 	}
 
+	/* initialize the normalized DN cache */
+	ndn_cache_init();
+
 	/*
 	 * Detach ourselves from the terminal (unless running in debug mode).
 	 * We must detach before we start any threads since detach forks() on
@@ -1267,6 +1270,7 @@ main( int argc, char **argv)
 cleanup:
 	SSL_ShutdownServerSessionIDCache();
 	SSL_ClearSessionCache();
+	ndn_cache_destroy();
 	NSS_Shutdown();
 	PR_Cleanup();
 #ifdef _WIN32
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index ce09260..0891608 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -389,6 +389,7 @@ int config_set_disk_threshold( const char *attrname, char *value, char *errorbuf
 int config_set_disk_grace_period( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_disk_logging_critical( const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_auditlog_unhashed_pw(const char *attrname, char *value, char *errorbuf, int apply);
+
 int config_set_sasl_maxbufsize(const char *attrname, char *value, char *errorbuf, int apply );
 int config_set_listen_backlog_size(const char *attrname, char *value, char *errorbuf, int apply);
 int config_set_ignore_time_skew(const char *attrname, char *value, char *errorbuf, int apply);
@@ -398,6 +399,10 @@ int config_set_malloc_trim_threshold(const char *attrname, char *value, char *er
 int config_set_malloc_mmap_threshold(const char *attrname, char *value, char *errorbuf, int apply);
 #endif
 
+int config_set_ndn_cache_enabled(const char *attrname, char *value, char *errorbuf, int apply);
+int config_set_ndn_cache_max_size(const char *attrname, char *value, char *errorbuf, int apply);
+
+
 #if !defined(_WIN32) && !defined(AIX)
 int config_set_maxdescriptors( const char *attrname, char *value, char *errorbuf, int apply );
 #endif /* !_WIN_32 && !AIX */
@@ -562,6 +567,13 @@ int config_get_malloc_trim_threshold();
 int config_get_malloc_mmap_threshold();
 #endif
 
+int config_get_ndn_cache_count();
+size_t config_get_ndn_cache_size();
+int config_get_ndn_cache_enabled();
+PLHashNumber hashNocaseString(const void *key);
+PRIntn hashNocaseCompare(const void *v1, const void *v2);
+
+
 int is_abspath(const char *);
 char* rel2abspath( char * );
 char* rel2abspath_ext( char *, char * );
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
index 28c1ffc..18ae152 100644
--- a/ldap/servers/slapd/schema.c
+++ b/ldap/servers/slapd/schema.c
@@ -250,22 +250,6 @@ dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int
     return SLAPI_DSE_CALLBACK_ERROR;
 }
 
-#if 0
-/*
- * hashNocaseString - used for case insensitive hash lookups
- */
-static PLHashNumber
-hashNocaseString(const void *key)
-{
-    PLHashNumber h = 0;
-    const unsigned char *s;
-
-    for (s = key; *s; s++)
-        h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
-    return h;
-}
-#endif
-
 static const char *
 skipWS(const char *s)
 {
@@ -278,7 +262,6 @@ skipWS(const char *s)
 	return s;
 }
 
-
 /*
  * like strchr() but strings within single quotes are skipped.
  */
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 33cfeb4..70e8a51 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -2015,6 +2015,7 @@ typedef struct _slapdEntryPoints {
 #define CONFIG_DISK_THRESHOLD "nsslapd-disk-monitoring-threshold"
 #define CONFIG_DISK_GRACE_PERIOD "nsslapd-disk-monitoring-grace-period"
 #define CONFIG_DISK_LOGGING_CRITICAL "nsslapd-disk-monitoring-logging-critical"
+
 #define CONFIG_SASL_MAXBUFSIZE "nsslapd-sasl-max-buffer-size"
 #define CONFIG_LISTEN_BACKLOG_SIZE	"nsslapd-listen-backlog-size"
 #define CONFIG_IGNORE_TIME_SKEW "nsslapd-ignore-time-skew"
@@ -2035,6 +2036,10 @@ typedef struct _slapdEntryPoints {
 #define DAEMON_LISTEN_SIZE 128
 #endif
 
+#define CONFIG_NDN_CACHE "nsslapd-ndn-cache-enabled"
+#define CONFIG_NDN_CACHE_SIZE "nsslapd-ndn-cache-max-size"
+
+
 #ifdef MEMPOOL_EXPERIMENTAL
 #define CONFIG_MEMPOOL_SWITCH_ATTRIBUTE "nsslapd-mempool"
 #define CONFIG_MEMPOOL_MAXFREELIST_ATTRIBUTE "nsslapd-mempool-maxfreelist"
@@ -2272,12 +2277,17 @@ typedef struct _slapdFrontendConfig {
   PRInt64 disk_threshold;
   int disk_grace_period;
   int disk_logging_critical;
+
   int ignore_time_skew;
 #if defined(LINUX)
   int malloc_mxfast;            /* mallopt M_MXFAST */
   int malloc_trim_threshold;    /* mallopt M_TRIM_THRESHOLD */
   int malloc_mmap_threshold;    /* mallopt M_MMAP_THRESHOLD */
 #endif
+
+  /* normalized dn cache */
+  int ndn_cache_enabled;
+  size_t ndn_cache_max_size;
 } slapdFrontendConfig_t;
 
 /* possible values for slapdFrontendConfig_t.schemareplace */
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
index 940260f..8507f47 100644
--- a/ldap/servers/slapd/slapi-private.h
+++ b/ldap/servers/slapd/slapi-private.h
@@ -387,12 +387,16 @@ Slapi_DN *slapi_sdn_init_normdn_ndn_passin(Slapi_DN *sdn, const char *dn);
 Slapi_DN *slapi_sdn_init_normdn_passin(Slapi_DN *sdn, const char *dn);
 char *slapi_dn_normalize_original( char *dn );
 char *slapi_dn_normalize_case_original( char *dn );
+void ndn_cache_init();
+void ndn_cache_destroy();
+int ndn_cache_started();
+void ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, long *count);
+#define NDN_DEFAULT_SIZE 20971520 /* 20mb - size of normalized dn cache */
 
 /* filter.c */
 int filter_flag_is_set(const Slapi_Filter *f,unsigned char flag);
 char *slapi_filter_to_string(const Slapi_Filter *f, char *buffer, size_t bufsize);
-char *
-slapi_filter_to_string_internal( const struct slapi_filter *f, char *buf, size_t *bufsize );
+char *slapi_filter_to_string_internal( const struct slapi_filter *f, char *buf, size_t *bufsize );
 
 /* operation.c */
 
-- 
1.9.3