2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
11 * Implement caching of rx connections.
14 #include <afsconfig.h>
15 #include <afs/param.h>
25 * We initialize rxi_connectionCache at compile time, so there is no
26 * need to call queue_Init(&rxi_connectionCache).
28 static struct opr_queue rxi_connectionCache = { &rxi_connectionCache,
32 #ifdef AFS_PTHREAD_ENV
35 * This mutex protects the following global variables:
39 afs_kmutex_t rxi_connCacheMutex;
40 #define LOCK_CONN_CACHE MUTEX_ENTER(&rxi_connCacheMutex)
41 #define UNLOCK_CONN_CACHE MUTEX_EXIT(&rxi_connCacheMutex)
43 #define LOCK_CONN_CACHE
44 #define UNLOCK_CONN_CACHE
45 #endif /* AFS_PTHREAD_ENV */
48 * convenience typedef - all the stuff that makes up an rx
52 typedef struct rx_connParts {
53 unsigned int hostAddr;
55 unsigned short service;
56 struct rx_securityClass *securityObject;
58 } rx_connParts_t, *rx_connParts_p;
61 * Each element in the cache is represented by the following
62 * structure. I use an opr_queue to manipulate the cache entries.
63 * inUse tracks the number of calls within this connection that
67 typedef struct cache_entry {
68 struct opr_queue queue;
69 struct rx_connection *conn;
73 } cache_entry_t, *cache_entry_p;
78 * rxi_connCacheMutex is the only mutex used by these functions. It should
79 * be locked when manipulating the connection cache.
87 * Compare two connections for equality
91 rxi_CachedConnectionsEqual(rx_connParts_p a, rx_connParts_p b)
93 return ((a->hostAddr == b->hostAddr) && (a->port == b->port)
94 && (a->service == b->service)
95 && (a->securityObject == b->securityObject)
96 && (a->securityIndex == b->securityIndex));
100 * Check the cache for a connection
104 rxi_FindCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
107 struct opr_queue *cursor;
109 for (opr_queue_Scan(&rxi_connectionCache, cursor)) {
110 struct cache_entry *cacheConn
111 = opr_queue_Entry(cursor, struct cache_entry, queue);
112 if ((rxi_CachedConnectionsEqual(parts, &cacheConn->parts))
113 && (cacheConn->inUse < RX_MAXCALLS)
114 && (cacheConn->hasError == 0)) {
116 *conn = cacheConn->conn;
125 * Create an rx connection and return it to the caller
129 * Add a connection to the cache keeping track of the input
130 * arguments that were used to create it
134 rxi_AddCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
136 cache_entry_p new_entry;
138 if ((new_entry = malloc(sizeof(cache_entry_t)))) {
139 new_entry->conn = *conn;
140 new_entry->parts = *parts;
141 new_entry->inUse = 1;
142 new_entry->hasError = 0;
143 opr_queue_Prepend(&rxi_connectionCache, &new_entry->queue);
145 (*conn)->flags |= RX_CONN_CACHED;
148 * if malloc fails, we fail silently
155 * Get a connection by first checking to see if any matching
156 * available connections are stored in the cache.
157 * Create a new connection if none are currently available.
161 rxi_GetCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
166 * Look to see if we have a cached connection
168 * Note - we hold the connection cache mutex for the entire
169 * search/create/enter operation. We want this entire block to
170 * be atomic so that in the event four threads all pass through
171 * this code at the same time only one actually allocates the
172 * new connection and the other three experience cache hits.
174 * We intentionally slow down throughput in order to
175 * increase the frequency of cache hits.
179 if (!rxi_FindCachedConnection(parts, conn)) {
181 * Create a new connection and enter it in the cache
184 rx_NewConnection(parts->hostAddr, parts->port, parts->service,
185 parts->securityObject, parts->securityIndex))) {
186 rxi_AddCachedConnection(parts, conn);
196 * Delete remaining entries in the cache.
197 * Note - only call this routine from rx_Finalize.
201 rxi_DeleteCachedConnections(void)
203 struct opr_queue *cursor, *store;
206 for (opr_queue_ScanSafe(&rxi_connectionCache, cursor, store)) {
207 struct cache_entry *cacheConn
208 = opr_queue_Entry(cursor, struct cache_entry, queue);
211 opr_queue_Remove(&cacheConn->queue);
212 rxi_DestroyConnection(cacheConn->conn);
223 * Hand back the caller a connection
224 * The function has the same foot print and return values
225 * as rx_NewConnection.
228 struct rx_connection *
229 rx_GetCachedConnection(unsigned int remoteAddr, unsigned short port,
230 unsigned short service,
231 struct rx_securityClass *securityObject,
234 struct rx_connection *conn = NULL;
235 rx_connParts_t parts;
237 parts.hostAddr = remoteAddr;
239 parts.service = service;
240 parts.securityObject = securityObject;
241 parts.securityIndex = securityIndex;
243 * Get a connection matching the user's request
244 * note we don't propagate the error returned by rxi_GetCachedConnection
245 * since rx_NewConnection doesn't return errors either.
247 rxi_GetCachedConnection(&parts, &conn);
253 * Release a connection we previously handed out
257 rx_ReleaseCachedConnection(struct rx_connection *conn)
259 struct opr_queue *cursor, *store;
263 /* Check if the caller is asking us to release a conn that did NOT come
264 * from the connection cache. If so, don't bother searching the cache
265 * because the connection won't be found or destroyed. Since we return
266 * void, the caller must assume the connection _has_ been found and
267 * destroyed. So to avoid leaking the connection, just destroy it now and
270 if (!(conn->flags & RX_CONN_CACHED)) {
271 rxi_DestroyConnection(conn);
275 for (opr_queue_ScanSafe(&rxi_connectionCache, cursor, store)) {
276 struct cache_entry *cacheConn
277 = opr_queue_Entry(cursor, struct cache_entry, queue);
279 if (conn == cacheConn->conn) {
282 * check to see if the connection is in error.
283 * If it is, mark its cache entry so it won't be
284 * given out subsequently. If nobody is using it, delete
287 if (rx_ConnError(conn)) {
288 cacheConn->hasError = 1;
289 if (cacheConn->inUse == 0) {
290 opr_queue_Remove(&cacheConn->queue);
291 cacheConn->conn->flags &= ~RX_CONN_CACHED;
292 rxi_DestroyConnection(cacheConn->conn);