Home | History | Annotate | Line # | Download | only in drm
drm_hashtab.c revision 1.2
      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 Hellstrm <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