"Fossies" - the Fresh Open Source Software archive 
Member "open-fcoe/kernel/libsa/sa_hash_kern.c" of archive open-fcoe.tar.gz:
/*
* Copyright(c) 2007 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Maintained at www.Open-FCoE.org
*/
#include "sa_kernel.h"
#include "sa_assert.h"
#include "sa_log.h"
#include "sa_hash.h"
struct sa_hash {
struct sa_hash_type sh_type;
u_int32_t sh_mask; /* mask for the size of the table */
u_int32_t sh_entries; /* number of entries now in the table */
struct hlist_head sh_table[0]; /* table (will be allocated bigger) */
};
struct sa_hash_elem { /* stand-in for the real client element */
struct hlist_node elem_node;
};
static inline struct hlist_head *sa_hash_bucket(struct sa_hash *hp,
sa_hash_key_t key)
{
return &hp->sh_table[(*hp->sh_type.st_hash) (key) & hp->sh_mask];
}
struct sa_hash *sa_hash_create(const struct sa_hash_type *tp, uint32_t req_size)
{
struct sa_hash *hp;
u_int32_t size;
size_t len;
/*
* Pick power of 2 at least as big as size.
*/
for (size = 4; size < (1UL << 31); size <<= 1)
if (size >= req_size)
break;
ASSERT(size >= req_size);
ASSERT(size < (1UL << 31));
ASSERT((size & (size - 1)) == 0); /* test for a power of 2 */
len = sizeof(*hp) + size * sizeof(struct hlist_head);
hp = sa_malloc(len);
if (hp) {
memset(hp, 0, len);
hp->sh_type = *tp;
hp->sh_mask = size - 1;
}
return hp;
}
void sa_hash_destroy(struct sa_hash *hp)
{
ASSERT(hp->sh_entries == 0);
sa_free(hp);
}
void *sa_hash_lookup(struct sa_hash *hp, const sa_hash_key_t key)
{
struct sa_hash_elem *ep;
struct hlist_node *np;
struct hlist_head *hhp;
void *rp = NULL;
hhp = sa_hash_bucket(hp, key);
hlist_for_each_entry_rcu(ep, np, hhp, elem_node) {
rp = (void *)((char *)ep - hp->sh_type.st_link_offset);
if ((*hp->sh_type.st_match) (key, rp))
break;
rp = NULL;
}
return rp;
}
void *sa_hash_lookup_delete(struct sa_hash *hp, const sa_hash_key_t key)
{
struct sa_hash_elem *ep;
struct hlist_node *np;
struct hlist_head *hhp;
void *rp = NULL;
hhp = sa_hash_bucket(hp, key);
hlist_for_each_entry_rcu(ep, np, hhp, elem_node) {
rp = (void *)((char *)ep - hp->sh_type.st_link_offset);
if ((*hp->sh_type.st_match) (key, rp)) {
hlist_del_rcu(np);
hp->sh_entries--;
break;
}
rp = NULL;
}
return (rp);
}
void sa_hash_insert(struct sa_hash *hp, const sa_hash_key_t key, void *ep)
{
struct hlist_head *hhp;
struct hlist_node *lp; /* new link pointer */
lp = (struct hlist_node *)((char *)ep + hp->sh_type.st_link_offset);
hhp = sa_hash_bucket(hp, key);
hlist_add_head_rcu(lp, hhp);
hp->sh_entries++;
ASSERT(hp->sh_entries > 0); /* check for overflow */
}
/*
* Iterate through all hash entries.
* For debugging. This can be slow.
*/
void
sa_hash_iterate(struct sa_hash *hp,
void (*callback) (void *ep, void *arg), void *arg)
{
struct hlist_head *hhp;
struct hlist_node *np;
struct sa_hash_elem *ep;
void *entry;
int count = 0;
for (hhp = hp->sh_table; hhp < &hp->sh_table[hp->sh_mask + 1]; hhp++) {
hlist_for_each_entry_rcu(ep, np, hhp, elem_node) {
entry = (void *)((char *)ep -
hp->sh_type.st_link_offset);
(*callback) (entry, arg);
count++;
}
}
if (count != hp->sh_entries)
SA_LOG("sh_entries %d != count %d", hp->sh_entries, count);
ASSERT(count == hp->sh_entries);
}