1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Implementation of the hash table type. 4 * 5 * Author : Stephen Smalley, <sds@tycho.nsa.gov> 6 */ 7#include <linux/kernel.h> 8#include <linux/slab.h> 9#include <linux/errno.h> 10#include "hashtab.h" 11 12static struct kmem_cache *hashtab_node_cachep; 13 14/* 15 * Here we simply round the number of elements up to the nearest power of two. 16 * I tried also other options like rouding down or rounding to the closest 17 * power of two (up or down based on which is closer), but I was unable to 18 * find any significant difference in lookup/insert performance that would 19 * justify switching to a different (less intuitive) formula. It could be that 20 * a different formula is actually more optimal, but any future changes here 21 * should be supported with performance/memory usage data. 22 * 23 * The total memory used by the htable arrays (only) with Fedora policy loaded 24 * is approximately 163 KB at the time of writing. 25 */ 26static u32 hashtab_compute_size(u32 nel) 27{ 28 return nel == 0 ? 0 : roundup_pow_of_two(nel); 29} 30 31int hashtab_init(struct hashtab *h, u32 nel_hint) 32{ 33 u32 size = hashtab_compute_size(nel_hint); 34 35 /* should already be zeroed, but better be safe */ 36 h->nel = 0; 37 h->size = 0; 38 h->htable = NULL; 39 40 if (size) { 41 h->htable = kcalloc(size, sizeof(*h->htable), GFP_KERNEL); 42 if (!h->htable) 43 return -ENOMEM; 44 h->size = size; 45 } 46 return 0; 47} 48 49int __hashtab_insert(struct hashtab *h, struct hashtab_node **dst, 50 void *key, void *datum) 51{ 52 struct hashtab_node *newnode; 53 54 newnode = kmem_cache_zalloc(hashtab_node_cachep, GFP_KERNEL); 55 if (!newnode) 56 return -ENOMEM; 57 newnode->key = key; 58 newnode->datum = datum; 59 newnode->next = *dst; 60 *dst = newnode; 61 62 h->nel++; 63 return 0; 64} 65 66void hashtab_destroy(struct hashtab *h) 67{ 68 u32 i; 69 struct hashtab_node *cur, *temp; 70 71 for (i = 0; i < h->size; i++) { 72 cur = h->htable[i]; 73 while (cur) { 74 temp = cur; 75 cur = cur->next; 76 kmem_cache_free(hashtab_node_cachep, temp); 77 } 78 h->htable[i] = NULL; 79 } 80 81 kfree(h->htable); 82 h->htable = NULL; 83} 84 85int hashtab_map(struct hashtab *h, 86 int (*apply)(void *k, void *d, void *args), 87 void *args) 88{ 89 u32 i; 90 int ret; 91 struct hashtab_node *cur; 92 93 for (i = 0; i < h->size; i++) { 94 cur = h->htable[i]; 95 while (cur) { 96 ret = apply(cur->key, cur->datum, args); 97 if (ret) 98 return ret; 99 cur = cur->next; 100 } 101 } 102 return 0; 103} 104 105 106void hashtab_stat(struct hashtab *h, struct hashtab_info *info) 107{ 108 u32 i, chain_len, slots_used, max_chain_len; 109 struct hashtab_node *cur; 110 111 slots_used = 0; 112 max_chain_len = 0; 113 for (i = 0; i < h->size; i++) { 114 cur = h->htable[i]; 115 if (cur) { 116 slots_used++; 117 chain_len = 0; 118 while (cur) { 119 chain_len++; 120 cur = cur->next; 121 } 122 123 if (chain_len > max_chain_len) 124 max_chain_len = chain_len; 125 } 126 } 127 128 info->slots_used = slots_used; 129 info->max_chain_len = max_chain_len; 130} 131 132int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, 133 int (*copy)(struct hashtab_node *new, 134 struct hashtab_node *orig, void *args), 135 int (*destroy)(void *k, void *d, void *args), 136 void *args) 137{ 138 struct hashtab_node *cur, *tmp, *tail; 139 int i, rc; 140 141 memset(new, 0, sizeof(*new)); 142 143 new->htable = kcalloc(orig->size, sizeof(*new->htable), GFP_KERNEL); 144 if (!new->htable) 145 return -ENOMEM; 146 147 new->size = orig->size; 148 149 for (i = 0; i < orig->size; i++) { 150 tail = NULL; 151 for (cur = orig->htable[i]; cur; cur = cur->next) { 152 tmp = kmem_cache_zalloc(hashtab_node_cachep, 153 GFP_KERNEL); 154 if (!tmp) 155 goto error; 156 rc = copy(tmp, cur, args); 157 if (rc) { 158 kmem_cache_free(hashtab_node_cachep, tmp); 159 goto error; 160 } 161 tmp->next = NULL; 162 if (!tail) 163 new->htable[i] = tmp; 164 else 165 tail->next = tmp; 166 tail = tmp; 167 new->nel++; 168 } 169 } 170 171 return 0; 172 173 error: 174 for (i = 0; i < new->size; i++) { 175 for (cur = new->htable[i]; cur; cur = tmp) { 176 tmp = cur->next; 177 destroy(cur->key, cur->datum, args); 178 kmem_cache_free(hashtab_node_cachep, cur); 179 } 180 } 181 kfree(new->htable); 182 memset(new, 0, sizeof(*new)); 183 return -ENOMEM; 184} 185 186void __init hashtab_cache_init(void) 187{ 188 hashtab_node_cachep = kmem_cache_create("hashtab_node", 189 sizeof(struct hashtab_node), 190 0, SLAB_PANIC, NULL); 191} 192