allow-rx-shutdown-for-userlevel-20010212
[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 #ifdef UKERNEL
15 #include "../afs/param.h"
16 #include "../afs/sysincludes.h"
17 #include "../afs/afsincludes.h"
18 #include "../rx/rx.h"
19 #else /* UKERNEL */
20 #include <afs/param.h>
21 #include <sys/types.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include "rx.h"
25 #endif /* UKERNEL */
26
27 extern void rxi_DestroyConnection(struct rx_connection *);
28 /*
29  * We initialize rxi_connectionCache at compile time, so there is no
30  * need to call queue_Init(&rxi_connectionCache).
31  */
32 static struct rx_queue rxi_connectionCache = { &rxi_connectionCache,
33                                                &rxi_connectionCache };
34
35 #ifdef AFS_PTHREAD_ENV
36 #include <pthread.h>
37 /*
38  * This mutex protects the following global variables:
39  * rxi_connectionCache
40  */
41
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);
45 #else
46 #define LOCK_CONN_CACHE
47 #define UNLOCK_CONN_CACHE
48 #endif /* AFS_PTHREAD_ENV */
49
50 /*
51  * convenience typedef - all the stuff that makes up an rx
52  * connection
53  */
54  
55 typedef struct rx_connParts {
56     unsigned int hostAddr;
57     unsigned short port;
58     unsigned short service;
59     struct rx_securityClass *securityObject;
60     int securityIndex;
61 } rx_connParts_t, *rx_connParts_p;
62  
63 /*
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
67  * we know are in use.
68  */
69  
70 typedef struct cache_entry {
71     struct rx_queue queue_header;
72     struct rx_connection *conn;
73     rx_connParts_t parts;
74     int inUse;
75     int hasError;
76 } cache_entry_t, *cache_entry_p;
77
78 /*
79  * Locking hierarchy
80  *
81  *    rxi_connCacheMutex is the only mutex used by these functions.  It should
82  *    be locked when manipulating the connection cache.
83  */
84
85 /*
86  * Internal functions
87  */
88  
89 /*
90  * Compare two connections for equality
91  */
92  
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));
99 }
100
101 /*
102  * Check the cache for a connection
103  */
104  
105 static int rxi_FindCachedConnection(rx_connParts_p parts,
106                                 struct rx_connection **conn) {
107     int error = 0;
108     cache_entry_p cacheConn, nCacheConn;
109  
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)) {
114            cacheConn->inUse++;
115            *conn = cacheConn->conn;
116            error = 1;
117            break;
118         }
119     }
120     return error;
121 }
122  
123 /*
124  * Create an rx connection and return it to the caller
125  */
126  
127 /*
128  * Add a connection to the cache keeping track of the input
129  * arguments that were used to create it
130  */
131  
132 static void rxi_AddCachedConnection(rx_connParts_p parts,
133                                 struct rx_connection **conn) {
134     cache_entry_p new_entry;
135  
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);
142     }
143  
144     /*
145      * if malloc fails, we fail silently
146      */
147
148     return;
149 }
150  
151 /*
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.
155  */
156  
157 static int rxi_GetCachedConnection(rx_connParts_p parts,
158                                 struct rx_connection **conn) {
159     int error = 0;
160  
161     /*
162      * Look to see if we have a cached connection
163      *
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.
169      *
170      * We intentionally slow down throughput in order to
171      * increase the frequency of cache hits.
172      */
173  
174     LOCK_CONN_CACHE
175     if (!rxi_FindCachedConnection(parts, conn)) {
176         /*
177          * Create a new connection and enter it in the cache
178          */
179         if ((*conn = rx_NewConnection(parts->hostAddr, parts->port,
180                                       parts->service, parts->securityObject,
181                                       parts->securityIndex))) {
182             rxi_AddCachedConnection(parts, conn);
183         }
184         else {
185             error = 1;
186         }
187     }
188     UNLOCK_CONN_CACHE
189     return error;
190 }
191
192 /*
193  * Delete remaining entries in the cache.
194  * Note - only call this routine from rx_Finalize.
195  */
196
197 void rxi_DeleteCachedConnections() {
198     cache_entry_p cacheConn, nCacheConn;
199  
200     LOCK_CONN_CACHE
201     for(queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
202         if(!cacheConn) break;
203         queue_Remove(cacheConn);
204         rxi_DestroyConnection(cacheConn->conn);
205         free(cacheConn);
206     }
207     UNLOCK_CONN_CACHE
208 }
209  
210 /*
211  * External functions
212  */
213  
214 /*
215  * Hand back the caller a connection
216  * The function has the same foot print and return values
217  * as rx_NewConnection.
218  */
219  
220 struct rx_connection *rx_GetCachedConnection(unsigned int remoteAddr,
221                       unsigned short port,
222                       unsigned short service,
223                       struct rx_securityClass *securityObject,
224                       int securityIndex) {
225     struct rx_connection *conn = NULL;
226     rx_connParts_t parts;
227  
228     parts.hostAddr = remoteAddr;
229     parts.port = port;
230     parts.service = service;
231     parts.securityObject = securityObject;
232     parts.securityIndex = securityIndex;
233     /*
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.
237      */
238     rxi_GetCachedConnection(&parts, &conn);
239  
240     return conn;
241 }
242  
243 /*
244  * Release a connection we previously handed out
245  */
246  
247 void rx_ReleaseCachedConnection(struct rx_connection *conn) {
248     cache_entry_p cacheConn, nCacheConn;
249  
250     LOCK_CONN_CACHE
251     for(queue_Scan(&rxi_connectionCache, cacheConn, nCacheConn, cache_entry)) {
252         if(conn == cacheConn->conn) {
253             cacheConn->inUse--;
254             /*
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
258              * it from the cache
259              */
260             if(rx_ConnError(conn)) {
261                 cacheConn->hasError = 1;
262                 if(cacheConn->inUse == 0) {
263                     queue_Remove(cacheConn);
264                     rxi_DestroyConnection(cacheConn->conn);
265                     free(cacheConn);
266                 }
267             }
268             break;
269         }
270     }
271     UNLOCK_CONN_CACHE
272  
273 }