Back to home page

OSCL-LXR

 
 

    


0001 /**************************************************************************
0002  *
0003  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
0004  * All Rights Reserved.
0005  *
0006  * Permission is hereby granted, free of charge, to any person obtaining a
0007  * copy of this software and associated documentation files (the
0008  * "Software"), to deal in the Software without restriction, including
0009  * without limitation the rights to use, copy, modify, merge, publish,
0010  * distribute, sub license, and/or sell copies of the Software, and to
0011  * permit persons to whom the Software is furnished to do so, subject to
0012  * the following conditions:
0013  *
0014  * The above copyright notice and this permission notice (including the
0015  * next paragraph) shall be included in all copies or substantial portions
0016  * of the Software.
0017  *
0018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0019  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0020  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
0021  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
0022  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
0023  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
0024  * USE OR OTHER DEALINGS IN THE SOFTWARE.
0025  *
0026  *
0027  **************************************************************************/
0028 /*
0029  * Simple open hash tab implementation.
0030  *
0031  * Authors:
0032  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
0033  */
0034 
0035 #include <linux/hash.h>
0036 #include <linux/mm.h>
0037 #include <linux/rculist.h>
0038 #include <linux/slab.h>
0039 #include <linux/vmalloc.h>
0040 
0041 #include <drm/drm_print.h>
0042 
0043 #include "drm_legacy.h"
0044 
0045 int drm_ht_create(struct drm_open_hash *ht, unsigned int order)
0046 {
0047     unsigned int size = 1 << order;
0048 
0049     ht->order = order;
0050     ht->table = NULL;
0051     if (size <= PAGE_SIZE / sizeof(*ht->table))
0052         ht->table = kcalloc(size, sizeof(*ht->table), GFP_KERNEL);
0053     else
0054         ht->table = vzalloc(array_size(size, sizeof(*ht->table)));
0055     if (!ht->table) {
0056         DRM_ERROR("Out of memory for hash table\n");
0057         return -ENOMEM;
0058     }
0059     return 0;
0060 }
0061 
0062 void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key)
0063 {
0064     struct drm_hash_item *entry;
0065     struct hlist_head *h_list;
0066     unsigned int hashed_key;
0067     int count = 0;
0068 
0069     hashed_key = hash_long(key, ht->order);
0070     DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
0071     h_list = &ht->table[hashed_key];
0072     hlist_for_each_entry(entry, h_list, head)
0073         DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
0074 }
0075 
0076 static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht,
0077                       unsigned long key)
0078 {
0079     struct drm_hash_item *entry;
0080     struct hlist_head *h_list;
0081     unsigned int hashed_key;
0082 
0083     hashed_key = hash_long(key, ht->order);
0084     h_list = &ht->table[hashed_key];
0085     hlist_for_each_entry(entry, h_list, head) {
0086         if (entry->key == key)
0087             return &entry->head;
0088         if (entry->key > key)
0089             break;
0090     }
0091     return NULL;
0092 }
0093 
0094 static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht,
0095                           unsigned long key)
0096 {
0097     struct drm_hash_item *entry;
0098     struct hlist_head *h_list;
0099     unsigned int hashed_key;
0100 
0101     hashed_key = hash_long(key, ht->order);
0102     h_list = &ht->table[hashed_key];
0103     hlist_for_each_entry_rcu(entry, h_list, head) {
0104         if (entry->key == key)
0105             return &entry->head;
0106         if (entry->key > key)
0107             break;
0108     }
0109     return NULL;
0110 }
0111 
0112 int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item)
0113 {
0114     struct drm_hash_item *entry;
0115     struct hlist_head *h_list;
0116     struct hlist_node *parent;
0117     unsigned int hashed_key;
0118     unsigned long key = item->key;
0119 
0120     hashed_key = hash_long(key, ht->order);
0121     h_list = &ht->table[hashed_key];
0122     parent = NULL;
0123     hlist_for_each_entry(entry, h_list, head) {
0124         if (entry->key == key)
0125             return -EINVAL;
0126         if (entry->key > key)
0127             break;
0128         parent = &entry->head;
0129     }
0130     if (parent) {
0131         hlist_add_behind_rcu(&item->head, parent);
0132     } else {
0133         hlist_add_head_rcu(&item->head, h_list);
0134     }
0135     return 0;
0136 }
0137 
0138 /*
0139  * Just insert an item and return any "bits" bit key that hasn't been
0140  * used before.
0141  */
0142 int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *item,
0143                   unsigned long seed, int bits, int shift,
0144                   unsigned long add)
0145 {
0146     int ret;
0147     unsigned long mask = (1UL << bits) - 1;
0148     unsigned long first, unshifted_key;
0149 
0150     unshifted_key = hash_long(seed, bits);
0151     first = unshifted_key;
0152     do {
0153         item->key = (unshifted_key << shift) + add;
0154         ret = drm_ht_insert_item(ht, item);
0155         if (ret)
0156             unshifted_key = (unshifted_key + 1) & mask;
0157     } while(ret && (unshifted_key != first));
0158 
0159     if (ret) {
0160         DRM_ERROR("Available key bit space exhausted\n");
0161         return -EINVAL;
0162     }
0163     return 0;
0164 }
0165 
0166 int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key,
0167              struct drm_hash_item **item)
0168 {
0169     struct hlist_node *list;
0170 
0171     list = drm_ht_find_key_rcu(ht, key);
0172     if (!list)
0173         return -EINVAL;
0174 
0175     *item = hlist_entry(list, struct drm_hash_item, head);
0176     return 0;
0177 }
0178 
0179 int drm_ht_remove_key(struct drm_open_hash *ht, unsigned long key)
0180 {
0181     struct hlist_node *list;
0182 
0183     list = drm_ht_find_key(ht, key);
0184     if (list) {
0185         hlist_del_init_rcu(list);
0186         return 0;
0187     }
0188     return -EINVAL;
0189 }
0190 
0191 int drm_ht_remove_item(struct drm_open_hash *ht, struct drm_hash_item *item)
0192 {
0193     hlist_del_init_rcu(&item->head);
0194     return 0;
0195 }
0196 
0197 void drm_ht_remove(struct drm_open_hash *ht)
0198 {
0199     if (ht->table) {
0200         kvfree(ht->table);
0201         ht->table = NULL;
0202     }
0203 }