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
);
147 * if malloc fails, we fail silently
154 * Get a connection by first checking to see if any matching
155 * available connections are stored in the cache.
156 * Create a new connection if none are currently available.
160 rxi_GetCachedConnection(rx_connParts_p parts
, struct rx_connection
**conn
)
165 * Look to see if we have a cached connection
167 * Note - we hold the connection cache mutex for the entire
168 * search/create/enter operation. We want this entire block to
169 * be atomic so that in the event four threads all pass through
170 * this code at the same time only one actually allocates the
171 * new connection and the other three experience cache hits.
173 * We intentionally slow down throughput in order to
174 * increase the frequency of cache hits.
178 if (!rxi_FindCachedConnection(parts
, conn
)) {
180 * Create a new connection and enter it in the cache
183 rx_NewConnection(parts
->hostAddr
, parts
->port
, parts
->service
,
184 parts
->securityObject
, parts
->securityIndex
))) {
185 rxi_AddCachedConnection(parts
, conn
);
195 * Delete remaining entries in the cache.
196 * Note - only call this routine from rx_Finalize.
200 rxi_DeleteCachedConnections(void)
202 struct opr_queue
*cursor
, *store
;
205 for (opr_queue_ScanSafe(&rxi_connectionCache
, cursor
, store
)) {
206 struct cache_entry
*cacheConn
207 = opr_queue_Entry(cursor
, struct cache_entry
, queue
);
210 opr_queue_Remove(&cacheConn
->queue
);
211 rxi_DestroyConnection(cacheConn
->conn
);
222 * Hand back the caller a connection
223 * The function has the same foot print and return values
224 * as rx_NewConnection.
227 struct rx_connection
*
228 rx_GetCachedConnection(unsigned int remoteAddr
, unsigned short port
,
229 unsigned short service
,
230 struct rx_securityClass
*securityObject
,
233 struct rx_connection
*conn
= NULL
;
234 rx_connParts_t parts
;
236 parts
.hostAddr
= remoteAddr
;
238 parts
.service
= service
;
239 parts
.securityObject
= securityObject
;
240 parts
.securityIndex
= securityIndex
;
242 * Get a connection matching the user's request
243 * note we don't propagate the error returned by rxi_GetCachedConnection
244 * since rx_NewConnection doesn't return errors either.
246 rxi_GetCachedConnection(&parts
, &conn
);
252 * Release a connection we previously handed out
256 rx_ReleaseCachedConnection(struct rx_connection
*conn
)
258 struct opr_queue
*cursor
, *store
;
261 for (opr_queue_ScanSafe(&rxi_connectionCache
, cursor
, store
)) {
262 struct cache_entry
*cacheConn
263 = opr_queue_Entry(cursor
, struct cache_entry
, queue
);
265 if (conn
== cacheConn
->conn
) {
268 * check to see if the connection is in error.
269 * If it is, mark its cache entry so it won't be
270 * given out subsequently. If nobody is using it, delete
273 if (rx_ConnError(conn
)) {
274 cacheConn
->hasError
= 1;
275 if (cacheConn
->inUse
== 0) {
276 opr_queue_Remove(&cacheConn
->queue
);
277 rxi_DestroyConnection(cacheConn
->conn
);