xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_hashtab.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1*41ec0267Sriastradh /*	$NetBSD: drm_hashtab.c,v 1.3 2021/12/18 23:44:57 riastradh Exp $	*/
2efa246c0Sriastradh 
3fcd0cb28Sriastradh /**************************************************************************
4fcd0cb28Sriastradh  *
5fcd0cb28Sriastradh  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
6fcd0cb28Sriastradh  * All Rights Reserved.
7fcd0cb28Sriastradh  *
8fcd0cb28Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
9fcd0cb28Sriastradh  * copy of this software and associated documentation files (the
10fcd0cb28Sriastradh  * "Software"), to deal in the Software without restriction, including
11fcd0cb28Sriastradh  * without limitation the rights to use, copy, modify, merge, publish,
12fcd0cb28Sriastradh  * distribute, sub license, and/or sell copies of the Software, and to
13fcd0cb28Sriastradh  * permit persons to whom the Software is furnished to do so, subject to
14fcd0cb28Sriastradh  * the following conditions:
15fcd0cb28Sriastradh  *
16fcd0cb28Sriastradh  * The above copyright notice and this permission notice (including the
17fcd0cb28Sriastradh  * next paragraph) shall be included in all copies or substantial portions
18fcd0cb28Sriastradh  * of the Software.
19fcd0cb28Sriastradh  *
20fcd0cb28Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21fcd0cb28Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22fcd0cb28Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
23fcd0cb28Sriastradh  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
24fcd0cb28Sriastradh  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
25fcd0cb28Sriastradh  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
26fcd0cb28Sriastradh  * USE OR OTHER DEALINGS IN THE SOFTWARE.
27fcd0cb28Sriastradh  *
28fcd0cb28Sriastradh  *
29fcd0cb28Sriastradh  **************************************************************************/
30fcd0cb28Sriastradh /*
31fcd0cb28Sriastradh  * Simple open hash tab implementation.
32fcd0cb28Sriastradh  *
33fcd0cb28Sriastradh  * Authors:
34fcd0cb28Sriastradh  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
35fcd0cb28Sriastradh  */
36fcd0cb28Sriastradh 
37efa246c0Sriastradh #include <sys/cdefs.h>
38*41ec0267Sriastradh __KERNEL_RCSID(0, "$NetBSD: drm_hashtab.c,v 1.3 2021/12/18 23:44:57 riastradh Exp $");
39efa246c0Sriastradh 
40fcd0cb28Sriastradh #include <linux/export.h>
41*41ec0267Sriastradh #include <linux/hash.h>
42*41ec0267Sriastradh #include <linux/mm.h>
43*41ec0267Sriastradh #include <linux/rculist.h>
44*41ec0267Sriastradh #include <linux/slab.h>
45*41ec0267Sriastradh #include <linux/vmalloc.h>
46*41ec0267Sriastradh 
47*41ec0267Sriastradh #include <drm/drm_hashtab.h>
48*41ec0267Sriastradh #include <drm/drm_print.h>
49fcd0cb28Sriastradh 
drm_ht_create(struct drm_open_hash * ht,unsigned int order)50fcd0cb28Sriastradh int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
51fcd0cb28Sriastradh {
52fcd0cb28Sriastradh 	unsigned int size = 1 << order;
53fcd0cb28Sriastradh 
54fcd0cb28Sriastradh 	ht->order = order;
55fcd0cb28Sriastradh 	ht->table = NULL;
56fcd0cb28Sriastradh 	if (size <= PAGE_SIZE / sizeof(*ht->table))
57fcd0cb28Sriastradh 		ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
58fcd0cb28Sriastradh 	else
59*41ec0267Sriastradh 		ht->table = vzalloc(array_size(size, sizeof(*ht->table)));
60fcd0cb28Sriastradh 	if (!ht->table) {
61fcd0cb28Sriastradh 		DRM_ERROR("Out of memory for hash table\n");
62fcd0cb28Sriastradh 		return -ENOMEM;
63fcd0cb28Sriastradh 	}
64fcd0cb28Sriastradh 	return 0;
65fcd0cb28Sriastradh }
66fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_create);
67fcd0cb28Sriastradh 
drm_ht_verbose_list(struct drm_open_hash * ht,unsigned long key)68fcd0cb28Sriastradh void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
69fcd0cb28Sriastradh {
70fcd0cb28Sriastradh 	struct drm_hash_item *entry;
71fcd0cb28Sriastradh 	struct hlist_head *h_list;
72fcd0cb28Sriastradh 	unsigned int hashed_key;
73fcd0cb28Sriastradh 	int count = 0;
74fcd0cb28Sriastradh 
75fcd0cb28Sriastradh 	hashed_key = hash_long(key, ht->order);
76fcd0cb28Sriastradh 	DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
77fcd0cb28Sriastradh 	h_list = &ht->table[hashed_key];
789d20d926Sriastradh 	hlist_for_each_entry(entry, h_list, head)
79fcd0cb28Sriastradh 		DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
80fcd0cb28Sriastradh }
81fcd0cb28Sriastradh 
drm_ht_find_key(struct drm_open_hash * ht,unsigned long key)82fcd0cb28Sriastradh static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
83fcd0cb28Sriastradh 					  unsigned long key)
84fcd0cb28Sriastradh {
85fcd0cb28Sriastradh 	struct drm_hash_item *entry;
86fcd0cb28Sriastradh 	struct hlist_head *h_list;
87fcd0cb28Sriastradh 	unsigned int hashed_key;
88fcd0cb28Sriastradh 
89fcd0cb28Sriastradh 	hashed_key = hash_long(key, ht->order);
90fcd0cb28Sriastradh 	h_list = &ht->table[hashed_key];
919d20d926Sriastradh 	hlist_for_each_entry(entry, h_list, head) {
92fcd0cb28Sriastradh 		if (entry->key == key)
939d20d926Sriastradh 			return &entry->head;
94fcd0cb28Sriastradh 		if (entry->key > key)
95fcd0cb28Sriastradh 			break;
96fcd0cb28Sriastradh 	}
97fcd0cb28Sriastradh 	return NULL;
98fcd0cb28Sriastradh }
99fcd0cb28Sriastradh 
drm_ht_find_key_rcu(struct drm_open_hash * ht,unsigned long key)100fcd0cb28Sriastradh static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
101fcd0cb28Sriastradh 					      unsigned long key)
102fcd0cb28Sriastradh {
103fcd0cb28Sriastradh 	struct drm_hash_item *entry;
104fcd0cb28Sriastradh 	struct hlist_head *h_list;
105fcd0cb28Sriastradh 	unsigned int hashed_key;
106fcd0cb28Sriastradh 
107fcd0cb28Sriastradh 	hashed_key = hash_long(key, ht->order);
108fcd0cb28Sriastradh 	h_list = &ht->table[hashed_key];
1099d20d926Sriastradh 	hlist_for_each_entry_rcu(entry, h_list, head) {
110fcd0cb28Sriastradh 		if (entry->key == key)
1119d20d926Sriastradh 			return &entry->head;
112fcd0cb28Sriastradh 		if (entry->key > key)
113fcd0cb28Sriastradh 			break;
114fcd0cb28Sriastradh 	}
115fcd0cb28Sriastradh 	return NULL;
116fcd0cb28Sriastradh }
117fcd0cb28Sriastradh 
drm_ht_insert_item(struct drm_open_hash * ht,struct drm_hash_item * item)118fcd0cb28Sriastradh int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
119fcd0cb28Sriastradh {
120fcd0cb28Sriastradh 	struct drm_hash_item *entry;
121fcd0cb28Sriastradh 	struct hlist_head *h_list;
1229d20d926Sriastradh 	struct hlist_node *parent;
123fcd0cb28Sriastradh 	unsigned int hashed_key;
124fcd0cb28Sriastradh 	unsigned long key = item->key;
125fcd0cb28Sriastradh 
126fcd0cb28Sriastradh 	hashed_key = hash_long(key, ht->order);
127fcd0cb28Sriastradh 	h_list = &ht->table[hashed_key];
128fcd0cb28Sriastradh 	parent = NULL;
1299d20d926Sriastradh 	hlist_for_each_entry(entry, h_list, head) {
130fcd0cb28Sriastradh 		if (entry->key == key)
131fcd0cb28Sriastradh 			return -EINVAL;
132fcd0cb28Sriastradh 		if (entry->key > key)
133fcd0cb28Sriastradh 			break;
1349d20d926Sriastradh 		parent = &entry->head;
135fcd0cb28Sriastradh 	}
136fcd0cb28Sriastradh 	if (parent) {
137efa246c0Sriastradh 		hlist_add_behind_rcu(&item->head, parent);
138fcd0cb28Sriastradh 	} else {
139fcd0cb28Sriastradh 		hlist_add_head_rcu(&item->head, h_list);
140fcd0cb28Sriastradh 	}
141fcd0cb28Sriastradh 	return 0;
142fcd0cb28Sriastradh }
143fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_insert_item);
144fcd0cb28Sriastradh 
145fcd0cb28Sriastradh /*
146fcd0cb28Sriastradh  * Just insert an item and return any "bits" bit key that hasn't been
147fcd0cb28Sriastradh  * used before.
148fcd0cb28Sriastradh  */
drm_ht_just_insert_please(struct drm_open_hash * ht,struct drm_hash_item * item,unsigned long seed,int bits,int shift,unsigned long add)149fcd0cb28Sriastradh int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
150fcd0cb28Sriastradh 			      unsigned long seed, int bits, int shift,
151fcd0cb28Sriastradh 			      unsigned long add)
152fcd0cb28Sriastradh {
153fcd0cb28Sriastradh 	int ret;
154*41ec0267Sriastradh 	unsigned long mask = (1UL << bits) - 1;
155fcd0cb28Sriastradh 	unsigned long first, unshifted_key;
156fcd0cb28Sriastradh 
157fcd0cb28Sriastradh 	unshifted_key = hash_long(seed, bits);
158fcd0cb28Sriastradh 	first = unshifted_key;
159fcd0cb28Sriastradh 	do {
160fcd0cb28Sriastradh 		item->key = (unshifted_key << shift) + add;
161fcd0cb28Sriastradh 		ret = drm_ht_insert_item(ht, item);
162fcd0cb28Sriastradh 		if (ret)
163fcd0cb28Sriastradh 			unshifted_key = (unshifted_key + 1) & mask;
164fcd0cb28Sriastradh 	} while(ret && (unshifted_key != first));
165fcd0cb28Sriastradh 
166fcd0cb28Sriastradh 	if (ret) {
167fcd0cb28Sriastradh 		DRM_ERROR("Available key bit space exhausted\n");
168fcd0cb28Sriastradh 		return -EINVAL;
169fcd0cb28Sriastradh 	}
170fcd0cb28Sriastradh 	return 0;
171fcd0cb28Sriastradh }
172fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_just_insert_please);
173fcd0cb28Sriastradh 
drm_ht_find_item(struct drm_open_hash * ht,unsigned long key,struct drm_hash_item ** item)174fcd0cb28Sriastradh int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
175fcd0cb28Sriastradh 		     struct drm_hash_item **item)
176fcd0cb28Sriastradh {
177fcd0cb28Sriastradh 	struct hlist_node *list;
178fcd0cb28Sriastradh 
179fcd0cb28Sriastradh 	list = drm_ht_find_key_rcu(ht, key);
180fcd0cb28Sriastradh 	if (!list)
181fcd0cb28Sriastradh 		return -EINVAL;
182fcd0cb28Sriastradh 
183fcd0cb28Sriastradh 	*item = hlist_entry(list, struct drm_hash_item, head);
184fcd0cb28Sriastradh 	return 0;
185fcd0cb28Sriastradh }
186fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_find_item);
187fcd0cb28Sriastradh 
drm_ht_remove_key(struct drm_open_hash * ht,unsigned long key)188fcd0cb28Sriastradh int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
189fcd0cb28Sriastradh {
190fcd0cb28Sriastradh 	struct hlist_node *list;
191fcd0cb28Sriastradh 
192fcd0cb28Sriastradh 	list = drm_ht_find_key(ht, key);
193fcd0cb28Sriastradh 	if (list) {
194fcd0cb28Sriastradh 		hlist_del_init_rcu(list);
195fcd0cb28Sriastradh 		return 0;
196fcd0cb28Sriastradh 	}
197fcd0cb28Sriastradh 	return -EINVAL;
198fcd0cb28Sriastradh }
199fcd0cb28Sriastradh 
drm_ht_remove_item(struct drm_open_hash * ht,struct drm_hash_item * item)200fcd0cb28Sriastradh int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
201fcd0cb28Sriastradh {
202fcd0cb28Sriastradh 	hlist_del_init_rcu(&item->head);
203fcd0cb28Sriastradh 	return 0;
204fcd0cb28Sriastradh }
205fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_remove_item);
206fcd0cb28Sriastradh 
drm_ht_remove(struct drm_open_hash * ht)207fcd0cb28Sriastradh void drm_ht_remove(struct drm_open_hash *ht)
208fcd0cb28Sriastradh {
209fcd0cb28Sriastradh 	if (ht->table) {
210*41ec0267Sriastradh 		kvfree(ht->table);
211fcd0cb28Sriastradh 		ht->table = NULL;
212fcd0cb28Sriastradh 	}
213fcd0cb28Sriastradh }
214fcd0cb28Sriastradh EXPORT_SYMBOL(drm_ht_remove);
215