Remove the RCSID macro
[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 #ifdef UKERNEL
16 #include "afs/param.h"
17 #else
18 #include <afs/param.h>
19 #endif
20
21
22 #ifdef UKERNEL
23 #include "afs/sysincludes.h"
24 #include "afsincludes.h"
25 #include "rx/rx.h"
26 #else /* ! UKERNEL */
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include "rx.h"
31 #endif /* UKERNEL */
32
33 /*
34  * We initialize rxi_connectionCache at compile time, so there is no
35  * need to call queue_Init(&rxi_connectionCache).
36  */
37 static struct rx_queue rxi_connectionCache = { &rxi_connectionCache,
38     &rxi_connectionCache
39 };
40
41 #ifdef AFS_PTHREAD_ENV
42 #include <pthread.h>
43 /*
44  * This mutex protects the following global variables:
45  * rxi_connectionCache
46  */
47
48 afs_kmutex_t rxi_connCacheMutex;
49 #define LOCK_CONN_CACHE MUTEX_ENTER(&rxi_connCacheMutex)
50 #define UNLOCK_CONN_CACHE MUTEX_EXIT(&rxi_connCacheMutex)
51 #else
52 #define LOCK_CONN_CACHE
53 #define UNLOCK_CONN_CACHE
54 #endif /* AFS_PTHREAD_ENV */
55
56 /*
57  * convenience typedef - all the stuff that makes up an rx
58  * connection
59  */
60
61 typedef struct rx_connParts {
62     unsigned int hostAddr;
63     unsigned short port;
64     unsigned short service;
65     struct rx_securityClass *securityObject;
66     int securityIndex;
67 } rx_connParts_t, *rx_connParts_p;
68
69 /*
70  * Each element in the cache is represented by the following
71  * structure.  I use an rx_queue to manipulate the cache entries.
72  * inUse tracks the number of calls within this connection that
73  * we know are in use.
74  */
75
76 typedef struct cache_entry {
77     struct rx_queue queue_header;
78     struct rx_connection *conn;
79     rx_connParts_t parts;
80     int inUse;
81     int hasError;
82 } cache_entry_t, *cache_entry_p;
83
84 /*
85  * Locking hierarchy
86  *
87  *    rxi_connCacheMutex is the only mutex used by these functions.  It should
88  *    be locked when manipulating the connection cache.
89  */
90
91 /*
92  * Internal functions
93  */
94
95 /*
96  * Compare two connections for equality
97  */
98
99 static int
100 rxi_CachedConnectionsEqual(rx_connParts_p a, rx_connParts_p b)
101 {
102     return ((a->hostAddr == b->hostAddr) && (a->port == b->port)
103             && (a->service == b->service)
104             && (a->securityObject == b->securityObject)
105             && (a->securityIndex == b->securityIndex));
106 }
107
108 /*
109  * Check the cache for a connection
110  */
111
112 static int
113 rxi_FindCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
114 {
115     int error = 0;
116     cache_entry_p cacheConn, nCacheConn;
117
118     for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
119         if ((rxi_CachedConnectionsEqual(parts, &cacheConn->parts))
120             && (cacheConn->inUse < RX_MAXCALLS)
121             && (cacheConn->hasError == 0)) {
122             cacheConn->inUse++;
123             *conn = cacheConn->conn;
124             error = 1;
125             break;
126         }
127     }
128     return error;
129 }
130
131 /*
132  * Create an rx connection and return it to the caller
133  */
134
135 /*
136  * Add a connection to the cache keeping track of the input
137  * arguments that were used to create it
138  */
139
140 static void
141 rxi_AddCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
142 {
143     cache_entry_p new_entry;
144
145     if ((new_entry = (cache_entry_p) malloc(sizeof(cache_entry_t)))) {
146         new_entry->conn = *conn;
147         new_entry->parts = *parts;
148         new_entry->inUse = 1;
149         new_entry->hasError = 0;
150         queue_Prepend(&rxi_connectionCache, new_entry);
151     }
152
153     /*
154      * if malloc fails, we fail silently
155      */
156
157     return;
158 }
159
160 /*
161  * Get a connection by first checking to see if any matching
162  * available connections are stored in the cache.
163  * Create a new connection if none are currently available.
164  */
165
166 static int
167 rxi_GetCachedConnection(rx_connParts_p parts, struct rx_connection **conn)
168 {
169     int error = 0;
170
171     /*
172      * Look to see if we have a cached connection
173      *
174      * Note - we hold the connection cache mutex for the entire
175      * search/create/enter operation.  We want this entire block to
176      * be atomic so that in the event four threads all pass through
177      * this code at the same time only one actually allocates the
178      * new connection and the other three experience cache hits.
179      *
180      * We intentionally slow down throughput in order to
181      * increase the frequency of cache hits.
182      */
183
184     LOCK_CONN_CACHE;
185     if (!rxi_FindCachedConnection(parts, conn)) {
186         /*
187          * Create a new connection and enter it in the cache
188          */
189         if ((*conn =
190              rx_NewConnection(parts->hostAddr, parts->port, parts->service,
191                               parts->securityObject, parts->securityIndex))) {
192             rxi_AddCachedConnection(parts, conn);
193         } else {
194             error = 1;
195         }
196     }
197     UNLOCK_CONN_CACHE;
198     return error;
199 }
200
201 /*
202  * Delete remaining entries in the cache.
203  * Note - only call this routine from rx_Finalize.
204  */
205
206 void
207 rxi_DeleteCachedConnections(void)
208 {
209     cache_entry_p cacheConn, nCacheConn;
210
211     LOCK_CONN_CACHE;
212     for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
213         if (!cacheConn)
214             break;
215         queue_Remove(cacheConn);
216         rxi_DestroyConnection(cacheConn->conn);
217         free(cacheConn);
218     }
219     UNLOCK_CONN_CACHE;
220 }
221
222 /*
223  * External functions
224  */
225
226 /*
227  * Hand back the caller a connection
228  * The function has the same foot print and return values
229  * as rx_NewConnection.
230  */
231
232 struct rx_connection *
233 rx_GetCachedConnection(unsigned int remoteAddr, unsigned short port,
234                        unsigned short service,
235                        struct rx_securityClass *securityObject,
236                        int securityIndex)
237 {
238     struct rx_connection *conn = NULL;
239     rx_connParts_t parts;
240
241     parts.hostAddr = remoteAddr;
242     parts.port = port;
243     parts.service = service;
244     parts.securityObject = securityObject;
245     parts.securityIndex = securityIndex;
246     /*
247      * Get a connection matching the user's request
248      * note we don't propagate the error returned by rxi_GetCachedConnection
249      * since rx_NewConnection doesn't return errors either.
250      */
251     rxi_GetCachedConnection(&parts, &conn);
252
253     return conn;
254 }
255
256 /*
257  * Release a connection we previously handed out
258  */
259
260 void
261 rx_ReleaseCachedConnection(struct rx_connection *conn)
262 {
263     cache_entry_p cacheConn, nCacheConn;
264
265     LOCK_CONN_CACHE;
266     for (queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
267         if (conn == cacheConn->conn) {
268             cacheConn->inUse--;
269             /*
270              * check to see if the connection is in error.
271              * If it is, mark its cache entry so it won't be 
272              * given out subsequently.  If nobody is using it, delete
273              * it from the cache
274              */
275             if (rx_ConnError(conn)) {
276                 cacheConn->hasError = 1;
277                 if (cacheConn->inUse == 0) {
278                     queue_Remove(cacheConn);
279                     rxi_DestroyConnection(cacheConn->conn);
280                     free(cacheConn);
281                 }
282             }
283             break;
284         }
285     }
286     UNLOCK_CONN_CACHE;
287 }