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>
16 #include "afs/param.h"
18 #include <afs/param.h>
25 #include "afs/sysincludes.h"
26 #include "afsincludes.h"
29 #include <sys/types.h>
36 * We initialize rxi_connectionCache at compile time, so there is no
37 * need to call queue_Init(&rxi_connectionCache).
39 static struct rx_queue rxi_connectionCache = { &rxi_connectionCache,
43 #ifdef AFS_PTHREAD_ENV
46 * This mutex protects the following global variables:
50 pthread_mutex_t rxi_connCacheMutex;
51 #define LOCK_CONN_CACHE assert(pthread_mutex_lock(&rxi_connCacheMutex)==0)
52 #define UNLOCK_CONN_CACHE assert(pthread_mutex_unlock(&rxi_connCacheMutex)==0)
54 #define LOCK_CONN_CACHE
55 #define UNLOCK_CONN_CACHE
56 #endif /* AFS_PTHREAD_ENV */
59 * convenience typedef - all the stuff that makes up an rx
63 typedef struct rx_connParts {
64 unsigned int hostAddr;
66 unsigned short service;
67 struct rx_securityClass *securityObject;
69 } rx_connParts_t, *rx_connParts_p;
72 * Each element in the cache is represented by the following
73 * structure. I use an rx_queue to manipulate the cache entries.
74 * inUse tracks the number of calls within this connection that
78 typedef struct cache_entry {
79 struct rx_queue queue_header;
80 struct rx_connection *conn;
84 } cache_entry_t, *cache_entry_p;
89 * rxi_connCacheMutex is the only mutex used by these functions. It should
90 * be locked when manipulating the connection cache.
98 * Compare two connections for equality
102 rxi_CachedConnectionsEqual(rx_connParts_p a, rx_connParts_p b)
104 return ((a->hostAddr == b->hostAddr) && (a->port == b->port)
105 && (a->service == b->service)
106 && (a->securityObject == b->securityObject)
107 && (a->securityIndex == b->securityIndex));
111 * Check the cache for a connection
115 rxi_FindCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
118 cache_entry_p cacheConn, nCacheConn;
120 for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
121 if ((rxi_CachedConnectionsEqual(parts, &cacheConn->parts))
122 && (cacheConn->inUse < RX_MAXCALLS)
123 && (cacheConn->hasError == 0)) {
125 *conn = cacheConn->conn;
134 * Create an rx connection and return it to the caller
138 * Add a connection to the cache keeping track of the input
139 * arguments that were used to create it
143 rxi_AddCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
145 cache_entry_p new_entry;
147 if ((new_entry = (cache_entry_p) malloc(sizeof(cache_entry_t)))) {
148 new_entry->conn = *conn;
149 new_entry->parts = *parts;
150 new_entry->inUse = 1;
151 new_entry->hasError = 0;
152 queue_Prepend(&rxi_connectionCache, new_entry);
156 * if malloc fails, we fail silently
163 * Get a connection by first checking to see if any matching
164 * available connections are stored in the cache.
165 * Create a new connection if none are currently available.
169 rxi_GetCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
174 * Look to see if we have a cached connection
176 * Note - we hold the connection cache mutex for the entire
177 * search/create/enter operation. We want this entire block to
178 * be atomic so that in the event four threads all pass through
179 * this code at the same time only one actually allocates the
180 * new connection and the other three experience cache hits.
182 * We intentionally slow down throughput in order to
183 * increase the frequency of cache hits.
187 if (!rxi_FindCachedConnection(parts, conn)) {
189 * Create a new connection and enter it in the cache
192 rx_NewConnection(parts->hostAddr, parts->port, parts->service,
193 parts->securityObject, parts->securityIndex))) {
194 rxi_AddCachedConnection(parts, conn);
204 * Delete remaining entries in the cache.
205 * Note - only call this routine from rx_Finalize.
209 rxi_DeleteCachedConnections(void)
211 cache_entry_p cacheConn, nCacheConn;
214 for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
217 queue_Remove(cacheConn);
218 rxi_DestroyConnection(cacheConn->conn);
229 * Hand back the caller a connection
230 * The function has the same foot print and return values
231 * as rx_NewConnection.
234 struct rx_connection *
235 rx_GetCachedConnection(unsigned int remoteAddr, unsigned short port,
236 unsigned short service,
237 struct rx_securityClass *securityObject,
240 struct rx_connection *conn = NULL;
241 rx_connParts_t parts;
243 parts.hostAddr = remoteAddr;
245 parts.service = service;
246 parts.securityObject = securityObject;
247 parts.securityIndex = securityIndex;
249 * Get a connection matching the user's request
250 * note we don't propagate the error returned by rxi_GetCachedConnection
251 * since rx_NewConnection doesn't return errors either.
253 rxi_GetCachedConnection(&parts, &conn);
259 * Release a connection we previously handed out
263 rx_ReleaseCachedConnection(struct rx_connection *conn)
265 cache_entry_p cacheConn, nCacheConn;
268 for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
269 if (conn == cacheConn->conn) {
272 * check to see if the connection is in error.
273 * If it is, mark its cache entry so it won't be
274 * given out subsequently. If nobody is using it, delete
277 if (rx_ConnError(conn)) {
278 cacheConn->hasError = 1;
279 if (cacheConn->inUse == 0) {
280 queue_Remove(cacheConn);
281 rxi_DestroyConnection(cacheConn->conn);