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