Viewing: osd_hash.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024-2025, Amazon and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2025-2026, DDN/Whamcloud, Inc.
*/
/*
* Hash index with FIXED key length.
* Traverse the index via linear list scanning.
*
* Author: Timothy Day <timday@amazon.com>
* Author: Yingjin Qian <qian@ddn.com>
*/
#define DEBUG_SUBSYSTEM S_OSD
#include <obd_support.h>
#include "index.h"
static u32 hash_index_keyhash(const void *data, u32 len, u32 seed)
{
return jhash(data, len, seed);
}
static u32 hash_index_entry_keyhash(const void *data, u32 len, u32 seed)
{
struct hash_index_entry *entry = (struct hash_index_entry *)data;
return hash_index_keyhash(&entry->he_buf, entry->he_keylen, seed);
}
static int hash_index_keycmp(struct rhashtable_compare_arg *arg,
const void *obj)
{
struct hash_index_entry *entry = (struct hash_index_entry *)obj;
LASSERT(arg->ht->key_len == entry->he_keylen);
if (!memcpy(entry->he_buf, arg->key, entry->he_keylen))
return 0;
/* ESRCH is typical for rhashtable */
return -ESRCH;
}
static const struct rhashtable_params hash_index_params = {
.head_offset = offsetof(struct hash_index_entry, he_hash),
.hashfn = hash_index_keyhash,
.obj_hashfn = hash_index_entry_keyhash,
.obj_cmpfn = hash_index_keycmp,
.automatic_shrinking = true,
};
int hash_index_init(struct hash_index *hind, size_t keylen, size_t reclen)
{
int rc;
LASSERT(keylen > 0);
INIT_LIST_HEAD(&hind->hi_list);
hind->hi_htbl_params = hash_index_params;
hind->hi_htbl_params.key_len = keylen;
hind->hi_reclen = reclen;
rc = rhashtable_init(&hind->hi_htbl, &hind->hi_htbl_params);
return rc;
}
void hash_index_fini(struct hash_index *hind)
{
struct hash_index_entry *entry, *tmp;
if (!hind)
return;
list_for_each_entry_safe(entry, tmp, &hind->hi_list, he_list_item) {
rhashtable_remove_fast(&hind->hi_htbl, &entry->he_hash,
hind->hi_htbl_params);
list_del(&entry->he_list_item);
OBD_FREE(entry, entry->he_len);
}
rhashtable_destroy(&hind->hi_htbl);
}
int hash_index_lookup(struct hash_index *hind, const void *key, void *rec)
{
struct hash_index_entry *entry;
int rc = 0;
entry = rhashtable_lookup_fast(&hind->hi_htbl, key,
hind->hi_htbl_params);
if (entry) {
size_t reclen;
reclen = entry->he_len - sizeof(*entry) - entry->he_keylen;
LASSERT(ergo(hind->hi_reclen, hind->hi_reclen == reclen));
memcpy(rec, entry->he_buf + entry->he_keylen, reclen);
return 1;
}
return rc;
}
int hash_index_insert(struct hash_index *hind, void *key, size_t keylen,
void *rec, size_t reclen)
{
struct hash_index_entry *entry;
size_t len;
int rc = 0;
ENTRY;
if (!keylen)
keylen = hind->hi_htbl_params.key_len;
else
LASSERT(keylen == hind->hi_htbl_params.key_len);
if (!reclen)
reclen = hind->hi_reclen;
else
LASSERT(reclen == hind->hi_reclen);
len = sizeof(*entry) + keylen + reclen;
OBD_ALLOC(entry, len);
if (!entry)
RETURN(-ENOMEM);
entry->he_len = len;
entry->he_keylen = keylen;
memcpy(entry->he_buf, key, keylen);
memcpy(entry->he_buf + keylen, rec, reclen);
rc = rhashtable_insert_fast(&hind->hi_htbl, &entry->he_hash,
hind->hi_htbl_params);
LASSERT(rc != -EBUSY);
if (rc)
GOTO(out_free, rc);
list_add_tail(&entry->he_list_item, &hind->hi_list);
/* TODO: Rollover? Should at least add detection... */
entry->he_offset = hind->hi_next_offset++;
RETURN(0);
out_free:
OBD_FREE(entry, len);
RETURN(rc);
}
void hash_index_remove(struct hash_index *hind, const void *key)
{
struct hash_index_entry *entry;
entry = rhashtable_lookup_fast(&hind->hi_htbl, key,
hind->hi_htbl_params);
if (!entry)
return;
rhashtable_remove_fast(&hind->hi_htbl, &entry->he_hash,
hind->hi_htbl_params);
/* FIXME: use RCU for list insert/remove. */
list_del(&entry->he_list_item);
OBD_FREE(entry, entry->he_len);
}