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