xref: /linux/drivers/infiniband/hw/efa/efa_ah_cache.c (revision 21bd0802cd3f58b656065f1be236694c40588c3a)
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