Viewing: osd_index_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.
*/
/*
* Index Access Module.
*
* Author: Timothy Day <timday@amazon.com>
* Author: Yingjin Qian <qian@ddn.com>
*/
#define DEBUG_SUBSYSTEM S_OSD
#include <obd_support.h>
#include <obd.h>
#include <obd_class.h>
#include "osd_internal.h"
#include "wbcfs.h"
static int osd_hash_index_lookup(const struct lu_env *env, struct dt_object *dt,
struct dt_rec *rec, const struct dt_key *key)
{
struct osd_object *obj = osd_dt_obj(dt);
struct hash_index *hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
int rc;
ENTRY;
down_read(&obj->oo_guard);
rc = hash_index_lookup(hind, (void *)key, rec);
up_read(&obj->oo_guard);
RETURN(rc);
}
static int
osd_hash_index_insert(const struct lu_env *env, struct dt_object *dt,
const struct dt_rec *rec, const struct dt_key *key,
struct thandle *th)
{
struct osd_object *obj = osd_dt_obj(dt);
struct hash_index *hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
int rc;
ENTRY;
down_write(&obj->oo_guard);
rc = hash_index_insert(hind, (void *)key, 0, (void *)rec, 0);
up_write(&obj->oo_guard);
RETURN(rc);
}
static int osd_hash_index_delete(const struct lu_env *env, struct dt_object *dt,
const struct dt_key *key, struct thandle *th)
{
struct osd_object *obj = osd_dt_obj(dt);
struct hash_index *hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
ENTRY;
down_write(&obj->oo_guard);
hash_index_remove(hind, (void *)key);
up_write(&obj->oo_guard);
RETURN(0);
}
static struct dt_it *osd_hash_index_it_init(const struct lu_env *env,
struct dt_object *dt, __u32 unused)
{
struct osd_object *obj = osd_dt_obj(dt);
struct hash_index *hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
struct osd_hash_it *it;
ENTRY;
if (obj->oo_destroyed)
RETURN(ERR_PTR(-ENOENT));
OBD_SLAB_ALLOC_PTR(it, osd_hash_it_cachep);
if (!it)
RETURN(ERR_PTR(-ENOMEM));
/* FIXME: race between concurrent iterating and deleting */
it->hit_cursor = &hind->hi_list;
it->hit_obj = obj;
RETURN((struct dt_it *)it);
}
static void osd_hash_index_it_fini(const struct lu_env *env,
struct dt_it *di)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
ENTRY;
OBD_SLAB_FREE_PTR(it, osd_hash_it_cachep);
EXIT;
}
static int osd_hash_index_it_get(const struct lu_env *env, struct dt_it *di,
const struct dt_key *key)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index_entry *entry;
struct hash_index *hind;
size_t keylen;
int rc = -EIO;
ENTRY;
if (obj->oo_destroyed)
RETURN(-ENOENT);
hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
keylen = hind->hi_htbl_params.key_len;
down_read(&obj->oo_guard);
list_for_each_entry(entry, &hind->hi_list, he_list_item) {
if (memcmp(key, entry->he_buf, keylen) == 0) {
it->hit_cursor = &entry->he_list_item;
rc = 0;
break;
}
}
up_read(&obj->oo_guard);
RETURN(rc);
}
/* TODO: remove and make fp optional. */
static void osd_hash_index_it_put(const struct lu_env *env, struct dt_it *di)
{
}
static int osd_hash_index_it_next(const struct lu_env *env, struct dt_it *di)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index *hind;
int rc = 0;
ENTRY;
if (obj->oo_destroyed)
RETURN(-ENOENT);
hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
down_read(&obj->oo_guard);
it->hit_cursor = it->hit_cursor->next;
if (it->hit_cursor == &hind->hi_list)
rc = 1;
up_read(&obj->oo_guard);
RETURN(rc);
}
static struct dt_key *osd_hash_index_it_key(const struct lu_env *env,
const struct dt_it *di)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index_entry *entry;
ENTRY;
if (obj->oo_destroyed)
RETURN(ERR_PTR(-ENOENT));
entry = container_of(it->hit_cursor, struct hash_index_entry,
he_list_item);
RETURN((struct dt_key *)entry->he_buf);
}
static int osd_hash_index_it_key_size(const struct lu_env *env,
const struct dt_it *di)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
RETURN(MEMFS_I(obj->oo_inode)->mei_hash_index.hi_htbl_params.key_len);
}
static int osd_hash_index_it_rec(const struct lu_env *env,
const struct dt_it *di, struct dt_rec *rec,
__u32 attr)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index_entry *entry;
struct hash_index *hind;
size_t reclen;
ENTRY;
hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
/* FIXME: use RCU to avoid concurrent operations on the list. */
entry = container_of(it->hit_cursor, struct hash_index_entry,
he_list_item);
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(0);
}
static int osd_hash_index_it_rec_size(const struct lu_env *env,
const struct dt_it *di, __u32 attr)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index_entry *entry;
struct hash_index *hind;
size_t reclen;
ENTRY;
hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
if (hind->hi_reclen == 0) {
entry = container_of(it->hit_cursor, struct hash_index_entry,
he_list_item);
reclen = entry->he_len - sizeof(*entry) - entry->he_keylen;
} else {
reclen = hind->hi_reclen;
}
RETURN(reclen);
}
static __u64 osd_hash_index_it_store(const struct lu_env *env,
const struct dt_it *di)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct hash_index_entry *entry;
ENTRY;
entry = container_of(it->hit_cursor, struct hash_index_entry,
he_list_item);
RETURN(entry->he_offset);
}
static int osd_hash_index_it_load(const struct lu_env *env,
const struct dt_it *di, __u64 hash)
{
struct osd_hash_it *it = (struct osd_hash_it *)di;
struct osd_object *obj = it->hit_obj;
struct hash_index_entry *entry;
struct hash_index *hind;
int rc = 1;
ENTRY;
hind = &MEMFS_I(obj->oo_inode)->mei_hash_index;
if (hash == 0) {
it->hit_cursor = &hind->hi_list;
it->hit_cursor = it->hit_cursor->next;
if (it->hit_cursor == &hind->hi_list)
rc = 0;
RETURN(rc);
}
/* TODO: A linear scan is not efficient, will use Maple Tree instead. */
list_for_each_entry(entry, &hind->hi_list, he_list_item) {
if (entry->he_offset == hash) {
it->hit_cursor = &entry->he_list_item;
rc = 1;
break;
}
}
RETURN(rc);
}
const struct dt_index_operations osd_hash_index_ops = {
.dio_lookup = osd_hash_index_lookup,
.dio_insert = osd_hash_index_insert,
.dio_delete = osd_hash_index_delete,
.dio_it = {
.init = osd_hash_index_it_init,
.fini = osd_hash_index_it_fini,
.get = osd_hash_index_it_get,
.put = osd_hash_index_it_put,
.next = osd_hash_index_it_next,
.key = osd_hash_index_it_key,
.key_size = osd_hash_index_it_key_size,
.rec = osd_hash_index_it_rec,
.rec_size = osd_hash_index_it_rec_size,
.store = osd_hash_index_it_store,
.load = osd_hash_index_it_load
}
};