Ian Kent b04561
autofs-5.0.4 - make hash table scale to thousands of entries
Ian Kent b04561
Ian Kent b04561
From: Valerie Aurora Henson <vaurora@redhat.com>
Ian Kent b04561
Ian Kent b04561
Originally written by Paul Wankadia <junyer@google.com>.
Ian Kent b04561
Ian Kent b04561
The autofs cache lookup performs poorly for large maps.
Ian Kent b04561
Ian Kent b04561
The additive hashing algorithm used by autofs results in a clustering
Ian Kent b04561
of hash values around the average hash chain size. It is biased toward
Ian Kent b04561
a small range of hash indexes which becomes worse as the hash table size
Ian Kent b04561
increases.
Ian Kent b04561
Ian Kent b04561
Simple testing shows that the "One-at-a-time" hash function (implemented
Ian Kent b04561
by this patch) gives a much better distribution of hash indexes as table
Ian Kent b04561
size increases.
Ian Kent b04561
Ian Kent b04561
The only change made to the original patch is to make the hash table size
Ian Kent b04561
configurable with a default somewhat larger than it is currently.
Ian Kent b04561
---
Ian Kent b04561
Ian Kent b04561
 CHANGELOG                      |    2 ++
Ian Kent b04561
 include/automount.h            |    2 +-
Ian Kent b04561
 include/defaults.h             |    3 +++
Ian Kent b04561
 lib/cache.c                    |   47 +++++++++++++++++++++++-----------------
Ian Kent b04561
 lib/defaults.c                 |   16 +++++++++++++-
Ian Kent b04561
 redhat/autofs.sysconfig.in     |    6 +++++
Ian Kent b04561
 samples/autofs.conf.default.in |    6 +++++
Ian Kent b04561
 7 files changed, 60 insertions(+), 22 deletions(-)
Ian Kent b04561
Ian Kent b04561
Ian Kent b04561
diff --git a/CHANGELOG b/CHANGELOG
Ian Kent b04561
index 0fb7db5..912c088 100644
Ian Kent b04561
--- a/CHANGELOG
Ian Kent b04561
+++ b/CHANGELOG
Ian Kent b04561
@@ -5,6 +5,8 @@
Ian Kent b04561
 - fix negative caching for non-existent map keys.
Ian Kent b04561
 - use CLOEXEC flag.
Ian Kent b04561
 - fix select(2) fd limit.
Ian Kent b04561
+- make hash table scale to thousands of entries (Paul Wankadia,
Ian Kent b04561
+  Valerie Aurora Henson).
Ian Kent b04561
 
Ian Kent b04561
 4/11/2008 autofs-5.0.4
Ian Kent b04561
 -----------------------
Ian Kent b04561
diff --git a/include/automount.h b/include/automount.h
Ian Kent b04561
index a55ddbc..fa24885 100644
Ian Kent b04561
--- a/include/automount.h
Ian Kent b04561
+++ b/include/automount.h
Ian Kent b04561
@@ -128,7 +128,7 @@ struct autofs_point;
Ian Kent b04561
 #define CHE_DUPLICATE	0x0020
Ian Kent b04561
 #define CHE_UNAVAIL	0x0040
Ian Kent b04561
 
Ian Kent b04561
-#define HASHSIZE		77
Ian Kent b04561
+#define NULL_MAP_HASHSIZE	64
Ian Kent b04561
 #define NEGATIVE_TIMEOUT	10
Ian Kent b04561
 #define UMOUNT_RETRIES		8
Ian Kent b04561
 #define EXPIRE_RETRIES		3
Ian Kent b04561
diff --git a/include/defaults.h b/include/defaults.h
Ian Kent b04561
index 12534ec..9a2430f 100644
Ian Kent b04561
--- a/include/defaults.h
Ian Kent b04561
+++ b/include/defaults.h
Ian Kent b04561
@@ -40,6 +40,8 @@
Ian Kent b04561
 #define DEFAULT_APPEND_OPTIONS		1
