mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
On create AH, first check if the AH cache entry already exists and if so, returns the already stored AH number. If the entry doesn't exist, the driver creates it and calls the device to create the AH. A per-entry mutex serializes concurrent device commands on the same AH cache entry, ensuring only one thread issues the device create while others wait and reuse the result. If the device create fails, the entry's user count remains zero so subsequent threads will retry the device create. On destroy AH, the user count is decremented under the entry mutex. If it reaches zero, the driver issues the device destroy command. After the device destroy completes, it removes the entry from the hashtable and frees it if no other references exist. If new users arrived during the destroy, the entry remains in the hashtable for reuse. Reviewed-by: Firas Jahjah <firasj@amazon.com> Reviewed-by: Michael Margolin <mrgolin@amazon.com> Signed-off-by: Yonatan Nachum <ynachum@amazon.com> Link: https://patch.msgid.link/20260706170008.1039417-3-ynachum@amazon.com Signed-off-by: Leon Romanovsky <leon@kernel.org>
136 lines
3.4 KiB
C
136 lines
3.4 KiB
C
// 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);
|
|
}
|