Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / rx / rx_conncache.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
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
8 */
9
10 /*
11 * Implement caching of rx connections.
12 */
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 #include <roken.h>
18 #include <afs/opr.h>
19
20 #include "rx.h"
21
22 #include "rx_conn.h"
23
24 /*
25 * We initialize rxi_connectionCache at compile time, so there is no
26 * need to call queue_Init(&rxi_connectionCache).
27 */
28 static struct opr_queue rxi_connectionCache = { &rxi_connectionCache,
29 &rxi_connectionCache
30 };
31
32 #ifdef AFS_PTHREAD_ENV
33 #include <pthread.h>
34 /*
35 * This mutex protects the following global variables:
36 * rxi_connectionCache
37 */
38
39 afs_kmutex_t rxi_connCacheMutex;
40 #define LOCK_CONN_CACHE MUTEX_ENTER(&rxi_connCacheMutex)
41 #define UNLOCK_CONN_CACHE MUTEX_EXIT(&rxi_connCacheMutex)
42 #else
43 #define LOCK_CONN_CACHE
44 #define UNLOCK_CONN_CACHE
45 #endif /* AFS_PTHREAD_ENV */
46
47 /*
48 * convenience typedef - all the stuff that makes up an rx
49 * connection
50 */
51
52 typedef struct rx_connParts {
53 unsigned int hostAddr;
54 unsigned short port;
55 unsigned short service;
56 struct rx_securityClass *securityObject;
57 int securityIndex;
58 } rx_connParts_t, *rx_connParts_p;
59
60 /*
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
64 * we know are in use.
65 */
66
67 typedef struct cache_entry {
68 struct opr_queue queue;
69 struct rx_connection *conn;
70 rx_connParts_t parts;
71 int inUse;
72 int hasError;
73 } cache_entry_t, *cache_entry_p;
74
75 /*
76 * Locking hierarchy
77 *
78 * rxi_connCacheMutex is the only mutex used by these functions. It should
79 * be locked when manipulating the connection cache.
80 */
81
82 /*
83 * Internal functions
84 */
85
86 /*
87 * Compare two connections for equality
88 */
89
90 static int
91 rxi_CachedConnectionsEqual(rx_connParts_p a, rx_connParts_p b)
92 {
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));
97 }
98
99 /*
100 * Check the cache for a connection
101 */
102
103 static int
104 rxi_FindCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
105 {
106 int error = 0;
107 struct opr_queue *cursor;
108
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)) {
115 cacheConn->inUse++;
116 *conn = cacheConn->conn;
117 error = 1;
118 break;
119 }
120 }
121 return error;
122 }
123
124 /*
125 * Create an rx connection and return it to the caller
126 */
127
128 /*
129 * Add a connection to the cache keeping track of the input
130 * arguments that were used to create it
131 */
132
133 static void
134 rxi_AddCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
135 {
136 cache_entry_p new_entry;
137
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);
144 }
145
146 /*
147 * if malloc fails, we fail silently
148 */
149
150 return;
151 }
152
153 /*
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.
157 */
158
159 static int
160 rxi_GetCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
161 {
162 int error = 0;
163
164 /*
165 * Look to see if we have a cached connection
166 *
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.
172 *
173 * We intentionally slow down throughput in order to
174 * increase the frequency of cache hits.
175 */
176
177 LOCK_CONN_CACHE;
178 if (!rxi_FindCachedConnection(parts, conn)) {
179 /*
180 * Create a new connection and enter it in the cache
181 */
182 if ((*conn =
183 rx_NewConnection(parts->hostAddr, parts->port, parts->service,
184 parts->securityObject, parts->securityIndex))) {
185 rxi_AddCachedConnection(parts, conn);
186 } else {
187 error = 1;
188 }
189 }
190 UNLOCK_CONN_CACHE;
191 return error;
192 }
193
194 /*
195 * Delete remaining entries in the cache.
196 * Note - only call this routine from rx_Finalize.
197 */
198
199 void
200 rxi_DeleteCachedConnections(void)
201 {
202 struct opr_queue *cursor, *store;
203
204 LOCK_CONN_CACHE;
205 for (opr_queue_ScanSafe(&rxi_connectionCache, cursor, store)) {
206 struct cache_entry *cacheConn
207 = opr_queue_Entry(cursor, struct cache_entry, queue);
208 if (!cacheConn)
209 break;
210 opr_queue_Remove(&cacheConn->queue);
211 rxi_DestroyConnection(cacheConn->conn);
212 free(cacheConn);
213 }
214 UNLOCK_CONN_CACHE;
215 }
216
217 /*
218 * External functions
219 */
220
221 /*
222 * Hand back the caller a connection
223 * The function has the same foot print and return values
224 * as rx_NewConnection.
225 */
226
227 struct rx_connection *
228 rx_GetCachedConnection(unsigned int remoteAddr, unsigned short port,
229 unsigned short service,
230 struct rx_securityClass *securityObject,
231 int securityIndex)
232 {
233 struct rx_connection *conn = NULL;
234 rx_connParts_t parts;
235
236 parts.hostAddr = remoteAddr;
237 parts.port = port;
238 parts.service = service;
239 parts.securityObject = securityObject;
240 parts.securityIndex = securityIndex;
241 /*
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.
245 */
246 rxi_GetCachedConnection(&parts, &conn);
247
248 return conn;
249 }
250
251 /*
252 * Release a connection we previously handed out
253 */
254
255 void
256 rx_ReleaseCachedConnection(struct rx_connection *conn)
257 {
258 struct opr_queue *cursor, *store;
259
260 LOCK_CONN_CACHE;
261 for (opr_queue_ScanSafe(&rxi_connectionCache, cursor, store)) {
262 struct cache_entry *cacheConn
263 = opr_queue_Entry(cursor, struct cache_entry, queue);
264
265 if (conn == cacheConn->conn) {
266 cacheConn->inUse--;
267 /*
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
271 * it from the cache
272 */
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);
278 free(cacheConn);
279 }
280 }
281 break;
282 }
283 }
284 UNLOCK_CONN_CACHE;
285 }