Ian Kent b04561
 #define DEFAULT_AUTH_CONF_FILE		AUTOFS_MAP_DIR "/autofs_ldap_auth.conf"
Ian Kent b04561
 
Ian Kent b04561
+#define DEFAULT_MAP_HASH_TABLE_SIZE	1024
Ian Kent b04561
+
Ian Kent b04561
 struct ldap_schema;
Ian Kent b04561
 struct ldap_searchdn;
Ian Kent b04561
 
Ian Kent b04561
@@ -62,6 +64,7 @@ void defaults_free_searchdns(struct ldap_searchdn *);
Ian Kent b04561
 unsigned int defaults_get_append_options(void);
Ian Kent b04561
 unsigned int defaults_get_umount_wait(void);
Ian Kent b04561
 const char *defaults_get_auth_conf_file(void);
Ian Kent b04561
+unsigned int defaults_get_map_hash_table_size(void);
Ian Kent b04561
 
Ian Kent b04561
 #endif
Ian Kent b04561
 
Ian Kent b04561
diff --git a/lib/cache.c b/lib/cache.c
Ian Kent b04561
index 4a00367..edb3192 100644
Ian Kent b04561
--- a/lib/cache.c
Ian Kent b04561
+++ b/lib/cache.c
Ian Kent b04561
@@ -190,7 +190,7 @@ struct mapent_cache *cache_init(struct autofs_point *ap, struct map_source *map)
Ian Kent b04561
 	if (!mc)
Ian Kent b04561
 		return NULL;
Ian Kent b04561
 
Ian Kent b04561
-	mc->size = HASHSIZE;
Ian Kent b04561
+	mc->size = defaults_get_map_hash_table_size();
Ian Kent b04561
 
Ian Kent b04561
 	mc->hash = malloc(mc->size * sizeof(struct entry *));
