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.
15 #include "../afs/param.h"
16 #include "../afs/sysincludes.h"
17 #include "../afs/afsincludes.h"
20 #include <afs/param.h>
21 #include <sys/types.h>
27 extern void rxi_DestroyConnection(struct rx_connection *);
29 * We initialize rxi_connectionCache at compile time, so there is no
30 * need to call queue_Init(&rxi_connectionCache).
32 static struct rx_queue rxi_connectionCache = { &rxi_connectionCache,
33 &rxi_connectionCache };
35 #ifdef AFS_PTHREAD_ENV
38 * This mutex protects the following global variables:
42 pthread_mutex_t rxi_connCacheMutex;
43 #define LOCK_CONN_CACHE assert(pthread_mutex_lock(&rxi_connCacheMutex)==0);
44 #define UNLOCK_CONN_CACHE assert(pthread_mutex_unlock(&rxi_connCacheMutex)==0);
46 #define LOCK_CONN_CACHE
47 #define UNLOCK_CONN_CACHE
48 #endif /* AFS_PTHREAD_ENV */
51 * convenience typedef - all the stuff that makes up an rx
55 typedef struct rx_connParts {
56 unsigned int hostAddr;
58 unsigned short service;
59 struct rx_securityClass *securityObject;
61 } rx_connParts_t, *rx_connParts_p;
64 * Each element in the cache is represented by the following
65 * structure. I use an rx_queue to manipulate the cache entries.
66 * inUse tracks the number of calls within this connection that
70 typedef struct cache_entry {
71 struct rx_queue queue_header;
72 struct rx_connection *conn;
76 } cache_entry_t, *cache_entry_p;
81 * rxi_connCacheMutex is the only mutex used by these functions. It should
82 * be locked when manipulating the connection cache.
90 * Compare two connections for equality
93 static int rxi_CachedConnectionsEqual(rx_connParts_p a, rx_connParts_p b) {
94 return ((a->hostAddr == b->hostAddr) &&
95 (a->port == b->port) &&
96 (a->service == b->service) &&
97 (a->securityObject == b->securityObject) &&
98 (a->securityIndex == b->securityIndex));
102 * Check the cache for a connection
105 static int rxi_FindCachedConnection(rx_connParts_p parts,
106 struct rx_connection **conn) {
108 cache_entry_p cacheConn, nCacheConn;
110 for(queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
111 if((rxi_CachedConnectionsEqual(parts, &cacheConn->parts)) &&
112 (cacheConn->inUse < RX_MAXCALLS) &&
113 (cacheConn->hasError == 0)) {
115 *conn = cacheConn->conn;
124 * Create an rx connection and return it to the caller
128 * Add a connection to the cache keeping track of the input
129 * arguments that were used to create it
132 static void rxi_AddCachedConnection(rx_connParts_p parts,
133 struct rx_connection **conn) {
134 cache_entry_p new_entry;
136 if(new_entry = (cache_entry_p) malloc(sizeof(cache_entry_t))) {
137 new_entry->conn = *conn;
138 new_entry->parts = *parts;
139 new_entry->inUse = 1;
140 new_entry->hasError = 0;
141 queue_Prepend(&rxi_connectionCache, new_entry);
145 * if malloc fails, we fail silently
152 * Get a connection by first checking to see if any matching
153 * available connections are stored in the cache.
154 * Create a new connection if none are currently available.
157 static int rxi_GetCachedConnection(rx_connParts_p parts,
158 struct rx_connection **conn) {
162 * Look to see if we have a cached connection
164 * Note - we hold the connection cache mutex for the entire
165 * search/create/enter operation. We want this entire block to
166 * be atomic so that in the event four threads all pass through
167 * this code at the same time only one actually allocates the
168 * new connection and the other three experience cache hits.
170 * We intentionally slow down throughput in order to
171 * increase the frequency of cache hits.
175 if (!rxi_FindCachedConnection(parts, conn)) {
177 * Create a new connection and enter it in the cache
179 if ((*conn = rx_NewConnection(parts->hostAddr, parts->port,
180 parts->service, parts->securityObject,
181 parts->securityIndex))) {
182 rxi_AddCachedConnection(parts, conn);
193 * Delete remaining entries in the cache.
194 * Note - only call this routine from rx_Finalize.
197 void rxi_DeleteCachedConnections() {
198 cache_entry_p cacheConn, nCacheConn;
201 for(queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
202 if(!cacheConn) break;
203 queue_Remove(cacheConn);
204 rxi_DestroyConnection(cacheConn->conn);
215 * Hand back the caller a connection
216 * The function has the same foot print and return values
217 * as rx_NewConnection.
220 struct rx_connection *rx_GetCachedConnection(unsigned int remoteAddr,
222 unsigned short service,
223 struct rx_securityClass *securityObject,
225 struct rx_connection *conn = NULL;
226 rx_connParts_t parts;
228 parts.hostAddr = remoteAddr;
230 parts.service = service;
231 parts.securityObject = securityObject;
232 parts.securityIndex = securityIndex;
234 * Get a connection matching the user's request
235 * note we don't propagate the error returned by rxi_GetCachedConnection
236 * since rx_NewConnection doesn't return errors either.
238 rxi_GetCachedConnection(&parts, &conn);
244 * Release a connection we previously handed out
247 void rx_ReleaseCachedConnection(struct rx_connection *conn) {
248 cache_entry_p cacheConn, nCacheConn;
251 for(queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
252 if(conn == cacheConn->conn) {
255 * check to see if the connection is in error.
256 * If it is, mark its cache entry so it won't be
257 * given out subsequently. If nobody is using it, delete
260 if(rx_ConnError(conn)) {
261 cacheConn->hasError = 1;
262 if(cacheConn->inUse == 0) {
263 queue_Remove(cacheConn);
264 rxi_DestroyConnection(cacheConn->conn);