blob: 6219529fa889cbb9bb0c065c5df2343efd1d02f9 [file] [edit]
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Copyright 2026 Amazon.com, Inc. or its affiliates. All rights reserved.
*/
#include <linux/slab.h>
#include "efa_ah_cache.h"
static const struct rhashtable_params ah_cache_params = {
.key_len = sizeof(struct efa_ah_cache_key),
.key_offset = offsetof(struct efa_ah_cache_entry, key),
.head_offset = offsetof(struct efa_ah_cache_entry, linkage),
};
int efa_ah_cache_init(struct efa_ah_cache *ah_cache)
{
int err;
mutex_init(&ah_cache->lock);
err = rhashtable_init(&ah_cache->hashtable, &ah_cache_params);
if (err)
mutex_destroy(&ah_cache->lock);
return err;
}
static void efa_ah_cache_entry_free(void *ptr, void *arg)
{
struct efa_ah_cache_entry *entry = ptr;
WARN_ON(entry->usecnt);
mutex_destroy(&entry->lock);
kfree(entry);
}
void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache)
{
rhashtable_free_and_destroy(&ah_cache->hashtable, efa_ah_cache_entry_free, NULL);
mutex_destroy(&ah_cache->lock);
}
static struct efa_ah_cache_entry *efa_ah_cache_lookup_locked(struct efa_ah_cache *ah_cache, u16 pd,
u8 *gid)
__must_hold(&ah_cache->lock)
{
struct efa_ah_cache_key key = {};
memcpy(key.gid, gid, sizeof(key.gid));
key.pd = pd;
return rhashtable_lookup_fast(&ah_cache->hashtable, &key, ah_cache_params);
}
struct efa_ah_cache_entry *efa_ah_cache_lookup(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid)
{
struct efa_ah_cache_entry *entry;
mutex_lock(&ah_cache->lock);
entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid);
mutex_unlock(&ah_cache->lock);
return entry;
}
/**
* efa_ah_cache_get - Get or create an AH cache entry
* @ah_cache: AH cache
* @pd: Protection domain number
* @gid: GID address
*
* Look up an AH cache entry by PD and GID. If found, take a reference and
* return it. If not found, allocate a new entry and insert it. The caller must lock
* the entry mutex and check usecnt to determine whether a device create
* command is needed.
*
* Return: Pointer to the entry on success, ERR_PTR on failure.
*/
struct efa_ah_cache_entry *efa_ah_cache_get(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid)
{
struct efa_ah_cache_entry *entry;
int err;
mutex_lock(&ah_cache->lock);
entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid);
if (entry) {
refcount_inc(&entry->refcount);
mutex_unlock(&ah_cache->lock);
return entry;
}
entry = kzalloc_obj(*entry);
if (!entry) {
mutex_unlock(&ah_cache->lock);
return ERR_PTR(-ENOMEM);
}
memcpy(entry->key.gid, gid, sizeof(entry->key.gid));
entry->key.pd = pd;
refcount_set(&entry->refcount, 1);
mutex_init(&entry->lock);
err = rhashtable_insert_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params);
if (err) {
mutex_destroy(&entry->lock);
kfree(entry);
mutex_unlock(&ah_cache->lock);
return ERR_PTR(err);
}
mutex_unlock(&ah_cache->lock);
return entry;
}
/**
* efa_ah_cache_put - Put a refcount of an AH cache entry
* @ah_cache: AH cache
* @entry: AH cache entry
*
* Drop the refcount. If it reaches zero, remove the entry from the hashtable
* and free it.
*/
void efa_ah_cache_put(struct efa_ah_cache *ah_cache, struct efa_ah_cache_entry *entry)
{
if (!refcount_dec_and_mutex_lock(&entry->refcount, &ah_cache->lock))
return;
/* AH cache lock is held here */
rhashtable_remove_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params);
mutex_unlock(&ah_cache->lock);
mutex_destroy(&entry->lock);
kfree(entry);
}