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