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