Windows: Add caching to cm_GetAddrsU
[openafs.git] / src / WINNT / afsd / cm_getaddrs.c
1 /*
2  * Copyright (c) 2014 Your File System, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of Secure Endpoints Inc. nor the names of its contributors
14  *   may be used to endorse or promote products derived from this software without
15  *   specific prior written permission from Secure Endpoints, Inc. and
16  *   Your File System, Inc.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include <afsconfig.h>
32 #include <afs/param.h>
33 #include <roken.h>
34
35 #include <afs/stds.h>
36 #include "afsd.h"
37 #include "cm_getaddrs.h"
38
39 typedef struct _uuid2addrsEntry {
40     osi_queue_t q;
41     afsUUID     uuid;
42     afs_int32   unique;
43     afs_uint32  nentries;
44     bulkaddrs   addrs;
45     afs_int32   refCount;
46     afs_uint32  flags;
47 } uuid2addrsEntry_t;
48
49 #define CM_GETADDRS_FLAG_DELETE         1
50
51 /* lock for hash table */
52 osi_rwlock_t cm_getaddrsLock;
53
54 /* hash table */
55 #define CM_GETADDRS_HASHTABLESIZE       128
56 static afs_uint32       cm_getaddrsHashTableSize = CM_GETADDRS_HASHTABLESIZE;
57
58 #define CM_GETADDRS_HASH(uuidp) \
59   (opr_jhash((const afs_uint32 *)uuidp, 4, 0) & (cm_getaddrsHashTableSize - 1))
60
61 static struct _uuid2addrsHashTableBucket {
62     uuid2addrsEntry_t * Firstp;
63     uuid2addrsEntry_t * Endp;
64 } cm_getaddrsHashTable[CM_GETADDRS_HASHTABLESIZE];
65
66 /*
67  * Return a cached entry that matches the requested
68  * uuid if the requested unique is less than or equal
69  * to the cached entry.  Otherwise, return NULL.
70  *
71  * Returned entries are returned with a reference.
72  */
73 static uuid2addrsEntry_t *
74 cm_getaddrsFind(afsUUID *uuid, afs_int32 srv_unique)
75 {
76     uuid2addrsEntry_t * entry = NULL;
77     afs_uint32 hash = CM_GETADDRS_HASH(uuid);
78
79     lock_ObtainRead(&cm_getaddrsLock);
80
81     for ( entry = cm_getaddrsHashTable[hash].Firstp;
82           entry;
83           entry = (uuid2addrsEntry_t *)osi_QNext(&entry->q))
84     {
85         if (memcmp(uuid, &entry->uuid, sizeof(afsUUID)) == 0 &&
86             srv_unique <= entry->unique &&
87             !(entry->flags & CM_GETADDRS_FLAG_DELETE))
88             break;
89     }
90
91     if (entry)
92         entry->refCount++;
93     lock_ReleaseRead(&cm_getaddrsLock);
94
95     return entry;
96 }
97
98 static void
99 cm_getaddrsPut(uuid2addrsEntry_t *entry)
100 {
101     lock_ObtainRead(&cm_getaddrsLock);
102     entry->refCount--;
103     lock_ReleaseRead(&cm_getaddrsLock);
104 }
105
106 /*
107  * Add a bulkaddrs list to the getaddrs queue.
108  * Once this function is called the bulkaddrs structure
109  * no longer belongs to the caller and must not be
110  * accessed.  It may be freed if there is a race with
111  * another thread.
112  */
113
114 static uuid2addrsEntry_t *
115 cm_getaddrsAdd(afsUUID *uuid, afs_int32 srv_unique,
116                afs_uint32 nentries, bulkaddrs *addrsp)
117 {
118     uuid2addrsEntry_t * entry, *next, *retentry = NULL;
119     afs_uint32 hash = CM_GETADDRS_HASH(uuid);
120     int insert = 1;
121
122     lock_ObtainWrite(&cm_getaddrsLock);
123     for ( entry = cm_getaddrsHashTable[hash].Firstp;
124           entry;
125           entry = next)
126     {
127         next = (uuid2addrsEntry_t *)osi_QNext(&entry->q);
128
129         if ((entry->flags & CM_GETADDRS_FLAG_DELETE) &&
130             entry->refCount == 0)
131         {
132             osi_QRemoveHT((osi_queue_t **) &cm_getaddrsHashTable[hash].Firstp,
133                           (osi_queue_t **) &cm_getaddrsHashTable[hash].Endp,
134                           &entry->q);
135             xdr_free((xdrproc_t) xdr_bulkaddrs, &entry->addrs);
136             continue;
137         }
138
139         if (memcmp(uuid, &entry->uuid, sizeof(afsUUID)) == 0)
140         {
141
142             if (srv_unique <= entry->unique) {
143                 /*
144                  * out of date or duplicate entry.
145                  * discard the input.
146                  */
147                 xdr_free((xdrproc_t) xdr_bulkaddrs, addrsp);
148                 insert = 0;
149                 retentry = entry;
150                 retentry->refCount++;
151                 continue;
152             }
153
154             /*
155              * this entry is newer than the one we found,
156              * if it is unused then update it
157              */
158             if (entry->refCount == 0) {
159                 xdr_free((xdrproc_t) xdr_bulkaddrs, &entry->addrs);
160
161                 entry->unique = srv_unique;
162                 entry->addrs = *addrsp;
163                 entry->nentries = nentries;
164                 insert = 0;
165                 retentry = entry;
166                 retentry->refCount++;
167                 continue;
168             }
169         }
170     }
171
172     if (insert) {
173         entry = calloc(1, sizeof(uuid2addrsEntry_t));
174         if (entry) {
175             memcpy(&entry->uuid, uuid, sizeof(afsUUID));
176             entry->unique = srv_unique;
177             entry->addrs = *addrsp;
178             entry->nentries = nentries;
179
180             osi_QAddH((osi_queue_t **) &cm_getaddrsHashTable[hash].Firstp,
181                        (osi_queue_t **) &cm_getaddrsHashTable[hash].Endp,
182                        &entry->q);
183             retentry = entry;
184             retentry->refCount++;
185         }
186     }
187     lock_ReleaseWrite(&cm_getaddrsLock);
188
189     return retentry;
190 }
191
192 /*
193  * cm_GetAddrsU takes as input a uuid and a unique value which
194  * represent the set of addresses that are required.  These values
195  * are used as input to the VL_GetAddrsU RPC which returns a list
196  * of addresses.  For each returned address a bucket in the provided
197  * arrays (serverFlags, serverNumber, serverUUID, serverUnique)
198  * are populated.  The serverFlags array entries are filled with the
199  * 'Flags' value provided as input.  'serverNumber' is the server's
200  * IP address.
201  */
202
203 afs_uint32
204 cm_GetAddrsU(cm_cell_t *cellp, cm_user_t *userp, cm_req_t *reqp,
205              afsUUID *Uuid, afs_int32 Unique, afs_int32 Flags,
206              int *index,
207              afs_int32 serverFlags[],
208              afs_int32 serverNumber[],
209              afsUUID serverUUID[],
210              afs_int32 serverUnique[])
211 {
212     afs_uint32 code = 0;
213     uuid2addrsEntry_t *entry;
214
215     entry = cm_getaddrsFind(Uuid, Unique);
216     if (entry == NULL)
217     {
218         cm_conn_t *connp;
219         struct rx_connection *rxconnp;
220         ListAddrByAttributes attrs;
221         afs_int32 unique;
222         afsUUID uuid;
223         afs_uint32 nentries;
224         bulkaddrs  addrs;
225
226         memset(&uuid, 0, sizeof(uuid));
227         memset(&addrs, 0, sizeof(addrs));
228         memset(&attrs, 0, sizeof(attrs));
229
230         attrs.Mask = VLADDR_UUID;
231         attrs.uuid = *Uuid;
232
233         do {
234             code = cm_ConnByMServers(cellp->vlServersp, 0, userp, reqp, &connp);
235             if (code)
236                 continue;
237             rxconnp = cm_GetRxConn(connp);
238             code = VL_GetAddrsU(rxconnp, &attrs, &uuid, &unique, &nentries,
239                                  &addrs);
240             rx_PutConnection(rxconnp);
241         } while (cm_Analyze(connp, userp, reqp, NULL, cellp, 0, NULL, NULL,
242                              &cellp->vlServersp, NULL, code));
243
244         code = cm_MapVLRPCError(code, reqp);
245
246         if (afsd_logp->enabled) {
247             char uuidstr[128];
248             afsUUID_to_string(Uuid, uuidstr, sizeof(uuidstr));
249
250             if (code)
251                 osi_Log2(afsd_logp,
252                           "CALL VL_GetAddrsU serverNumber %s FAILURE, code 0x%x",
253                           osi_LogSaveString(afsd_logp, uuidstr),
254                           code);
255             else
256                 osi_Log1(afsd_logp, "CALL VL_GetAddrsU serverNumber %s SUCCESS",
257                           osi_LogSaveString(afsd_logp, uuidstr));
258         }
259
260         if (code)
261             return CM_ERROR_RETRY;
262
263         if (nentries == 0) {
264             xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
265             code = CM_ERROR_INVAL;
266         } else {
267             /* addrs will either be cached or freed by cm_getaddrsAdd() */
268             entry = cm_getaddrsAdd(&uuid, unique, nentries, &addrs);
269         }
270     }
271
272     if (entry != NULL) {
273         afs_uint32 * addrp;
274         afs_uint32 n;
275
276         addrp = entry->addrs.bulkaddrs_val;
277         for (n = 0; n < entry->nentries && (*index) < NMAXNSERVERS;
278              (*index)++, n++) {
279             serverFlags[*index] = Flags;
280             serverNumber[*index] = addrp[n];
281             serverUUID[*index] = entry->uuid;
282             serverUnique[*index] = entry->unique;
283         }
284         cm_getaddrsPut(entry);
285     }
286
287     return code;
288 }
289
290 void
291 cm_getaddrsInit(void)
292 {
293     static osi_once_t once;
294
295     if (osi_Once(&once)) {
296         lock_InitializeRWLock(&cm_getaddrsLock, "cm_getaddrsLock",
297                               LOCK_HIERARCHY_GETADDRS_GLOBAL);
298         osi_EndOnce(&once);
299     }
300 }
301
302 void
303 cm_getaddrsShutdown(void)
304 {
305     lock_FinalizeRWLock(&cm_getaddrsLock);
306 }