13f6063ccSDavid Shao /**************************************************************************
23f6063ccSDavid Shao *
33f6063ccSDavid Shao * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
43f6063ccSDavid Shao * All Rights Reserved.
53f6063ccSDavid Shao *
63f6063ccSDavid Shao * Permission is hereby granted, free of charge, to any person obtaining a
73f6063ccSDavid Shao * copy of this software and associated documentation files (the
83f6063ccSDavid Shao * "Software"), to deal in the Software without restriction, including
93f6063ccSDavid Shao * without limitation the rights to use, copy, modify, merge, publish,
103f6063ccSDavid Shao * distribute, sub license, and/or sell copies of the Software, and to
113f6063ccSDavid Shao * permit persons to whom the Software is furnished to do so, subject to
123f6063ccSDavid Shao * the following conditions:
133f6063ccSDavid Shao *
143f6063ccSDavid Shao * The above copyright notice and this permission notice (including the
153f6063ccSDavid Shao * next paragraph) shall be included in all copies or substantial portions
163f6063ccSDavid Shao * of the Software.
173f6063ccSDavid Shao *
183f6063ccSDavid Shao * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193f6063ccSDavid Shao * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203f6063ccSDavid Shao * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
213f6063ccSDavid Shao * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
223f6063ccSDavid Shao * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
233f6063ccSDavid Shao * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
243f6063ccSDavid Shao * USE OR OTHER DEALINGS IN THE SOFTWARE.
253f6063ccSDavid Shao *
263f6063ccSDavid Shao *
273f6063ccSDavid Shao **************************************************************************/
283f6063ccSDavid Shao /*
293f6063ccSDavid Shao * Simple open hash tab implementation.
303f6063ccSDavid Shao *
313f6063ccSDavid Shao * Authors:
323f6063ccSDavid Shao * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
333f6063ccSDavid Shao */
343f6063ccSDavid Shao
3518e26a6dSFrançois Tigeot #include <drm/drmP.h>
3618e26a6dSFrançois Tigeot #include <drm/drm_hashtab.h>
376e7a447aSFrançois Tigeot #include <linux/hash.h>
389edbd4a0SFrançois Tigeot #include <linux/slab.h>
396e7a447aSFrançois Tigeot #include <linux/export.h>
403f6063ccSDavid Shao
drm_ht_create(struct drm_open_hash * ht,unsigned int order)413f6063ccSDavid Shao int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
423f6063ccSDavid Shao {
436e7a447aSFrançois Tigeot unsigned int size = 1 << order;
446e7a447aSFrançois Tigeot
453f6063ccSDavid Shao ht->order = order;
463f6063ccSDavid Shao ht->table = NULL;
479edbd4a0SFrançois Tigeot if (size <= PAGE_SIZE / sizeof(*ht->table))
4822ee5efbSFrançois Tigeot ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
499edbd4a0SFrançois Tigeot else
5022ee5efbSFrançois Tigeot ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
513f6063ccSDavid Shao if (!ht->table) {
523f6063ccSDavid Shao DRM_ERROR("Out of memory for hash table\n");
533f6063ccSDavid Shao return -ENOMEM;
543f6063ccSDavid Shao }
553f6063ccSDavid Shao return 0;
563f6063ccSDavid Shao }
576e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_create);
583f6063ccSDavid Shao
drm_ht_verbose_list(struct drm_open_hash * ht,unsigned long key)593f6063ccSDavid Shao void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
603f6063ccSDavid Shao {
613f6063ccSDavid Shao struct drm_hash_item *entry;
626e7a447aSFrançois Tigeot struct hlist_head *h_list;
633f6063ccSDavid Shao unsigned int hashed_key;
643f6063ccSDavid Shao int count = 0;
653f6063ccSDavid Shao
666e7a447aSFrançois Tigeot hashed_key = hash_long(key, ht->order);
673f6063ccSDavid Shao DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
686e7a447aSFrançois Tigeot h_list = &ht->table[hashed_key];
69d2a9170aSFrançois Tigeot hlist_for_each_entry(entry, h_list, head)
703f6063ccSDavid Shao DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
713f6063ccSDavid Shao }
723f6063ccSDavid Shao
drm_ht_find_key(struct drm_open_hash * ht,unsigned long key)736e7a447aSFrançois Tigeot static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
746e7a447aSFrançois Tigeot unsigned long key)
753f6063ccSDavid Shao {
763f6063ccSDavid Shao struct drm_hash_item *entry;
776e7a447aSFrançois Tigeot struct hlist_head *h_list;
783f6063ccSDavid Shao unsigned int hashed_key;
793f6063ccSDavid Shao
806e7a447aSFrançois Tigeot hashed_key = hash_long(key, ht->order);
816e7a447aSFrançois Tigeot h_list = &ht->table[hashed_key];
82d2a9170aSFrançois Tigeot hlist_for_each_entry(entry, h_list, head) {
833f6063ccSDavid Shao if (entry->key == key)
84d2a9170aSFrançois Tigeot return &entry->head;
853f6063ccSDavid Shao if (entry->key > key)
863f6063ccSDavid Shao break;
873f6063ccSDavid Shao }
883f6063ccSDavid Shao return NULL;
893f6063ccSDavid Shao }
903f6063ccSDavid Shao
drm_ht_find_key_rcu(struct drm_open_hash * ht,unsigned long key)916e7a447aSFrançois Tigeot static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
926e7a447aSFrançois Tigeot unsigned long key)
936e7a447aSFrançois Tigeot {
946e7a447aSFrançois Tigeot struct drm_hash_item *entry;
956e7a447aSFrançois Tigeot struct hlist_head *h_list;
966e7a447aSFrançois Tigeot unsigned int hashed_key;
976e7a447aSFrançois Tigeot
986e7a447aSFrançois Tigeot hashed_key = hash_long(key, ht->order);
996e7a447aSFrançois Tigeot h_list = &ht->table[hashed_key];
100d2a9170aSFrançois Tigeot hlist_for_each_entry_rcu(entry, h_list, head) {
1016e7a447aSFrançois Tigeot if (entry->key == key)
102d2a9170aSFrançois Tigeot return &entry->head;
1036e7a447aSFrançois Tigeot if (entry->key > key)
1046e7a447aSFrançois Tigeot break;
1056e7a447aSFrançois Tigeot }
1066e7a447aSFrançois Tigeot return NULL;
1076e7a447aSFrançois Tigeot }
1083f6063ccSDavid Shao
drm_ht_insert_item(struct drm_open_hash * ht,struct drm_hash_item * item)1093f6063ccSDavid Shao int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1103f6063ccSDavid Shao {
1116e7a447aSFrançois Tigeot struct drm_hash_item *entry;
1126e7a447aSFrançois Tigeot struct hlist_head *h_list;
113d2a9170aSFrançois Tigeot struct hlist_node *parent;
1143f6063ccSDavid Shao unsigned int hashed_key;
1153f6063ccSDavid Shao unsigned long key = item->key;
1163f6063ccSDavid Shao
1176e7a447aSFrançois Tigeot hashed_key = hash_long(key, ht->order);
1186e7a447aSFrançois Tigeot h_list = &ht->table[hashed_key];
1193f6063ccSDavid Shao parent = NULL;
120d2a9170aSFrançois Tigeot hlist_for_each_entry(entry, h_list, head) {
1213f6063ccSDavid Shao if (entry->key == key)
1223f6063ccSDavid Shao return -EINVAL;
1233f6063ccSDavid Shao if (entry->key > key)
1243f6063ccSDavid Shao break;
125d2a9170aSFrançois Tigeot parent = &entry->head;
1263f6063ccSDavid Shao }
1273f6063ccSDavid Shao if (parent) {
128d2a9170aSFrançois Tigeot hlist_add_behind_rcu(&item->head, parent);
1293f6063ccSDavid Shao } else {
1306e7a447aSFrançois Tigeot hlist_add_head_rcu(&item->head, h_list);
1313f6063ccSDavid Shao }
1323f6063ccSDavid Shao return 0;
1333f6063ccSDavid Shao }
1346e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_insert_item);
1353f6063ccSDavid Shao
1363f6063ccSDavid Shao /*
1373f6063ccSDavid Shao * Just insert an item and return any "bits" bit key that hasn't been
1383f6063ccSDavid Shao * used before.
1393f6063ccSDavid Shao */
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)1403f6063ccSDavid Shao int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
1413f6063ccSDavid Shao unsigned long seed, int bits, int shift,
1423f6063ccSDavid Shao unsigned long add)
1433f6063ccSDavid Shao {
1443f6063ccSDavid Shao int ret;
145*1dedbd3bSFrançois Tigeot unsigned long mask = (1UL << bits) - 1;
1469edbd4a0SFrançois Tigeot unsigned long first, unshifted_key;
1473f6063ccSDavid Shao
1486e7a447aSFrançois Tigeot unshifted_key = hash_long(seed, bits);
1493f6063ccSDavid Shao first = unshifted_key;
1503f6063ccSDavid Shao do {
1513f6063ccSDavid Shao item->key = (unshifted_key << shift) + add;
1523f6063ccSDavid Shao ret = drm_ht_insert_item(ht, item);
1533f6063ccSDavid Shao if (ret)
1543f6063ccSDavid Shao unshifted_key = (unshifted_key + 1) & mask;
1553f6063ccSDavid Shao } while(ret && (unshifted_key != first));
1563f6063ccSDavid Shao
1573f6063ccSDavid Shao if (ret) {
1583f6063ccSDavid Shao DRM_ERROR("Available key bit space exhausted\n");
1593f6063ccSDavid Shao return -EINVAL;
1603f6063ccSDavid Shao }
1613f6063ccSDavid Shao return 0;
1623f6063ccSDavid Shao }
1636e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_just_insert_please);
1643f6063ccSDavid Shao
drm_ht_find_item(struct drm_open_hash * ht,unsigned long key,struct drm_hash_item ** item)1653f6063ccSDavid Shao int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
1663f6063ccSDavid Shao struct drm_hash_item **item)
1673f6063ccSDavid Shao {
1686e7a447aSFrançois Tigeot struct hlist_node *list;
1693f6063ccSDavid Shao
1706e7a447aSFrançois Tigeot list = drm_ht_find_key_rcu(ht, key);
1716e7a447aSFrançois Tigeot if (!list)
1723f6063ccSDavid Shao return -EINVAL;
1733f6063ccSDavid Shao
1746e7a447aSFrançois Tigeot *item = hlist_entry(list, struct drm_hash_item, head);
1753f6063ccSDavid Shao return 0;
1763f6063ccSDavid Shao }
1776e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_find_item);
1783f6063ccSDavid Shao
drm_ht_remove_key(struct drm_open_hash * ht,unsigned long key)1793f6063ccSDavid Shao int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
1803f6063ccSDavid Shao {
1816e7a447aSFrançois Tigeot struct hlist_node *list;
1823f6063ccSDavid Shao
1836e7a447aSFrançois Tigeot list = drm_ht_find_key(ht, key);
1846e7a447aSFrançois Tigeot if (list) {
1856e7a447aSFrançois Tigeot hlist_del_init_rcu(list);
1863f6063ccSDavid Shao return 0;
1873f6063ccSDavid Shao }
1883f6063ccSDavid Shao return -EINVAL;
1893f6063ccSDavid Shao }
1903f6063ccSDavid Shao
drm_ht_remove_item(struct drm_open_hash * ht,struct drm_hash_item * item)1913f6063ccSDavid Shao int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
1923f6063ccSDavid Shao {
1936e7a447aSFrançois Tigeot hlist_del_init_rcu(&item->head);
1943f6063ccSDavid Shao return 0;
1953f6063ccSDavid Shao }
1966e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_remove_item);
1973f6063ccSDavid Shao
drm_ht_remove(struct drm_open_hash * ht)1983f6063ccSDavid Shao void drm_ht_remove(struct drm_open_hash *ht)
1993f6063ccSDavid Shao {
2003f6063ccSDavid Shao if (ht->table) {
2019edbd4a0SFrançois Tigeot if ((PAGE_SIZE / sizeof(*ht->table)) >> ht->order)
2029edbd4a0SFrançois Tigeot kfree(ht->table);
2039edbd4a0SFrançois Tigeot else
204158486a6SFrançois Tigeot kfree(ht->table);
2053f6063ccSDavid Shao ht->table = NULL;
2063f6063ccSDavid Shao }
2073f6063ccSDavid Shao }
2086e7a447aSFrançois Tigeot EXPORT_SYMBOL(drm_ht_remove);
209