1 // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause 2 /* 3 * Copyright 2026 Amazon.com, Inc. or its affiliates. All rights reserved. 4 */ 5 6 #include <linux/slab.h> 7 8 #include "efa_ah_cache.h" 9 10 static const struct rhashtable_params ah_cache_params = { 11 .key_len = sizeof(struct efa_ah_cache_key), 12 .key_offset = offsetof(struct efa_ah_cache_entry, key), 13 .head_offset = offsetof(struct efa_ah_cache_entry, linkage), 14 }; 15 16 int efa_ah_cache_init(struct efa_ah_cache *ah_cache) 17 { 18 int err; 19 20 mutex_init(&ah_cache->lock); 21 err = rhashtable_init(&ah_cache->hashtable, &ah_cache_params); 22 if (err) 23 mutex_destroy(&ah_cache->lock); 24 25 return err; 26 } 27 28 static void efa_ah_cache_entry_free(void *ptr, void *arg) 29 { 30 struct efa_ah_cache_entry *entry = ptr; 31 32 WARN_ON(entry->usecnt); 33 mutex_destroy(&entry->lock); 34 kfree(entry); 35 } 36 37 void efa_ah_cache_destroy(struct efa_ah_cache *ah_cache) 38 { 39 rhashtable_free_and_destroy(&ah_cache->hashtable, efa_ah_cache_entry_free, NULL); 40 mutex_destroy(&ah_cache->lock); 41 } 42 43 static struct efa_ah_cache_entry *efa_ah_cache_lookup_locked(struct efa_ah_cache *ah_cache, u16 pd, 44 u8 *gid) 45 __must_hold(&ah_cache->lock) 46 { 47 struct efa_ah_cache_key key = {}; 48 49 memcpy(key.gid, gid, sizeof(key.gid)); 50 key.pd = pd; 51 52 return rhashtable_lookup_fast(&ah_cache->hashtable, &key, ah_cache_params); 53 } 54 55 struct efa_ah_cache_entry *efa_ah_cache_lookup(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid) 56 { 57 struct efa_ah_cache_entry *entry; 58 59 mutex_lock(&ah_cache->lock); 60 entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid); 61 mutex_unlock(&ah_cache->lock); 62 63 return entry; 64 } 65 66 /** 67 * efa_ah_cache_get - Get or create an AH cache entry 68 * @ah_cache: AH cache 69 * @pd: Protection domain number 70 * @gid: GID address 71 * 72 * Look up an AH cache entry by PD and GID. If found, take a reference and 73 * return it. If not found, allocate a new entry and insert it. The caller must lock 74 * the entry mutex and check usecnt to determine whether a device create 75 * command is needed. 76 * 77 * Return: Pointer to the entry on success, ERR_PTR on failure. 78 */ 79 struct efa_ah_cache_entry *efa_ah_cache_get(struct efa_ah_cache *ah_cache, u16 pd, u8 *gid) 80 { 81 struct efa_ah_cache_entry *entry; 82 int err; 83 84 mutex_lock(&ah_cache->lock); 85 86 entry = efa_ah_cache_lookup_locked(ah_cache, pd, gid); 87 if (entry) { 88 refcount_inc(&entry->refcount); 89 mutex_unlock(&ah_cache->lock); 90 return entry; 91 } 92 93 entry = kzalloc_obj(*entry); 94 if (!entry) { 95 mutex_unlock(&ah_cache->lock); 96 return ERR_PTR(-ENOMEM); 97 } 98 99 memcpy(entry->key.gid, gid, sizeof(entry->key.gid)); 100 entry->key.pd = pd; 101 refcount_set(&entry->refcount, 1); 102 mutex_init(&entry->lock); 103 104 err = rhashtable_insert_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params); 105 if (err) { 106 mutex_destroy(&entry->lock); 107 kfree(entry); 108 mutex_unlock(&ah_cache->lock); 109 return ERR_PTR(err); 110 } 111 112 mutex_unlock(&ah_cache->lock); 113 return entry; 114 } 115 116 /** 117 * efa_ah_cache_put - Put a refcount of an AH cache entry 118 * @ah_cache: AH cache 119 * @entry: AH cache entry 120 * 121 * Drop the refcount. If it reaches zero, remove the entry from the hashtable 122 * and free it. 123 */ 124 void efa_ah_cache_put(struct efa_ah_cache *ah_cache, struct efa_ah_cache_entry *entry) 125 { 126 if (!refcount_dec_and_mutex_lock(&entry->refcount, &ah_cache->lock)) 127 return; 128 129 /* AH cache lock is held here */ 130 rhashtable_remove_fast(&ah_cache->hashtable, &entry->linkage, ah_cache_params); 131 mutex_unlock(&ah_cache->lock); 132 133 mutex_destroy(&entry->lock); 134 kfree(entry); 135 } 136