xref: /openbmc/linux/drivers/gpu/drm/drm_hashtab.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1c0e09200SDave Airlie /**************************************************************************
2c0e09200SDave Airlie  *
3c0e09200SDave Airlie  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
4c0e09200SDave Airlie  * All Rights Reserved.
5c0e09200SDave Airlie  *
6c0e09200SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
7c0e09200SDave Airlie  * copy of this software and associated documentation files (the
8c0e09200SDave Airlie  * "Software"), to deal in the Software without restriction, including
9c0e09200SDave Airlie  * without limitation the rights to use, copy, modify, merge, publish,
10c0e09200SDave Airlie  * distribute, sub license, and/or sell copies of the Software, and to
11c0e09200SDave Airlie  * permit persons to whom the Software is furnished to do so, subject to
12c0e09200SDave Airlie  * the following conditions:
13c0e09200SDave Airlie  *
14c0e09200SDave Airlie  * The above copyright notice and this permission notice (including the
15c0e09200SDave Airlie  * next paragraph) shall be included in all copies or substantial portions
16c0e09200SDave Airlie  * of the Software.
17c0e09200SDave Airlie  *
18c0e09200SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19c0e09200SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20c0e09200SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21c0e09200SDave Airlie  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22c0e09200SDave Airlie  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23c0e09200SDave Airlie  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24c0e09200SDave Airlie  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25c0e09200SDave Airlie  *
26c0e09200SDave Airlie  *
27c0e09200SDave Airlie  **************************************************************************/
28c0e09200SDave Airlie /*
29c0e09200SDave Airlie  * Simple open hash tab implementation.
30c0e09200SDave Airlie  *
31c0e09200SDave Airlie  * Authors:
32c0e09200SDave Airlie  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
33c0e09200SDave Airlie  */
34c0e09200SDave Airlie 
350500c04eSSam Ravnborg #include <linux/hash.h>
360500c04eSSam Ravnborg #include <linux/mm.h>
370500c04eSSam Ravnborg #include <linux/rculist.h>
380500c04eSSam Ravnborg #include <linux/slab.h>
390500c04eSSam Ravnborg #include <linux/vmalloc.h>
400500c04eSSam Ravnborg 
410500c04eSSam Ravnborg #include <drm/drm_print.h>
42c0e09200SDave Airlie 
43*a21800bcSThomas Zimmermann #include "drm_legacy.h"
44*a21800bcSThomas Zimmermann 
drm_ht_create(struct drm_open_hash * ht,unsigned int order)45c0e09200SDave Airlie int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
46c0e09200SDave Airlie {
477811bddbSChris Wilson 	unsigned int size = 1 << order;
48c0e09200SDave Airlie 
49c0e09200SDave Airlie 	ht->order = order;
50c0e09200SDave Airlie 	ht->table = NULL;
517811bddbSChris Wilson 	if (size <= PAGE_SIZE / sizeof(*ht->table))
527811bddbSChris Wilson 		ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
537811bddbSChris Wilson 	else
54fad953ceSKees Cook 		ht->table = vzalloc(array_size(size, sizeof(*ht->table)));
55c0e09200SDave Airlie 	if (!ht->table) {
56c0e09200SDave Airlie 		DRM_ERROR("Out of memory for hash table\n");
57c0e09200SDave Airlie 		return -ENOMEM;
58c0e09200SDave Airlie 	}
59c0e09200SDave Airlie 	return 0;
60c0e09200SDave Airlie }
61c0e09200SDave Airlie 
drm_ht_verbose_list(struct drm_open_hash * ht,unsigned long key)62c0e09200SDave Airlie void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
63c0e09200SDave Airlie {
64c0e09200SDave Airlie 	struct drm_hash_item *entry;
65c0e09200SDave Airlie 	struct hlist_head *h_list;
66c0e09200SDave Airlie 	unsigned int hashed_key;
67c0e09200SDave Airlie 	int count = 0;
68c0e09200SDave Airlie 
69c0e09200SDave Airlie 	hashed_key = hash_long(key, ht->order);
70c0e09200SDave Airlie 	DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
71c0e09200SDave Airlie 	h_list = &ht->table[hashed_key];
72b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, h_list, head)
73c0e09200SDave Airlie 		DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
74c0e09200SDave Airlie }
75c0e09200SDave Airlie 
drm_ht_find_key(struct drm_open_hash * ht,unsigned long key)76c0e09200SDave Airlie static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
77c0e09200SDave Airlie 					  unsigned long key)
78c0e09200SDave Airlie {
79c0e09200SDave Airlie 	struct drm_hash_item *entry;
80c0e09200SDave Airlie 	struct hlist_head *h_list;
81c0e09200SDave Airlie 	unsigned int hashed_key;
82c0e09200SDave Airlie 
83c0e09200SDave Airlie 	hashed_key = hash_long(key, ht->order);
84c0e09200SDave Airlie 	h_list = &ht->table[hashed_key];
85b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, h_list, head) {
86384cc2f9SThomas Hellstrom 		if (entry->key == key)
87b67bfe0dSSasha Levin 			return &entry->head;
88384cc2f9SThomas Hellstrom 		if (entry->key > key)
89384cc2f9SThomas Hellstrom 			break;
90384cc2f9SThomas Hellstrom 	}
91384cc2f9SThomas Hellstrom 	return NULL;
92384cc2f9SThomas Hellstrom }
93384cc2f9SThomas Hellstrom 
drm_ht_find_key_rcu(struct drm_open_hash * ht,unsigned long key)94384cc2f9SThomas Hellstrom static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
95384cc2f9SThomas Hellstrom 					      unsigned long key)
96384cc2f9SThomas Hellstrom {
97384cc2f9SThomas Hellstrom 	struct drm_hash_item *entry;
98384cc2f9SThomas Hellstrom 	struct hlist_head *h_list;
99384cc2f9SThomas Hellstrom 	unsigned int hashed_key;
100384cc2f9SThomas Hellstrom 
101384cc2f9SThomas Hellstrom 	hashed_key = hash_long(key, ht->order);
102384cc2f9SThomas Hellstrom 	h_list = &ht->table[hashed_key];
103b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(entry, h_list, head) {
104c0e09200SDave Airlie 		if (entry->key == key)
105b67bfe0dSSasha Levin 			return &entry->head;
106c0e09200SDave Airlie 		if (entry->key > key)
107c0e09200SDave Airlie 			break;
108c0e09200SDave Airlie 	}
109c0e09200SDave Airlie 	return NULL;
110c0e09200SDave Airlie }
111c0e09200SDave Airlie 
drm_ht_insert_item(struct drm_open_hash * ht,struct drm_hash_item * item)112c0e09200SDave Airlie int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
113c0e09200SDave Airlie {
114c0e09200SDave Airlie 	struct drm_hash_item *entry;
115c0e09200SDave Airlie 	struct hlist_head *h_list;
116b67bfe0dSSasha Levin 	struct hlist_node *parent;
117c0e09200SDave Airlie 	unsigned int hashed_key;
118c0e09200SDave Airlie 	unsigned long key = item->key;
119c0e09200SDave Airlie 
120c0e09200SDave Airlie 	hashed_key = hash_long(key, ht->order);
121c0e09200SDave Airlie 	h_list = &ht->table[hashed_key];
122c0e09200SDave Airlie 	parent = NULL;
123b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, h_list, head) {
124c0e09200SDave Airlie 		if (entry->key == key)
125c0e09200SDave Airlie 			return -EINVAL;
126c0e09200SDave Airlie 		if (entry->key > key)
127c0e09200SDave Airlie 			break;
128b67bfe0dSSasha Levin 		parent = &entry->head;
129c0e09200SDave Airlie 	}
130c0e09200SDave Airlie 	if (parent) {
1311d023284SKen Helias 		hlist_add_behind_rcu(&item->head, parent);
132c0e09200SDave Airlie 	} else {
133d7144556SThomas Hellstrom 		hlist_add_head_rcu(&item->head, h_list);
134c0e09200SDave Airlie 	}
135c0e09200SDave Airlie 	return 0;
136c0e09200SDave Airlie }
137c0e09200SDave Airlie 
138c0e09200SDave Airlie /*
139c0e09200SDave Airlie  * Just insert an item and return any "bits" bit key that hasn't been
140c0e09200SDave Airlie  * used before.
141c0e09200SDave Airlie  */
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)142c0e09200SDave Airlie int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
143c0e09200SDave Airlie 			      unsigned long seed, int bits, int shift,
144c0e09200SDave Airlie 			      unsigned long add)
145c0e09200SDave Airlie {
146c0e09200SDave Airlie 	int ret;
147ae0119f5SXie XiuQi 	unsigned long mask = (1UL << bits) - 1;
148c0e09200SDave Airlie 	unsigned long first, unshifted_key;
149c0e09200SDave Airlie 
150c0e09200SDave Airlie 	unshifted_key = hash_long(seed, bits);
151c0e09200SDave Airlie 	first = unshifted_key;
152c0e09200SDave Airlie 	do {
153c0e09200SDave Airlie 		item->key = (unshifted_key << shift) + add;
154c0e09200SDave Airlie 		ret = drm_ht_insert_item(ht, item);
155c0e09200SDave Airlie 		if (ret)
156c0e09200SDave Airlie 			unshifted_key = (unshifted_key + 1) & mask;
157c0e09200SDave Airlie 	} while(ret && (unshifted_key != first));
158c0e09200SDave Airlie 
159c0e09200SDave Airlie 	if (ret) {
160c0e09200SDave Airlie 		DRM_ERROR("Available key bit space exhausted\n");
161c0e09200SDave Airlie 		return -EINVAL;
162c0e09200SDave Airlie 	}
163c0e09200SDave Airlie 	return 0;
164c0e09200SDave Airlie }
165c0e09200SDave Airlie 
drm_ht_find_item(struct drm_open_hash * ht,unsigned long key,struct drm_hash_item ** item)166c0e09200SDave Airlie int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
167c0e09200SDave Airlie 		     struct drm_hash_item **item)
168c0e09200SDave Airlie {
169c0e09200SDave Airlie 	struct hlist_node *list;
170c0e09200SDave Airlie 
171384cc2f9SThomas Hellstrom 	list = drm_ht_find_key_rcu(ht, key);
172c0e09200SDave Airlie 	if (!list)
173c0e09200SDave Airlie 		return -EINVAL;
174c0e09200SDave Airlie 
175c0e09200SDave Airlie 	*item = hlist_entry(list, struct drm_hash_item, head);
176c0e09200SDave Airlie 	return 0;
177c0e09200SDave Airlie }
178c0e09200SDave Airlie 
drm_ht_remove_key(struct drm_open_hash * ht,unsigned long key)179c0e09200SDave Airlie int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
180c0e09200SDave Airlie {
181c0e09200SDave Airlie 	struct hlist_node *list;
182c0e09200SDave Airlie 
183c0e09200SDave Airlie 	list = drm_ht_find_key(ht, key);
184c0e09200SDave Airlie 	if (list) {
185d7144556SThomas Hellstrom 		hlist_del_init_rcu(list);
186c0e09200SDave Airlie 		return 0;
187c0e09200SDave Airlie 	}
188c0e09200SDave Airlie 	return -EINVAL;
189c0e09200SDave Airlie }
190c0e09200SDave Airlie 
drm_ht_remove_item(struct drm_open_hash * ht,struct drm_hash_item * item)191c0e09200SDave Airlie int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
192c0e09200SDave Airlie {
193d7144556SThomas Hellstrom 	hlist_del_init_rcu(&item->head);
194c0e09200SDave Airlie 	return 0;
195c0e09200SDave Airlie }
196c0e09200SDave Airlie 
drm_ht_remove(struct drm_open_hash * ht)197c0e09200SDave Airlie void drm_ht_remove(struct drm_open_hash *ht)
198c0e09200SDave Airlie {
199c0e09200SDave Airlie 	if (ht->table) {
2001d5cfdb0STetsuo Handa 		kvfree(ht->table);
201c0e09200SDave Airlie 		ht->table = NULL;
202c0e09200SDave Airlie 	}
203c0e09200SDave Airlie }
204