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