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