xref: /dflybsd-src/sys/dev/drm/drm_hashtab.c (revision 9edbd4a07c3138f5c4f076f77de5d722fcc606cc)
13f6063ccSDavid Shao /**************************************************************************
23f6063ccSDavid Shao  *
33f6063ccSDavid Shao  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
43f6063ccSDavid Shao  * All Rights Reserved.
53f6063ccSDavid Shao  *
63f6063ccSDavid Shao  * Permission is hereby granted, free of charge, to any person obtaining a
73f6063ccSDavid Shao  * copy of this software and associated documentation files (the
83f6063ccSDavid Shao  * "Software"), to deal in the Software without restriction, including
93f6063ccSDavid Shao  * without limitation the rights to use, copy, modify, merge, publish,
103f6063ccSDavid Shao  * distribute, sub license, and/or sell copies of the Software, and to
113f6063ccSDavid Shao  * permit persons to whom the Software is furnished to do so, subject to
123f6063ccSDavid Shao  * the following conditions:
133f6063ccSDavid Shao  *
143f6063ccSDavid Shao  * The above copyright notice and this permission notice (including the
153f6063ccSDavid Shao  * next paragraph) shall be included in all copies or substantial portions
163f6063ccSDavid Shao  * of the Software.
173f6063ccSDavid Shao  *
183f6063ccSDavid Shao  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193f6063ccSDavid Shao  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203f6063ccSDavid Shao  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
213f6063ccSDavid Shao  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
223f6063ccSDavid Shao  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
233f6063ccSDavid Shao  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
243f6063ccSDavid Shao  * USE OR OTHER DEALINGS IN THE SOFTWARE.
253f6063ccSDavid Shao  *
263f6063ccSDavid Shao  *
273f6063ccSDavid Shao  **************************************************************************/
283f6063ccSDavid Shao /*
293f6063ccSDavid Shao  * Simple open hash tab implementation.
303f6063ccSDavid Shao  *
313f6063ccSDavid Shao  * Authors:
323f6063ccSDavid Shao  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
333f6063ccSDavid Shao  */
343f6063ccSDavid Shao 
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_hashtab.h>
376e7a447aSFrançois Tigeot #include <linux/hash.h>
38*9edbd4a0SFrançois Tigeot #include <linux/slab.h>
396e7a447aSFrançois Tigeot #include <linux/export.h>
403f6063ccSDavid Shao 
413f6063ccSDavid Shao int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
423f6063ccSDavid Shao {
436e7a447aSFrançois Tigeot 	unsigned int size = 1 << order;
446e7a447aSFrançois Tigeot 
453f6063ccSDavid Shao 	ht->order = order;
463f6063ccSDavid Shao 	ht->table = NULL;
47*9edbd4a0SFrançois Tigeot 	if (size <= PAGE_SIZE / sizeof(*ht->table))
48*9edbd4a0SFrançois Tigeot 		ht->table = kzalloc(size*sizeof(*ht->table), GFP_KERNEL);
49*9edbd4a0SFrançois Tigeot 	else
50*9edbd4a0SFrançois Tigeot 		ht->table = kzalloc(size*sizeof(*ht->table), GFP_KERNEL);
513f6063ccSDavid Shao 	if (!ht->table) {
523f6063ccSDavid Shao 		DRM_ERROR("Out of memory for hash table\n");
533f6063ccSDavid Shao 		return -ENOMEM;
543f6063ccSDavid Shao 	}
553f6063ccSDavid Shao 	return 0;
563f6063ccSDavid Shao }
576e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_create);
583f6063ccSDavid Shao 
593f6063ccSDavid Shao void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
603f6063ccSDavid Shao {
613f6063ccSDavid Shao 	struct drm_hash_item *entry;
626e7a447aSFrançois Tigeot 	struct hlist_head *h_list;
636e7a447aSFrançois Tigeot 	struct hlist_node *list;
643f6063ccSDavid Shao 	unsigned int hashed_key;
653f6063ccSDavid Shao 	int count = 0;
663f6063ccSDavid Shao 
676e7a447aSFrançois Tigeot 	hashed_key = hash_long(key, ht->order);
683f6063ccSDavid Shao 	DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
696e7a447aSFrançois Tigeot 	h_list = &ht->table[hashed_key];
706e7a447aSFrançois Tigeot 	hlist_for_each_entry(entry, list, h_list, head)
713f6063ccSDavid Shao 		DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
723f6063ccSDavid Shao }
733f6063ccSDavid Shao 
746e7a447aSFrançois Tigeot static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
756e7a447aSFrançois Tigeot 					  unsigned long key)
763f6063ccSDavid Shao {
773f6063ccSDavid Shao 	struct drm_hash_item *entry;
786e7a447aSFrançois Tigeot 	struct hlist_head *h_list;
796e7a447aSFrançois Tigeot 	struct hlist_node *list;
803f6063ccSDavid Shao 	unsigned int hashed_key;
813f6063ccSDavid Shao 
826e7a447aSFrançois Tigeot 	hashed_key = hash_long(key, ht->order);
836e7a447aSFrançois Tigeot 	h_list = &ht->table[hashed_key];
846e7a447aSFrançois Tigeot 	hlist_for_each_entry(entry, list, h_list, head) {
853f6063ccSDavid Shao 		if (entry->key == key)
866e7a447aSFrançois Tigeot 			return list;
873f6063ccSDavid Shao 		if (entry->key > key)
883f6063ccSDavid Shao 			break;
893f6063ccSDavid Shao 	}
903f6063ccSDavid Shao 	return NULL;
913f6063ccSDavid Shao }
923f6063ccSDavid Shao 
936e7a447aSFrançois Tigeot static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
946e7a447aSFrançois Tigeot 					      unsigned long key)
956e7a447aSFrançois Tigeot {
966e7a447aSFrançois Tigeot 	struct drm_hash_item *entry;
976e7a447aSFrançois Tigeot 	struct hlist_head *h_list;
986e7a447aSFrançois Tigeot 	struct hlist_node *list;
996e7a447aSFrançois Tigeot 	unsigned int hashed_key;
1006e7a447aSFrançois Tigeot 
1016e7a447aSFrançois Tigeot 	hashed_key = hash_long(key, ht->order);
1026e7a447aSFrançois Tigeot 	h_list = &ht->table[hashed_key];
1036e7a447aSFrançois Tigeot 	hlist_for_each_entry_rcu(entry, list, h_list, head) {
1046e7a447aSFrançois Tigeot 		if (entry->key == key)
1056e7a447aSFrançois Tigeot 			return list;
1066e7a447aSFrançois Tigeot 		if (entry->key > key)
1076e7a447aSFrançois Tigeot 			break;
1086e7a447aSFrançois Tigeot 	}
1096e7a447aSFrançois Tigeot 	return NULL;
1106e7a447aSFrançois Tigeot }
1113f6063ccSDavid Shao 
1123f6063ccSDavid Shao int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1133f6063ccSDavid Shao {
1146e7a447aSFrançois Tigeot 	struct drm_hash_item *entry;
1156e7a447aSFrançois Tigeot 	struct hlist_head *h_list;
1166e7a447aSFrançois Tigeot 	struct hlist_node *list, *parent;
1173f6063ccSDavid Shao 	unsigned int hashed_key;
1183f6063ccSDavid Shao 	unsigned long key = item->key;
1193f6063ccSDavid Shao 
1206e7a447aSFrançois Tigeot 	hashed_key = hash_long(key, ht->order);
1216e7a447aSFrançois Tigeot 	h_list = &ht->table[hashed_key];
1223f6063ccSDavid Shao 	parent = NULL;
1236e7a447aSFrançois Tigeot 	hlist_for_each_entry(entry, list, h_list, head) {
1243f6063ccSDavid Shao 		if (entry->key == key)
1253f6063ccSDavid Shao 			return -EINVAL;
1263f6063ccSDavid Shao 		if (entry->key > key)
1273f6063ccSDavid Shao 			break;
1286e7a447aSFrançois Tigeot 		parent = list;
1293f6063ccSDavid Shao 	}
1303f6063ccSDavid Shao 	if (parent) {
1316e7a447aSFrançois Tigeot 		hlist_add_after_rcu(parent, &item->head);
1323f6063ccSDavid Shao 	} else {
1336e7a447aSFrançois Tigeot 		hlist_add_head_rcu(&item->head, h_list);
1343f6063ccSDavid Shao 	}
1353f6063ccSDavid Shao 	return 0;
1363f6063ccSDavid Shao }
1376e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_insert_item);
1383f6063ccSDavid Shao 
1393f6063ccSDavid Shao /*
1403f6063ccSDavid Shao  * Just insert an item and return any "bits" bit key that hasn't been
1413f6063ccSDavid Shao  * used before.
1423f6063ccSDavid Shao  */
1433f6063ccSDavid Shao int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
1443f6063ccSDavid Shao 			      unsigned long seed, int bits, int shift,
1453f6063ccSDavid Shao 			      unsigned long add)
1463f6063ccSDavid Shao {
1473f6063ccSDavid Shao 	int ret;
1483f6063ccSDavid Shao 	unsigned long mask = (1 << bits) - 1;
149*9edbd4a0SFrançois Tigeot 	unsigned long first, unshifted_key;
1503f6063ccSDavid Shao 
1516e7a447aSFrançois Tigeot 	unshifted_key = hash_long(seed, bits);
1523f6063ccSDavid Shao 	first = unshifted_key;
1533f6063ccSDavid Shao 	do {
1543f6063ccSDavid Shao 		item->key = (unshifted_key << shift) + add;
1553f6063ccSDavid Shao 		ret = drm_ht_insert_item(ht, item);
1563f6063ccSDavid Shao 		if (ret)
1573f6063ccSDavid Shao 			unshifted_key = (unshifted_key + 1) & mask;
1583f6063ccSDavid Shao 	} while(ret && (unshifted_key != first));
1593f6063ccSDavid Shao 
1603f6063ccSDavid Shao 	if (ret) {
1613f6063ccSDavid Shao 		DRM_ERROR("Available key bit space exhausted\n");
1623f6063ccSDavid Shao 		return -EINVAL;
1633f6063ccSDavid Shao 	}
1643f6063ccSDavid Shao 	return 0;
1653f6063ccSDavid Shao }
1666e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_just_insert_please);
1673f6063ccSDavid Shao 
1683f6063ccSDavid Shao int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
1693f6063ccSDavid Shao 		     struct drm_hash_item **item)
1703f6063ccSDavid Shao {
1716e7a447aSFrançois Tigeot 	struct hlist_node *list;
1723f6063ccSDavid Shao 
1736e7a447aSFrançois Tigeot 	list = drm_ht_find_key_rcu(ht, key);
1746e7a447aSFrançois Tigeot 	if (!list)
1753f6063ccSDavid Shao 		return -EINVAL;
1763f6063ccSDavid Shao 
1776e7a447aSFrançois Tigeot 	*item = hlist_entry(list, struct drm_hash_item, head);
1783f6063ccSDavid Shao 	return 0;
1793f6063ccSDavid Shao }
1806e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_find_item);
1813f6063ccSDavid Shao 
1823f6063ccSDavid Shao int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
1833f6063ccSDavid Shao {
1846e7a447aSFrançois Tigeot 	struct hlist_node *list;
1853f6063ccSDavid Shao 
1866e7a447aSFrançois Tigeot 	list = drm_ht_find_key(ht, key);
1876e7a447aSFrançois Tigeot 	if (list) {
1886e7a447aSFrançois Tigeot 		hlist_del_init_rcu(list);
1893f6063ccSDavid Shao 		return 0;
1903f6063ccSDavid Shao 	}
1913f6063ccSDavid Shao 	return -EINVAL;
1923f6063ccSDavid Shao }
1933f6063ccSDavid Shao 
1943f6063ccSDavid Shao int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1953f6063ccSDavid Shao {
1966e7a447aSFrançois Tigeot 	hlist_del_init_rcu(&item->head);
1973f6063ccSDavid Shao 	return 0;
1983f6063ccSDavid Shao }
1996e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_remove_item);
2003f6063ccSDavid Shao 
2013f6063ccSDavid Shao void drm_ht_remove(struct drm_open_hash *ht)
2023f6063ccSDavid Shao {
2033f6063ccSDavid Shao 	if (ht->table) {
204*9edbd4a0SFrançois Tigeot 		if ((PAGE_SIZE / sizeof(*ht->table)) >> ht->order)
205*9edbd4a0SFrançois Tigeot 			kfree(ht->table);
206*9edbd4a0SFrançois Tigeot 		else
207158486a6SFrançois Tigeot 			kfree(ht->table);
2083f6063ccSDavid Shao 		ht->table = NULL;
2093f6063ccSDavid Shao 	}
2103f6063ccSDavid Shao }
2116e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_remove);
212