18c2ecf20Sopenharmony_ci/**************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
48c2ecf20Sopenharmony_ci * All Rights Reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the
88c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
98c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
108c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
118c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
128c2ecf20Sopenharmony_ci * the following conditions:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
158c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
168c2ecf20Sopenharmony_ci * of the Software.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
198c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
208c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
218c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
228c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
238c2ecf20Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
248c2ecf20Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci **************************************************************************/
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * Simple open hash tab implementation.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Authors:
328c2ecf20Sopenharmony_ci * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/export.h>
368c2ecf20Sopenharmony_ci#include <linux/hash.h>
378c2ecf20Sopenharmony_ci#include <linux/mm.h>
388c2ecf20Sopenharmony_ci#include <linux/rculist.h>
398c2ecf20Sopenharmony_ci#include <linux/slab.h>
408c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <drm/drm_hashtab.h>
438c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint drm_ht_create(struct drm_open_hash *ht, unsigned int order)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	unsigned int size = 1 << order;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ht->order = order;
508c2ecf20Sopenharmony_ci	ht->table = NULL;
518c2ecf20Sopenharmony_ci	if (size <= PAGE_SIZE / sizeof(*ht->table))
528c2ecf20Sopenharmony_ci		ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
538c2ecf20Sopenharmony_ci	else
548c2ecf20Sopenharmony_ci		ht->table = vzalloc(array_size(size, sizeof(*ht->table)));
558c2ecf20Sopenharmony_ci	if (!ht->table) {
568c2ecf20Sopenharmony_ci		DRM_ERROR("Out of memory for hash table\n");
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_create);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_civoid drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct drm_hash_item *entry;
668c2ecf20Sopenharmony_ci	struct hlist_head *h_list;
678c2ecf20Sopenharmony_ci	unsigned int hashed_key;
688c2ecf20Sopenharmony_ci	int count = 0;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	hashed_key = hash_long(key, ht->order);
718c2ecf20Sopenharmony_ci	DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
728c2ecf20Sopenharmony_ci	h_list = &ht->table[hashed_key];
738c2ecf20Sopenharmony_ci	hlist_for_each_entry(entry, h_list, head)
748c2ecf20Sopenharmony_ci		DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
788c2ecf20Sopenharmony_ci					  unsigned long key)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct drm_hash_item *entry;
818c2ecf20Sopenharmony_ci	struct hlist_head *h_list;
828c2ecf20Sopenharmony_ci	unsigned int hashed_key;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	hashed_key = hash_long(key, ht->order);
858c2ecf20Sopenharmony_ci	h_list = &ht->table[hashed_key];
868c2ecf20Sopenharmony_ci	hlist_for_each_entry(entry, h_list, head) {
878c2ecf20Sopenharmony_ci		if (entry->key == key)
888c2ecf20Sopenharmony_ci			return &entry->head;
898c2ecf20Sopenharmony_ci		if (entry->key > key)
908c2ecf20Sopenharmony_ci			break;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	return NULL;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
968c2ecf20Sopenharmony_ci					      unsigned long key)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct drm_hash_item *entry;
998c2ecf20Sopenharmony_ci	struct hlist_head *h_list;
1008c2ecf20Sopenharmony_ci	unsigned int hashed_key;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	hashed_key = hash_long(key, ht->order);
1038c2ecf20Sopenharmony_ci	h_list = &ht->table[hashed_key];
1048c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(entry, h_list, head) {
1058c2ecf20Sopenharmony_ci		if (entry->key == key)
1068c2ecf20Sopenharmony_ci			return &entry->head;
1078c2ecf20Sopenharmony_ci		if (entry->key > key)
1088c2ecf20Sopenharmony_ci			break;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	return NULL;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciint drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct drm_hash_item *entry;
1168c2ecf20Sopenharmony_ci	struct hlist_head *h_list;
1178c2ecf20Sopenharmony_ci	struct hlist_node *parent;
1188c2ecf20Sopenharmony_ci	unsigned int hashed_key;
1198c2ecf20Sopenharmony_ci	unsigned long key = item->key;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	hashed_key = hash_long(key, ht->order);
1228c2ecf20Sopenharmony_ci	h_list = &ht->table[hashed_key];
1238c2ecf20Sopenharmony_ci	parent = NULL;
1248c2ecf20Sopenharmony_ci	hlist_for_each_entry(entry, h_list, head) {
1258c2ecf20Sopenharmony_ci		if (entry->key == key)
1268c2ecf20Sopenharmony_ci			return -EINVAL;
1278c2ecf20Sopenharmony_ci		if (entry->key > key)
1288c2ecf20Sopenharmony_ci			break;
1298c2ecf20Sopenharmony_ci		parent = &entry->head;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	if (parent) {
1328c2ecf20Sopenharmony_ci		hlist_add_behind_rcu(&item->head, parent);
1338c2ecf20Sopenharmony_ci	} else {
1348c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&item->head, h_list);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_insert_item);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Just insert an item and return any "bits" bit key that hasn't been
1428c2ecf20Sopenharmony_ci * used before.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_ciint drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
1458c2ecf20Sopenharmony_ci			      unsigned long seed, int bits, int shift,
1468c2ecf20Sopenharmony_ci			      unsigned long add)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int ret;
1498c2ecf20Sopenharmony_ci	unsigned long mask = (1UL << bits) - 1;
1508c2ecf20Sopenharmony_ci	unsigned long first, unshifted_key;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	unshifted_key = hash_long(seed, bits);
1538c2ecf20Sopenharmony_ci	first = unshifted_key;
1548c2ecf20Sopenharmony_ci	do {
1558c2ecf20Sopenharmony_ci		item->key = (unshifted_key << shift) + add;
1568c2ecf20Sopenharmony_ci		ret = drm_ht_insert_item(ht, item);
1578c2ecf20Sopenharmony_ci		if (ret)
1588c2ecf20Sopenharmony_ci			unshifted_key = (unshifted_key + 1) & mask;
1598c2ecf20Sopenharmony_ci	} while(ret && (unshifted_key != first));
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (ret) {
1628c2ecf20Sopenharmony_ci		DRM_ERROR("Available key bit space exhausted\n");
1638c2ecf20Sopenharmony_ci		return -EINVAL;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_just_insert_please);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciint drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
1708c2ecf20Sopenharmony_ci		     struct drm_hash_item **item)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct hlist_node *list;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	list = drm_ht_find_key_rcu(ht, key);
1758c2ecf20Sopenharmony_ci	if (!list)
1768c2ecf20Sopenharmony_ci		return -EINVAL;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	*item = hlist_entry(list, struct drm_hash_item, head);
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_find_item);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciint drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct hlist_node *list;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	list = drm_ht_find_key(ht, key);
1888c2ecf20Sopenharmony_ci	if (list) {
1898c2ecf20Sopenharmony_ci		hlist_del_init_rcu(list);
1908c2ecf20Sopenharmony_ci		return 0;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci	return -EINVAL;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciint drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	hlist_del_init_rcu(&item->head);
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_remove_item);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_civoid drm_ht_remove(struct drm_open_hash *ht)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	if (ht->table) {
2058c2ecf20Sopenharmony_ci		kvfree(ht->table);
2068c2ecf20Sopenharmony_ci		ht->table = NULL;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_ht_remove);
210