Ian Kent b04561
 	if (!mc->hash) {
Ian Kent b04561
@@ -241,7 +241,7 @@ struct mapent_cache *cache_init_null_cache(struct master *master)
Ian Kent b04561
 	if (!mc)
Ian Kent b04561
 		return NULL;
Ian Kent b04561
 
Ian Kent b04561
-	mc->size = HASHSIZE;
Ian Kent b04561
+	mc->size = NULL_MAP_HASHSIZE;
Ian Kent b04561
 
Ian Kent b04561
 	mc->hash = malloc(mc->size * sizeof(struct entry *));
Ian Kent b04561
 	if (!mc->hash) {
Ian Kent b04561
@@ -279,29 +279,36 @@ struct mapent_cache *cache_init_null_cache(struct master *master)
Ian Kent b04561
 	return mc;
Ian Kent b04561
 }
Ian Kent b04561
 
Ian Kent b04561
-static unsigned int hash(const char *key)
Ian Kent b04561
+static u_int32_t hash(const char *key, unsigned int size)
Ian Kent b04561
 {
Ian Kent b04561
-	unsigned long hashval;
Ian Kent b04561
+	u_int32_t hashval;
Ian Kent b04561
 	char *s = (char *) key;
Ian Kent b04561
 
Ian Kent b04561
-	for (hashval = 0; *s != '\0';)
Ian Kent b04561
-		hashval += *s++;
Ian Kent b04561
+	for (hashval = 0; *s != '\0';) {
Ian Kent b04561
+		hashval += (unsigned char) *s++;
Ian Kent b04561
+		hashval += (hashval << 10);
Ian Kent b04561
+		hashval ^= (hashval >> 6);
Ian Kent b04561
+	}
Ian Kent b04561
+
Ian Kent b04561
+	hashval += (hashval << 3);
Ian Kent b04561
+	hashval ^= (hashval >> 11);
Ian Kent b04561
+	hashval += (hashval << 15);
Ian Kent b04561
 
Ian Kent b04561
-	return hashval % HASHSIZE;
Ian Kent b04561
+	return hashval % size;
Ian Kent b04561
 }
Ian Kent b04561
 
Ian Kent b04561
-static unsigned int ino_hash(dev_t dev, ino_t ino)
Ian Kent b04561
+static u_int32_t ino_hash(dev_t dev, ino_t ino, unsigned int size)
Ian Kent b04561
 {
Ian Kent b04561
-	unsigned long hashval;
Ian Kent b04561
+	u_int32_t hashval;
Ian Kent b04561
 
Ian Kent b04561
 	hashval = dev + ino;
Ian Kent b04561
 
Ian Kent b04561
-	return hashval % HASHSIZE;
Ian Kent b04561
+	return hashval % size;
Ian Kent b04561
 }
Ian Kent b04561
 
Ian Kent b04561
 int cache_set_ino_index(struct mapent_cache *mc, const char *key, dev_t dev, ino_t ino)
Ian Kent b04561
 {
Ian Kent b04561
-	unsigned int ino_index = ino_hash(dev, ino);
Ian Kent b04561
+	u_int32_t ino_index = ino_hash(dev, ino, mc->size);
Ian Kent b04561
 	struct mapent *me;
Ian Kent b04561
 
Ian Kent b04561
 	me = cache_lookup_distinct(mc, key);
Ian Kent b04561
@@ -323,10 +330,10 @@ struct mapent *cache_lookup_ino(struct mapent_cache *mc, dev_t dev, ino_t ino)
Ian Kent b04561
 {
Ian Kent b04561
 	struct mapent *me = NULL;
Ian Kent b04561
 	struct list_head *head, *p;
Ian Kent b04561
-	unsigned int ino_index;
Ian Kent b04561
+	u_int32_t ino_index;
Ian Kent b04561
 
Ian Kent b04561
 	ino_index_lock(mc);
Ian Kent b04561
-	ino_index = ino_hash(dev, ino);
Ian Kent b04561
+	ino_index = ino_hash(dev, ino, mc->size);
Ian Kent b04561
 	head = &mc->ino_index[ino_index];
Ian Kent b04561
 
Ian Kent b04561
 	list_for_each(p, head) {
Ian Kent b04561
@@ -369,7 +376,7 @@ struct mapent *cache_lookup_first(struct mapent_cache *mc)
Ian Kent b04561
 struct mapent *cache_lookup_next(struct mapent_cache *mc, struct mapent *me)
Ian Kent b04561
 {
Ian Kent b04561
 	struct mapent *this;
Ian Kent b04561
-	unsigned long hashval;
Ian Kent b04561
+	u_int32_t hashval;
Ian Kent b04561
 	unsigned int i;
Ian Kent b04561
 
Ian Kent b04561
 	if (!me)
Ian Kent b04561
@@ -385,7 +392,7 @@ struct mapent *cache_lookup_next(struct mapent_cache *mc, struct mapent *me)
Ian Kent b04561
 		return this;
Ian Kent b04561
 	}
Ian Kent b04561
 
Ian Kent b04561
-	hashval = hash(me->key) + 1;
Ian Kent b04561
+	hashval = hash(me->key, mc->size) + 1;
Ian Kent b04561
 	if (hashval < mc->size) {
Ian Kent b04561
 		for (i = (unsigned int) hashval; i < mc->size; i++) {
Ian Kent b04561
 			this = mc->hash[i];
Ian Kent b04561
@@ -433,7 +440,7 @@ struct mapent *cache_lookup(struct mapent_cache *mc, const char *key)
Ian Kent b04561
 	if (!key)
Ian Kent b04561
 		return NULL;
Ian Kent b04561
 
Ian Kent b04561
-	for (me = mc->hash[hash(key)]; me != NULL; me = me->next) {
Ian Kent b04561
+	for (me = mc->hash[hash(key, mc->size)]; me != NULL; me = me->next) {
Ian Kent b04561
 		if (strcmp(key, me->key) == 0)
Ian Kent b04561
 			goto done;
Ian Kent b04561
 	}
Ian Kent b04561
@@ -446,7 +453,7 @@ struct mapent *cache_lookup(struct mapent_cache *mc, const char *key)
Ian Kent b04561
 			goto done;
Ian Kent b04561
 		}
Ian Kent b04561
 
Ian Kent b04561
-		for (me = mc->hash[hash("*")]; me != NULL; me = me->next)
Ian Kent b04561
+		for (me = mc->hash[hash("*", mc->size)]; me != NULL; me = me->next)
Ian Kent b04561
 			if (strcmp("*", me->key) == 0)
Ian Kent b04561
 				goto done;
Ian Kent b04561
 	}
Ian Kent b04561
@@ -462,7 +469,7 @@ struct mapent *cache_lookup_distinct(struct mapent_cache *mc, const char *key)
Ian Kent b04561
 	if (!key)
Ian Kent b04561
 		return NULL;
Ian Kent b04561
 
Ian Kent b04561
-	for (me = mc->hash[hash(key)]; me != NULL; me = me->next) {
Ian Kent b04561
+	for (me = mc->hash[hash(key, mc->size)]; me != NULL; me = me->next) {
Ian Kent b04561
 		if (strcmp(key, me->key) == 0)
Ian Kent b04561
 			return me;
Ian Kent b04561
 	}
Ian Kent b04561
@@ -530,7 +537,7 @@ int cache_add(struct mapent_cache *mc, struct map_source *ms, const char *key, c
Ian Kent b04561
 {
Ian Kent b04561
 	struct mapent *me, *existing = NULL;
Ian Kent b04561
 	char *pkey, *pent;
Ian Kent b04561
-	unsigned int hashval = hash(key);
Ian Kent b04561
+	u_int32_t hashval = hash(key, mc->size);
Ian Kent b04561
 	int status;
Ian Kent b04561
 
Ian Kent b04561
 	me = (struct mapent *) malloc(sizeof(struct mapent));
Ian Kent b04561
@@ -750,7 +757,7 @@ int cache_update(struct mapent_cache *mc, struct map_source *ms, const char *key
Ian Kent b04561
 int cache_delete(struct mapent_cache *mc, const char *key)
Ian Kent b04561
 {
Ian Kent b04561
 	struct mapent *me = NULL, *pred;
Ian Kent b04561
-	unsigned int hashval = hash(key);
Ian Kent b04561
+	u_int32_t hashval = hash(key, mc->size);
Ian Kent b04561
 	int status, ret = CHE_OK;
Ian Kent b04561
 	char *this;
Ian Kent b04561
 
Ian Kent b04561
diff --git a/lib/defaults.c b/lib/defaults.c
Ian Kent b04561
index ff653e3..0d39716 100644
Ian Kent b04561
--- a/lib/defaults.c
Ian Kent b04561
+++ b/lib/defaults.c
Ian Kent b04561
@@ -49,6 +49,8 @@
Ian Kent b04561
 #define ENV_UMOUNT_WAIT			"UMOUNT_WAIT"
Ian Kent b04561
 #define ENV_AUTH_CONF_FILE		"AUTH_CONF_FILE"
Ian Kent b04561
 
Ian Kent b04561
+#define ENV_MAP_HASH_TABLE_SIZE		"MAP_HASH_TABLE_SIZE"
Ian Kent b04561
+
Ian Kent b04561
 static const char *default_master_map_name = DEFAULT_MASTER_MAP_NAME;
Ian Kent b04561
 static const char *default_auth_conf_file  = DEFAULT_AUTH_CONF_FILE;
Ian Kent b04561
 
Ian Kent b04561
@@ -323,7 +325,8 @@ unsigned int defaults_read_config(unsigned int to_syslog)
Ian Kent b04561
 		    check_set_config_value(key, ENV_NAME_VALUE_ATTR, value, to_syslog) ||
Ian Kent b04561
 		    check_set_config_value(key, ENV_APPEND_OPTIONS, value, to_syslog) ||
Ian Kent b04561
 		    check_set_config_value(key, ENV_UMOUNT_WAIT, value, to_syslog) ||
Ian Kent b04561
-		    check_set_config_value(key, ENV_AUTH_CONF_FILE, value, to_syslog))
Ian Kent b04561
+		    check_set_config_value(key, ENV_AUTH_CONF_FILE, value, to_syslog) ||
Ian Kent b04561
+		    check_set_config_value(key, ENV_MAP_HASH_TABLE_SIZE, value, to_syslog))
Ian Kent b04561
 			;
Ian Kent b04561
 	}
Ian Kent b04561
 
Ian Kent b04561
@@ -672,3 +675,14 @@ const char *defaults_get_auth_conf_file(void)
Ian Kent b04561
 	return (const char *) cf;
Ian Kent b04561
 }
Ian Kent b04561
 
Ian Kent b04561
+unsigned int defaults_get_map_hash_table_size(void)
Ian Kent b04561
+{
Ian Kent b04561
+	long size;
Ian Kent b04561
+
Ian Kent b04561
+	size = get_env_number(ENV_MAP_HASH_TABLE_SIZE);
Ian Kent b04561
+	if (size < 0)
Ian Kent b04561
+		size = DEFAULT_MAP_HASH_TABLE_SIZE;
Ian Kent b04561
+
Ian Kent b04561
+	return (unsigned int) size;
Ian Kent b04561
+}
Ian Kent b04561
+
Ian Kent b04561
diff --git a/redhat/autofs.sysconfig.in b/redhat/autofs.sysconfig.in
Ian Kent b04561
index 8256888..fe36f45 100644
Ian Kent b04561
--- a/redhat/autofs.sysconfig.in
Ian Kent b04561
+++ b/redhat/autofs.sysconfig.in
Ian Kent b04561
@@ -89,6 +89,12 @@ BROWSE_MODE="no"
Ian Kent b04561
 #
Ian Kent b04561
 #AUTH_CONF_FILE="@@autofsmapdir@@/autofs_ldap_auth.conf"
Ian Kent b04561
 #
Ian Kent b04561
+# MAP_HASH_TABLE_SIZE - set the map cache hash table size.
Ian Kent b04561
+# 			Should be a power of 2 with a ratio roughly
Ian Kent b04561
+# 			between 1:10 and 1:20 for each map.
Ian Kent b04561
+#
Ian Kent b04561
+#MAP_HASH_TABLE_SIZE=1024
Ian Kent b04561
+#
Ian Kent b04561
 # General global options
Ian Kent b04561
 #
Ian Kent b04561
 # If the kernel supports using the autofs miscellanous device
Ian Kent b04561
diff --git a/samples/autofs.conf.default.in b/samples/autofs.conf.default.in
Ian Kent b04561
index 844a6f3..4496738 100644
Ian Kent b04561
--- a/samples/autofs.conf.default.in
Ian Kent b04561
+++ b/samples/autofs.conf.default.in
Ian Kent b04561
@@ -89,6 +89,12 @@ BROWSE_MODE="no"
Ian Kent b04561
 #
Ian Kent b04561
 #AUTH_CONF_FILE="@@autofsmapdir@@/autofs_ldap_auth.conf"
Ian Kent b04561
 #
Ian Kent b04561
+# MAP_HASH_TABLE_SIZE - set the map cache hash table size.
Ian Kent b04561
+# 			Should be a power of 2 with a ratio roughly
Ian Kent b04561
+# 			between 1:10 and 1:20 for each map.
Ian Kent b04561
+#
Ian Kent b04561
+#MAP_HASH_TABLE_SIZE=1024
Ian Kent b04561
+#
Ian Kent b04561
 # General global options
Ian Kent b04561
 #
Ian Kent b04561
 # If the kernel supports using the autofs miscellanous device