xref: /openbmc/linux/security/selinux/ss/hashtab.c (revision 5794ed76)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Implementation of the hash table type.
31da177e4SLinus Torvalds  *
47efbb60bSStephen Smalley  * Author : Stephen Smalley, <sds@tycho.nsa.gov>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds #include <linux/kernel.h>
71da177e4SLinus Torvalds #include <linux/slab.h>
81da177e4SLinus Torvalds #include <linux/errno.h>
9ed1c9642SDave Jones #include <linux/sched.h>
101da177e4SLinus Torvalds #include "hashtab.h"
111da177e4SLinus Torvalds 
127c620eceSKyeongdon Kim static struct kmem_cache *hashtab_node_cachep;
137c620eceSKyeongdon Kim 
14bb242497SChad Sellers struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
15bb242497SChad Sellers 			       int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
161da177e4SLinus Torvalds 			       u32 size)
171da177e4SLinus Torvalds {
181da177e4SLinus Torvalds 	struct hashtab *p;
191da177e4SLinus Torvalds 	u32 i;
201da177e4SLinus Torvalds 
2189d155efSJames Morris 	p = kzalloc(sizeof(*p), GFP_KERNEL);
22cb8d21e3SMarkus Elfring 	if (!p)
231da177e4SLinus Torvalds 		return p;
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 	p->size = size;
261da177e4SLinus Torvalds 	p->nel = 0;
271da177e4SLinus Torvalds 	p->hash_value = hash_value;
281da177e4SLinus Torvalds 	p->keycmp = keycmp;
292f00e680SMarkus Elfring 	p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL);
30cb8d21e3SMarkus Elfring 	if (!p->htable) {
311da177e4SLinus Torvalds 		kfree(p);
321da177e4SLinus Torvalds 		return NULL;
331da177e4SLinus Torvalds 	}
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	for (i = 0; i < size; i++)
361da177e4SLinus Torvalds 		p->htable[i] = NULL;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 	return p;
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds int hashtab_insert(struct hashtab *h, void *key, void *datum)
421da177e4SLinus Torvalds {
431da177e4SLinus Torvalds 	u32 hvalue;
441da177e4SLinus Torvalds 	struct hashtab_node *prev, *cur, *newnode;
451da177e4SLinus Torvalds 
46ed1c9642SDave Jones 	cond_resched();
47ed1c9642SDave Jones 
481da177e4SLinus Torvalds 	if (!h || h->nel == HASHTAB_MAX_NODES)
491da177e4SLinus Torvalds 		return -EINVAL;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	hvalue = h->hash_value(h, key);
521da177e4SLinus Torvalds 	prev = NULL;
531da177e4SLinus Torvalds 	cur = h->htable[hvalue];
541da177e4SLinus Torvalds 	while (cur && h->keycmp(h, key, cur->key) > 0) {
551da177e4SLinus Torvalds 		prev = cur;
561da177e4SLinus Torvalds 		cur = cur->next;
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 	if (cur && (h->keycmp(h, key, cur->key) == 0))
601da177e4SLinus Torvalds 		return -EEXIST;
611da177e4SLinus Torvalds 
627c620eceSKyeongdon Kim 	newnode = kmem_cache_zalloc(hashtab_node_cachep, GFP_KERNEL);
63cb8d21e3SMarkus Elfring 	if (!newnode)
641da177e4SLinus Torvalds 		return -ENOMEM;
651da177e4SLinus Torvalds 	newnode->key = key;
661da177e4SLinus Torvalds 	newnode->datum = datum;
671da177e4SLinus Torvalds 	if (prev) {
681da177e4SLinus Torvalds 		newnode->next = prev->next;
691da177e4SLinus Torvalds 		prev->next = newnode;
701da177e4SLinus Torvalds 	} else {
711da177e4SLinus Torvalds 		newnode->next = h->htable[hvalue];
721da177e4SLinus Torvalds 		h->htable[hvalue] = newnode;
731da177e4SLinus Torvalds 	}
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	h->nel++;
761da177e4SLinus Torvalds 	return 0;
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
79bb242497SChad Sellers void *hashtab_search(struct hashtab *h, const void *key)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	u32 hvalue;
821da177e4SLinus Torvalds 	struct hashtab_node *cur;
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 	if (!h)
851da177e4SLinus Torvalds 		return NULL;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	hvalue = h->hash_value(h, key);
881da177e4SLinus Torvalds 	cur = h->htable[hvalue];
89dbc74c65SVesa-Matti Kari 	while (cur && h->keycmp(h, key, cur->key) > 0)
901da177e4SLinus Torvalds 		cur = cur->next;
911da177e4SLinus Torvalds 
92cb8d21e3SMarkus Elfring 	if (!cur || (h->keycmp(h, key, cur->key) != 0))
931da177e4SLinus Torvalds 		return NULL;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	return cur->datum;
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds void hashtab_destroy(struct hashtab *h)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	u32 i;
1011da177e4SLinus Torvalds 	struct hashtab_node *cur, *temp;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	if (!h)
1041da177e4SLinus Torvalds 		return;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	for (i = 0; i < h->size; i++) {
1071da177e4SLinus Torvalds 		cur = h->htable[i];
108dbc74c65SVesa-Matti Kari 		while (cur) {
1091da177e4SLinus Torvalds 			temp = cur;
1101da177e4SLinus Torvalds 			cur = cur->next;
1117c620eceSKyeongdon Kim 			kmem_cache_free(hashtab_node_cachep, temp);
1121da177e4SLinus Torvalds 		}
1131da177e4SLinus Torvalds 		h->htable[i] = NULL;
1141da177e4SLinus Torvalds 	}
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	kfree(h->htable);
1171da177e4SLinus Torvalds 	h->htable = NULL;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	kfree(h);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds int hashtab_map(struct hashtab *h,
1231da177e4SLinus Torvalds 		int (*apply)(void *k, void *d, void *args),
1241da177e4SLinus Torvalds 		void *args)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	u32 i;
1271da177e4SLinus Torvalds 	int ret;
1281da177e4SLinus Torvalds 	struct hashtab_node *cur;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	if (!h)
1311da177e4SLinus Torvalds 		return 0;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	for (i = 0; i < h->size; i++) {
1341da177e4SLinus Torvalds 		cur = h->htable[i];
135dbc74c65SVesa-Matti Kari 		while (cur) {
1361da177e4SLinus Torvalds 			ret = apply(cur->key, cur->datum, args);
1371da177e4SLinus Torvalds 			if (ret)
1381da177e4SLinus Torvalds 				return ret;
1391da177e4SLinus Torvalds 			cur = cur->next;
1401da177e4SLinus Torvalds 		}
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds 	return 0;
1431da177e4SLinus Torvalds }
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
1471da177e4SLinus Torvalds {
1481da177e4SLinus Torvalds 	u32 i, chain_len, slots_used, max_chain_len;
1491da177e4SLinus Torvalds 	struct hashtab_node *cur;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	slots_used = 0;
1521da177e4SLinus Torvalds 	max_chain_len = 0;
1535794ed76SColin Ian King 	for (i = 0; i < h->size; i++) {
1541da177e4SLinus Torvalds 		cur = h->htable[i];
1551da177e4SLinus Torvalds 		if (cur) {
1561da177e4SLinus Torvalds 			slots_used++;
1571da177e4SLinus Torvalds 			chain_len = 0;
1581da177e4SLinus Torvalds 			while (cur) {
1591da177e4SLinus Torvalds 				chain_len++;
1601da177e4SLinus Torvalds 				cur = cur->next;
1611da177e4SLinus Torvalds 			}
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 			if (chain_len > max_chain_len)
1641da177e4SLinus Torvalds 				max_chain_len = chain_len;
1651da177e4SLinus Torvalds 		}
1661da177e4SLinus Torvalds 	}
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	info->slots_used = slots_used;
1691da177e4SLinus Torvalds 	info->max_chain_len = max_chain_len;
1701da177e4SLinus Torvalds }
1717c620eceSKyeongdon Kim void hashtab_cache_init(void)
1727c620eceSKyeongdon Kim {
1737c620eceSKyeongdon Kim 		hashtab_node_cachep = kmem_cache_create("hashtab_node",
1747c620eceSKyeongdon Kim 			sizeof(struct hashtab_node),
1757c620eceSKyeongdon Kim 			0, SLAB_PANIC, NULL);
1767c620eceSKyeongdon Kim }
1777c620eceSKyeongdon Kim 
1787c620eceSKyeongdon Kim void hashtab_cache_destroy(void)
1797c620eceSKyeongdon Kim {
1807c620eceSKyeongdon Kim 		kmem_cache_destroy(hashtab_node_cachep);
1817c620eceSKyeongdon Kim }
182