Windows: set hard dead timeout not conn timeout for probes
[openafs.git] / src / WINNT / afsd / cm_server.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 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <roken.h>
13
14 #include <afs/stds.h>
15
16 #include <windows.h>
17 #include <winsock2.h>
18 #include <nb30.h>
19 #include <stdlib.h>
20 #include <malloc.h>
21 #include <string.h>
22
23 #include "afsd.h"
24 #include <WINNT\syscfg.h>
25 #include <WINNT/afsreg.h>
26 #include <osi.h>
27 #include <rx/rx.h>
28 #include <math.h>
29
30 osi_rwlock_t cm_serverLock;
31 osi_rwlock_t cm_syscfgLock;
32
33 cm_server_t *cm_serversAllFirstp = NULL;
34 cm_server_t *cm_serversAllLastp = NULL;
35
36 afs_uint32   cm_numFileServers = 0;
37 afs_uint32   cm_numVldbServers = 0;
38
39 void
40 cm_ForceNewConnectionsAllServers(void)
41 {
42     cm_server_t *tsp;
43
44     lock_ObtainRead(&cm_serverLock);
45     for (tsp = cm_serversAllFirstp;
46          tsp;
47          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
48         cm_GetServerNoLock(tsp);
49         lock_ReleaseRead(&cm_serverLock);
50         cm_ForceNewConnections(tsp);
51         lock_ObtainRead(&cm_serverLock);
52         cm_PutServerNoLock(tsp);
53     }
54     lock_ReleaseRead(&cm_serverLock);
55 }
56
57 void
58 cm_ServerClearRPCStats(void) {
59     cm_server_t *tsp;
60     afs_uint16 port;
61
62     lock_ObtainRead(&cm_serverLock);
63     for (tsp = cm_serversAllFirstp;
64          tsp;
65          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
66         switch (tsp->type) {
67         case CM_SERVER_VLDB:
68             port = htons(7003);
69             rx_ClearPeerRPCStats(opcode_VL_ProbeServer>>32, tsp->addr.sin_addr.s_addr, port);
70             break;
71         case CM_SERVER_FILE:
72             port = htons(7000);
73             rx_ClearPeerRPCStats(opcode_RXAFS_GetCapabilities>>32, tsp->addr.sin_addr.s_addr, port);
74             rx_ClearPeerRPCStats(opcode_RXAFS_GetTime>>32, tsp->addr.sin_addr.s_addr, port);
75             break;
76         }
77     }
78     lock_ReleaseRead(&cm_serverLock);
79 }
80
81 /*
82  * lock_ObtainMutex must be held prior to calling
83  * this function.
84  */
85 afs_int32
86 cm_RankServer(cm_server_t * tsp)
87 {
88     afs_int32 code = 0; /* start with "success" */
89     struct rx_debugPeer tpeer;
90     struct rx_peer * rxPeer;
91     afs_uint16 port;
92     afs_uint64 newRank;
93     afs_uint64 perfRank = 0;
94     afs_uint64 rtt = 0;
95     double log_rtt;
96
97     int isDown = (tsp->flags & CM_SERVERFLAG_DOWN);
98     void *peerRpcStats = NULL;
99     afs_uint64 opcode = 0;
100
101     switch(tsp->type) {
102         case CM_SERVER_VLDB:
103             port = htons(7003);
104             opcode = opcode_VL_ProbeServer;
105             break;
106         case CM_SERVER_FILE:
107             port = htons(7000);
108             opcode = opcode_RXAFS_GetCapabilities;
109             break;
110         default:
111             return -1;
112     }
113
114     cm_SetServerIPRank(tsp);
115
116     if (isDown) {
117         newRank = 0xFFFF;
118     } else {
119         /*
120         * There are three potential components to the ranking:
121         *  1. Any administrative set preference whether it be
122         *     via "fs setserverprefs", registry or dns.
123         *
124         *  2. Network subnet mask comparison.
125         *
126         *  3. Performance data.
127         *
128         * If there is an administrative rank, that is the
129         * the primary factor.  If not the primary factor
130         * is the network ranking.
131         */
132
133         code = rx_GetLocalPeers(tsp->addr.sin_addr.s_addr, port, &tpeer);
134         if (code == 0) {
135             peerRpcStats = rx_CopyPeerRPCStats(opcode, tsp->addr.sin_addr.s_addr, port);
136             if (peerRpcStats == NULL && tsp->type == CM_SERVER_FILE)
137                 peerRpcStats = rx_CopyPeerRPCStats(opcode_RXAFS_GetTime, tsp->addr.sin_addr.s_addr, port);
138             if (peerRpcStats) {
139                 afs_uint64 execTimeSum = _8THMSEC(RPCOpStat_ExecTimeSum(peerRpcStats));
140                 afs_uint64 queueTimeSum = _8THMSEC(RPCOpStat_QTimeSum(peerRpcStats));
141                 afs_uint64 numCalls = RPCOpStat_NumCalls(peerRpcStats);
142
143                 if (numCalls > 0)
144                     rtt = (execTimeSum - queueTimeSum) / numCalls;
145
146                 rx_ReleaseRPCStats(peerRpcStats);
147             }
148
149             if (rtt == 0 && tpeer.rtt) {
150                 /* rtt is ms/8 */
151                 rtt = tpeer.rtt;
152             }
153
154             if (rtt > 0) {
155                 log_rtt = log(rtt);
156                 perfRank += (6000 * log_rtt / 5000) * 5000;
157
158                 if (tsp->type == CM_SERVER_FILE) {
159                     /* give an edge to servers with high congestion windows */
160                     perfRank -= (tpeer.cwind - 1)* 15;
161                 }
162             }
163         }
164
165         if (tsp->adminRank) {
166             newRank = tsp->adminRank * 0.8;
167             newRank += tsp->ipRank * 0.2;
168         } else {
169             newRank = tsp->ipRank;
170         }
171         if (perfRank) {
172             newRank *= 0.9;
173             newRank += perfRank * 0.1;
174         }
175         newRank += (rand() & 0x000f); /* randomize */
176
177         if (newRank > 0xFFFF)
178             osi_Log1(afsd_logp, "new server rank %I64u exceeds 0xFFFF", newRank);
179
180         /*
181          * If the ranking changes by more than the randomization
182          * factor, update the server reference lists.
183          */
184         if (abs(newRank - tsp->activeRank) > 0xf) {
185             tsp->activeRank = newRank;
186
187             lock_ReleaseMutex(&tsp->mx);
188             switch (tsp->type) {
189             case CM_SERVER_FILE:
190                 /*
191                  * find volumes which might have RO copy
192                  * on server and change the ordering of
193                  * their RO list
194                  */
195                 cm_ChangeRankVolume(tsp);
196                 break;
197             case CM_SERVER_VLDB:
198                 /* set preferences for an existing vlserver */
199                 cm_ChangeRankCellVLServer(tsp);
200                 break;
201             }
202             lock_ObtainMutex(&tsp->mx);
203         }
204     }
205
206     return code;
207 }
208
209 void
210 cm_PingServer(cm_server_t *tsp)
211 {
212     long code;
213     int wasDown = 0;
214     cm_conn_t *connp;
215     struct rx_connection * rxconnp;
216     Capabilities caps = {0, 0};
217     char hoststr[16];
218     cm_req_t req;
219
220     lock_ObtainMutex(&tsp->mx);
221     if (tsp->flags & CM_SERVERFLAG_PINGING) {
222         tsp->waitCount++;
223         osi_SleepM((LONG_PTR)tsp, &tsp->mx);
224         lock_ObtainMutex(&tsp->mx);
225         tsp->waitCount--;
226         if (tsp->waitCount == 0)
227             _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_PINGING);
228         else
229             osi_Wakeup((LONG_PTR)tsp);
230         lock_ReleaseMutex(&tsp->mx);
231         return;
232     }
233     _InterlockedOr(&tsp->flags, CM_SERVERFLAG_PINGING);
234     wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
235     afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr);
236     lock_ReleaseMutex(&tsp->mx);
237
238     code = cm_ConnByServer(tsp, cm_rootUserp, FALSE, &connp);
239     if (code == 0) {
240         /* now call the appropriate ping call.  Drop the timeout if
241         * the server is known to be down, so that we don't waste a
242         * lot of time retiming out down servers.
243         */
244
245         osi_Log4(afsd_logp, "cm_PingServer server %s (%s) was %s with caps 0x%x",
246                   osi_LogSaveString(afsd_logp, hoststr),
247                   tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
248                   wasDown ? "down" : "up",
249                   tsp->capabilities);
250
251         rxconnp = cm_GetRxConn(connp);
252         if (wasDown)
253             rx_SetConnHardDeadTime(rxconnp, 10);
254         if (tsp->type == CM_SERVER_VLDB) {
255             code = VL_ProbeServer(rxconnp);
256         }
257         else {
258             /* file server */
259             code = RXAFS_GetCapabilities(rxconnp, &caps);
260         }
261         if (wasDown)
262             rx_SetConnHardDeadTime(rxconnp, HardDeadtimeout);
263         rx_PutConnection(rxconnp);
264         cm_PutConn(connp);
265     }   /* got an unauthenticated connection to this server */
266
267     lock_ObtainMutex(&tsp->mx);
268     if (code >= 0 || code == RXGEN_OPCODE || code == RX_CALL_BUSY) {
269         /* mark server as up */
270         _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
271         tsp->downTime = 0;
272
273         /* we currently handle 32-bits of capabilities */
274         if (code != RXGEN_OPCODE && code != RX_CALL_BUSY &&
275             caps.Capabilities_len > 0) {
276             tsp->capabilities = caps.Capabilities_val[0];
277             xdr_free((xdrproc_t) xdr_Capabilities, &caps);
278             caps.Capabilities_len = 0;
279             caps.Capabilities_val = 0;
280         } else {
281             tsp->capabilities = 0;
282         }
283
284         osi_Log3(afsd_logp, "cm_PingServer server %s (%s) is up with caps 0x%x",
285                   osi_LogSaveString(afsd_logp, hoststr),
286                   tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
287                   tsp->capabilities);
288
289         /* Now update the volume status if necessary */
290         if (wasDown) {
291             cm_server_vols_t * tsrvp;
292             cm_volume_t * volp;
293             int i;
294
295             for (tsrvp = tsp->vols; tsrvp; tsrvp = tsrvp->nextp) {
296                 for (i=0; i<NUM_SERVER_VOLS; i++) {
297                     if (tsrvp->ids[i] != 0) {
298                         cm_InitReq(&req);
299
300                         lock_ReleaseMutex(&tsp->mx);
301                         code = cm_FindVolumeByID(tsp->cellp, tsrvp->ids[i], cm_rootUserp,
302                                                 &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
303                         lock_ObtainMutex(&tsp->mx);
304                         if (code == 0) {
305                             cm_UpdateVolumeStatus(volp, tsrvp->ids[i]);
306                             cm_PutVolume(volp);
307                         }
308                     }
309                 }
310             }
311             cm_RankServer(tsp);
312         }
313     } else {
314         /* mark server as down */
315         if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
316             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_DOWN);
317             tsp->downTime = time(NULL);
318         }
319         if (code != VRESTARTING) {
320             lock_ReleaseMutex(&tsp->mx);
321             cm_ForceNewConnections(tsp);
322             lock_ObtainMutex(&tsp->mx);
323         }
324         osi_Log3(afsd_logp, "cm_PingServer server %s (%s) is down with caps 0x%x",
325                   osi_LogSaveString(afsd_logp, hoststr),
326                   tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
327                   tsp->capabilities);
328
329         /* Now update the volume status if necessary */
330         if (!wasDown) {
331             cm_server_vols_t * tsrvp;
332             cm_volume_t * volp;
333             int i;
334
335             for (tsrvp = tsp->vols; tsrvp; tsrvp = tsrvp->nextp) {
336                 for (i=0; i<NUM_SERVER_VOLS; i++) {
337                     if (tsrvp->ids[i] != 0) {
338                         cm_InitReq(&req);
339
340                         lock_ReleaseMutex(&tsp->mx);
341                         code = cm_FindVolumeByID(tsp->cellp, tsrvp->ids[i], cm_rootUserp,
342                                                 &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
343                         lock_ObtainMutex(&tsp->mx);
344                         if (code == 0) {
345                             cm_UpdateVolumeStatus(volp, tsrvp->ids[i]);
346                             cm_PutVolume(volp);
347                         }
348                     }
349                 }
350             }
351             cm_RankServer(tsp);
352         }
353     }
354
355     if (tsp->waitCount == 0)
356         _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_PINGING);
357     else
358         osi_Wakeup((LONG_PTR)tsp);
359     lock_ReleaseMutex(&tsp->mx);
360 }
361
362 void
363 cm_RankUpServers()
364 {
365     cm_server_t * tsp;
366
367     lock_ObtainRead(&cm_serverLock);
368     for (tsp = cm_serversAllFirstp;
369          tsp;
370          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
371         cm_GetServerNoLock(tsp);
372         lock_ReleaseRead(&cm_serverLock);
373
374         lock_ObtainMutex(&tsp->mx);
375
376         /* if the server is not down, rank the server */
377         if(!(tsp->flags & CM_SERVERFLAG_DOWN))
378            cm_RankServer(tsp);
379
380         lock_ReleaseMutex(&tsp->mx);
381
382         lock_ObtainRead(&cm_serverLock);
383         cm_PutServerNoLock(tsp);
384     }
385     lock_ReleaseRead(&cm_serverLock);
386 }
387
388 static void cm_CheckServersSingular(afs_uint32 flags, cm_cell_t *cellp)
389 {
390     /* ping all file servers, up or down, with unauthenticated connection,
391      * to find out whether we have all our callbacks from the server still.
392      * Also, ping down VLDBs.
393      */
394     cm_server_t *tsp;
395     int doPing;
396     int isDown;
397     int isFS;
398     int isVLDB;
399
400     lock_ObtainRead(&cm_serverLock);
401     for (tsp = cm_serversAllFirstp;
402          tsp;
403          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
404         cm_GetServerNoLock(tsp);
405         lock_ReleaseRead(&cm_serverLock);
406
407         /* now process the server */
408         lock_ObtainMutex(&tsp->mx);
409
410         doPing = 0;
411         isDown = tsp->flags & CM_SERVERFLAG_DOWN;
412         isFS   = tsp->type == CM_SERVER_FILE;
413         isVLDB = tsp->type == CM_SERVER_VLDB;
414
415         /* only do the ping if the cell matches the requested cell, or we're
416          * matching all cells (cellp == NULL), and if we've requested to ping
417          * this type of {up, down} servers.
418          */
419         if ((cellp == NULL || cellp == tsp->cellp) &&
420              ((isDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
421                (!isDown && (flags & CM_FLAG_CHECKUPSERVERS))) &&
422              ((!(flags & CM_FLAG_CHECKVLDBSERVERS) ||
423                isVLDB && (flags & CM_FLAG_CHECKVLDBSERVERS)) &&
424               (!(flags & CM_FLAG_CHECKFILESERVERS) ||
425                  isFS && (flags & CM_FLAG_CHECKFILESERVERS)))) {
426             doPing = 1;
427         }       /* we're supposed to check this up/down server */
428         lock_ReleaseMutex(&tsp->mx);
429
430         /* at this point, we've adjusted the server state, so do the ping and
431          * adjust things.
432          */
433         if (doPing)
434             cm_PingServer(tsp);
435
436         /* also, run the GC function for connections on all of the
437          * server's connections.
438          */
439         cm_GCConnections(tsp);
440
441         lock_ObtainRead(&cm_serverLock);
442         cm_PutServerNoLock(tsp);
443     }
444     lock_ReleaseRead(&cm_serverLock);
445 }
446
447 static void cm_CheckServersMulti(afs_uint32 flags, cm_cell_t *cellp)
448 {
449     /*
450      * The goal of this function is to probe simultaneously
451      * probe all of the up/down servers (vldb/file) as
452      * specified by flags in the minimum number of RPCs.
453      * Effectively that means use one multi_RXAFS_GetCapabilities()
454      * followed by possibly one multi_RXAFS_GetTime() and
455      * one multi_VL_ProbeServer().
456      *
457      * To make this work we must construct the list of vldb
458      * and file servers that are to be probed as well as the
459      * associated data structures.
460      */
461
462     int srvAddrCount = 0;
463     struct srvAddr **addrs = NULL;
464     cm_conn_t **conns = NULL;
465     struct rx_connection **rxconns = NULL;
466     cm_req_t req;
467     afs_int32 i, nconns = 0, maxconns;
468     afs_int32 *conntimer, *results;
469     Capabilities *caps = NULL;
470     cm_server_t ** serversp, *tsp;
471     afs_uint32 isDown, wasDown;
472     afs_uint32 code;
473     time_t start;
474     char hoststr[16];
475
476     cm_InitReq(&req);
477     maxconns = max(cm_numFileServers,cm_numVldbServers);
478     if (maxconns == 0)
479         return;
480
481     conns = (cm_conn_t **)malloc(maxconns * sizeof(cm_conn_t *));
482     rxconns = (struct rx_connection **)malloc(maxconns * sizeof(struct rx_connection *));
483     conntimer = (afs_int32 *)malloc(maxconns * sizeof (afs_int32));
484     results = (afs_int32 *)malloc(maxconns * sizeof (afs_int32));
485     serversp = (cm_server_t **)malloc(maxconns * sizeof(cm_server_t *));
486     caps = (Capabilities *)malloc(maxconns * sizeof(Capabilities));
487
488     memset(caps, 0, maxconns * sizeof(Capabilities));
489
490     if ((flags & CM_FLAG_CHECKFILESERVERS) ||
491         !(flags & (CM_FLAG_CHECKFILESERVERS|CM_FLAG_CHECKVLDBSERVERS)))
492     {
493         lock_ObtainRead(&cm_serverLock);
494         for (nconns=0, tsp = cm_serversAllFirstp;
495               tsp != NULL && nconns < maxconns;
496               tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
497             if (tsp->type != CM_SERVER_FILE ||
498                 tsp->cellp == NULL ||           /* SetPref only */
499                 cellp && cellp != tsp->cellp)
500                 continue;
501
502             cm_GetServerNoLock(tsp);
503             lock_ReleaseRead(&cm_serverLock);
504
505             lock_ObtainMutex(&tsp->mx);
506             isDown = tsp->flags & CM_SERVERFLAG_DOWN;
507
508             if ((tsp->flags & CM_SERVERFLAG_PINGING) ||
509                 !((isDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
510                    (!isDown && (flags & CM_FLAG_CHECKUPSERVERS)))) {
511                 lock_ReleaseMutex(&tsp->mx);
512                 lock_ObtainRead(&cm_serverLock);
513                 cm_PutServerNoLock(tsp);
514                 continue;
515             }
516
517             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_PINGING);
518             lock_ReleaseMutex(&tsp->mx);
519
520             serversp[nconns] = tsp;
521             code = cm_ConnByServer(tsp, cm_rootUserp, FALSE, &conns[nconns]);
522             if (code) {
523                 lock_ObtainRead(&cm_serverLock);
524                 cm_PutServerNoLock(tsp);
525                 continue;
526             }
527             lock_ObtainRead(&cm_serverLock);
528             rxconns[nconns] = cm_GetRxConn(conns[nconns]);
529             if (conntimer[nconns] = (isDown ? 1 : 0))
530                 rx_SetConnHardDeadTime(rxconns[nconns], 10);
531
532             nconns++;
533         }
534         lock_ReleaseRead(&cm_serverLock);
535
536         if (nconns) {
537             /* Perform the multi call */
538             start = time(NULL);
539             multi_Rx(rxconns,nconns)
540             {
541                 multi_RXAFS_GetCapabilities(&caps[multi_i]);
542                 results[multi_i]=multi_error;
543             } multi_End;
544         }
545
546         /* Process results of servers that support RXAFS_GetCapabilities */
547         for (i=0; i<nconns; i++) {
548             if (conntimer[i])
549                 rx_SetConnHardDeadTime(rxconns[i], HardDeadtimeout);
550             rx_PutConnection(rxconns[i]);
551             cm_PutConn(conns[i]);
552
553             tsp = serversp[i];
554             cm_GCConnections(tsp);
555
556             lock_ObtainMutex(&tsp->mx);
557             wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
558
559             if (results[i] >= 0 || results[i] == RXGEN_OPCODE ||
560                 results[i] == RX_CALL_BUSY)  {
561                 /* mark server as up */
562                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
563                 tsp->downTime = 0;
564
565                 /* we currently handle 32-bits of capabilities */
566                 if (results[i] != RXGEN_OPCODE && results[i] != RX_CALL_BUSY &&
567                     caps[i].Capabilities_len > 0) {
568                     tsp->capabilities = caps[i].Capabilities_val[0];
569                     xdr_free((xdrproc_t) xdr_Capabilities, &caps[i]);
570                     caps[i].Capabilities_len = 0;
571                     caps[i].Capabilities_val = 0;
572                 } else {
573                     tsp->capabilities = 0;
574                 }
575
576                 afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr);
577                 osi_Log3(afsd_logp, "cm_MultiPingServer server %s (%s) is up with caps 0x%x",
578                           osi_LogSaveString(afsd_logp, hoststr),
579                           tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
580                           tsp->capabilities);
581
582                 /* Now update the volume status if necessary */
583                 if (wasDown) {
584                     cm_server_vols_t * tsrvp;
585                     cm_volume_t * volp;
586                     int i;
587
588                     for (tsrvp = tsp->vols; tsrvp; tsrvp = tsrvp->nextp) {
589                         for (i=0; i<NUM_SERVER_VOLS; i++) {
590                             if (tsrvp->ids[i] != 0) {
591                                 cm_InitReq(&req);
592
593                                 lock_ReleaseMutex(&tsp->mx);
594                                 code = cm_FindVolumeByID(tsp->cellp, tsrvp->ids[i], cm_rootUserp,
595                                                          &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
596                                 lock_ObtainMutex(&tsp->mx);
597                                 if (code == 0) {
598                                     cm_UpdateVolumeStatus(volp, tsrvp->ids[i]);
599                                     cm_PutVolume(volp);
600                                 }
601                             }
602                         }
603                     }
604                     cm_RankServer(tsp);
605                 }
606             } else {
607                 /* mark server as down */
608                 if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
609                     _InterlockedOr(&tsp->flags, CM_SERVERFLAG_DOWN);
610                     tsp->downTime = time(NULL);
611                 }
612                 if (code != VRESTARTING) {
613                     lock_ReleaseMutex(&tsp->mx);
614                     cm_ForceNewConnections(tsp);
615                     lock_ObtainMutex(&tsp->mx);
616                 }
617                 afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr);
618                 osi_Log3(afsd_logp, "cm_MultiPingServer server %s (%s) is down with caps 0x%x",
619                           osi_LogSaveString(afsd_logp, hoststr),
620                           tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
621                           tsp->capabilities);
622
623                 /* Now update the volume status if necessary */
624                 if (!wasDown) {
625                     cm_server_vols_t * tsrvp;
626                     cm_volume_t * volp;
627                     int i;
628
629                     for (tsrvp = tsp->vols; tsrvp; tsrvp = tsrvp->nextp) {
630                         for (i=0; i<NUM_SERVER_VOLS; i++) {
631                             if (tsrvp->ids[i] != 0) {
632                                 cm_InitReq(&req);
633
634                                 lock_ReleaseMutex(&tsp->mx);
635                                 code = cm_FindVolumeByID(tsp->cellp, tsrvp->ids[i], cm_rootUserp,
636                                                          &req, CM_GETVOL_FLAG_NO_LRU_UPDATE, &volp);
637                                 lock_ObtainMutex(&tsp->mx);
638                                 if (code == 0) {
639                                     cm_UpdateVolumeStatus(volp, tsrvp->ids[i]);
640                                     cm_PutVolume(volp);
641                                 }
642                             }
643                         }
644                     }
645                     cm_RankServer(tsp);
646                 }
647             }
648
649             if (tsp->waitCount == 0)
650                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_PINGING);
651             else
652                 osi_Wakeup((LONG_PTR)tsp);
653
654             lock_ReleaseMutex(&tsp->mx);
655
656             cm_PutServer(tsp);
657         }
658     }
659
660     if ((flags & CM_FLAG_CHECKVLDBSERVERS) ||
661         !(flags & (CM_FLAG_CHECKFILESERVERS|CM_FLAG_CHECKVLDBSERVERS)))
662     {
663         lock_ObtainRead(&cm_serverLock);
664         for (nconns=0, tsp = cm_serversAllFirstp;
665              tsp != NULL && nconns < maxconns;
666              tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
667             if (tsp->type != CM_SERVER_VLDB ||
668                 tsp->cellp == NULL ||           /* SetPref only */
669                 cellp && cellp != tsp->cellp)
670                 continue;
671
672             cm_GetServerNoLock(tsp);
673             lock_ReleaseRead(&cm_serverLock);
674
675             lock_ObtainMutex(&tsp->mx);
676             isDown = tsp->flags & CM_SERVERFLAG_DOWN;
677
678             if ((tsp->flags & CM_SERVERFLAG_PINGING) ||
679                 !((isDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
680                    (!isDown && (flags & CM_FLAG_CHECKUPSERVERS)))) {
681                 lock_ReleaseMutex(&tsp->mx);
682                 lock_ObtainRead(&cm_serverLock);
683                 cm_PutServerNoLock(tsp);
684                 continue;
685             }
686
687             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_PINGING);
688             lock_ReleaseMutex(&tsp->mx);
689
690             serversp[nconns] = tsp;
691             code = cm_ConnByServer(tsp, cm_rootUserp, FALSE, &conns[nconns]);
692             if (code) {
693                 lock_ObtainRead(&cm_serverLock);
694                 cm_PutServerNoLock(tsp);
695                 continue;
696             }
697             lock_ObtainRead(&cm_serverLock);
698             rxconns[nconns] = cm_GetRxConn(conns[nconns]);
699             conntimer[nconns] = (isDown ? 1 : 0);
700             if (isDown)
701                 rx_SetConnHardDeadTime(rxconns[nconns], 10);
702
703             nconns++;
704         }
705         lock_ReleaseRead(&cm_serverLock);
706
707         if (nconns) {
708             /* Perform the multi call */
709             start = time(NULL);
710             multi_Rx(rxconns,nconns)
711             {
712                 multi_VL_ProbeServer();
713                 results[multi_i]=multi_error;
714             } multi_End;
715         }
716
717         /* Process results of servers that support VL_ProbeServer */
718         for (i=0; i<nconns; i++) {
719             if (conntimer[i])
720                 rx_SetConnHardDeadTime(rxconns[i], HardDeadtimeout);
721             rx_PutConnection(rxconns[i]);
722             cm_PutConn(conns[i]);
723
724             tsp = serversp[i];
725             cm_GCConnections(tsp);
726
727             lock_ObtainMutex(&tsp->mx);
728             wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
729
730             if (results[i] >= 0 || results[i] == RX_CALL_BUSY)  {
731                 /* mark server as up */
732                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_DOWN);
733                 tsp->downTime = 0;
734                 tsp->capabilities = 0;
735
736                 afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr);
737                 osi_Log3(afsd_logp, "cm_MultiPingServer server %s (%s) is up with caps 0x%x",
738                           osi_LogSaveString(afsd_logp, hoststr),
739                           tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
740                           tsp->capabilities);
741                 if (wasDown)
742                     cm_RankServer(tsp);
743             } else {
744                 /* mark server as down */
745                 if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
746                     _InterlockedOr(&tsp->flags, CM_SERVERFLAG_DOWN);
747                     tsp->downTime = time(NULL);
748                 }
749                 if (code != VRESTARTING) {
750                     lock_ReleaseMutex(&tsp->mx);
751                     cm_ForceNewConnections(tsp);
752                     lock_ObtainMutex(&tsp->mx);
753                 }
754                 afs_inet_ntoa_r(tsp->addr.sin_addr.S_un.S_addr, hoststr);
755                 osi_Log3(afsd_logp, "cm_MultiPingServer server %s (%s) is down with caps 0x%x",
756                           osi_LogSaveString(afsd_logp, hoststr),
757                           tsp->type == CM_SERVER_VLDB ? "vldb" : "file",
758                           tsp->capabilities);
759                 if (!wasDown)
760                     cm_RankServer(tsp);
761             }
762
763             if (tsp->waitCount == 0)
764                 _InterlockedAnd(&tsp->flags, ~CM_SERVERFLAG_PINGING);
765             else
766                 osi_Wakeup((LONG_PTR)tsp);
767
768             lock_ReleaseMutex(&tsp->mx);
769
770             cm_PutServer(tsp);
771         }
772     }
773
774     free(conns);
775     free(rxconns);
776     free(conntimer);
777     free(results);
778     free(serversp);
779     free(caps);
780 }
781
782 void cm_CheckServers(afs_uint32 flags, cm_cell_t *cellp)
783 {
784     DWORD code;
785     HKEY parmKey;
786     DWORD dummyLen;
787     DWORD multi = 1;
788
789     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
790                          0, KEY_QUERY_VALUE, &parmKey);
791     if (code == ERROR_SUCCESS) {
792         dummyLen = sizeof(multi);
793         code = RegQueryValueEx(parmKey, "MultiCheckServers", NULL, NULL,
794                                 (BYTE *) &multi, &dummyLen);
795         RegCloseKey (parmKey);
796     }
797
798     if (multi)
799         cm_CheckServersMulti(flags, cellp);
800     else
801         cm_CheckServersSingular(flags, cellp);
802 }
803
804 void cm_InitServer(void)
805 {
806     static osi_once_t once;
807
808     if (osi_Once(&once)) {
809         lock_InitializeRWLock(&cm_serverLock, "cm_serverLock", LOCK_HIERARCHY_SERVER_GLOBAL);
810         lock_InitializeRWLock(&cm_syscfgLock, "cm_syscfgLock", LOCK_HIERARCHY_SYSCFG_GLOBAL);
811         osi_EndOnce(&once);
812     }
813 }
814
815 /* Protected by cm_syscfgLock (rw) */
816 int cm_noIPAddr;         /* number of client network interfaces */
817 int cm_IPAddr[CM_MAXINTERFACE_ADDR];    /* client's IP address in host order */
818 int cm_SubnetMask[CM_MAXINTERFACE_ADDR];/* client's subnet mask in host order*/
819 int cm_NetMtu[CM_MAXINTERFACE_ADDR];    /* client's MTU sizes */
820 int cm_NetFlags[CM_MAXINTERFACE_ADDR];  /* network flags */
821 int cm_LanAdapterChangeDetected = 1;
822
823 void cm_SetLanAdapterChangeDetected(void)
824 {
825     lock_ObtainWrite(&cm_syscfgLock);
826     cm_LanAdapterChangeDetected = 1;
827     lock_ReleaseWrite(&cm_syscfgLock);
828 }
829
830 void cm_GetServer(cm_server_t *serverp)
831 {
832     lock_ObtainRead(&cm_serverLock);
833     InterlockedIncrement(&serverp->refCount);
834     lock_ReleaseRead(&cm_serverLock);
835 }
836
837 void cm_GetServerNoLock(cm_server_t *serverp)
838 {
839     InterlockedIncrement(&serverp->refCount);
840 }
841
842 void cm_PutServer(cm_server_t *serverp)
843 {
844     afs_int32 refCount;
845     lock_ObtainRead(&cm_serverLock);
846     refCount = InterlockedDecrement(&serverp->refCount);
847     osi_assertx(refCount >= 0, "cm_server_t refCount underflow");
848     lock_ReleaseRead(&cm_serverLock);
849 }
850
851 void cm_PutServerNoLock(cm_server_t *serverp)
852 {
853     afs_int32 refCount = InterlockedDecrement(&serverp->refCount);
854     osi_assertx(refCount >= 0, "cm_server_t refCount underflow");
855 }
856
857 void cm_SetServerNo64Bit(cm_server_t * serverp, int no64bit)
858 {
859     lock_ObtainMutex(&serverp->mx);
860     if (no64bit)
861         _InterlockedOr(&serverp->flags, CM_SERVERFLAG_NO64BIT);
862     else
863         _InterlockedAnd(&serverp->flags, ~CM_SERVERFLAG_NO64BIT);
864     lock_ReleaseMutex(&serverp->mx);
865 }
866
867 void cm_SetServerNoInlineBulk(cm_server_t * serverp, int no)
868 {
869     lock_ObtainMutex(&serverp->mx);
870     if (no)
871         _InterlockedOr(&serverp->flags, CM_SERVERFLAG_NOINLINEBULK);
872     else
873         _InterlockedAnd(&serverp->flags, ~CM_SERVERFLAG_NOINLINEBULK);
874     lock_ReleaseMutex(&serverp->mx);
875 }
876
877 void cm_SetServerIPRank(cm_server_t * serverp)
878 {
879     unsigned long       serverAddr;     /* in host byte order */
880     unsigned long       myAddr, myNet, mySubnet;/* in host byte order */
881     unsigned long       netMask;
882     int                 i;
883     long code;
884
885     lock_ObtainRead(&cm_syscfgLock);
886     if (cm_LanAdapterChangeDetected) {
887         lock_ConvertRToW(&cm_syscfgLock);
888         if (cm_LanAdapterChangeDetected) {
889             /* get network related info */
890             cm_noIPAddr = CM_MAXINTERFACE_ADDR;
891             code = syscfg_GetIFInfo(&cm_noIPAddr,
892                                      cm_IPAddr, cm_SubnetMask,
893                                      cm_NetMtu, cm_NetFlags);
894             cm_LanAdapterChangeDetected = 0;
895         }
896         lock_ConvertWToR(&cm_syscfgLock);
897     }
898
899     serverAddr = ntohl(serverp->addr.sin_addr.s_addr);
900     serverp->ipRank  = CM_IPRANK_LOW;   /* default settings */
901
902     for ( i=0; i < cm_noIPAddr; i++)
903     {
904         /* loop through all the client's IP address and compare
905         ** each of them against the server's IP address */
906
907         myAddr = cm_IPAddr[i];
908         if ( IN_CLASSA(myAddr) )
909             netMask = IN_CLASSA_NET;
910         else if ( IN_CLASSB(myAddr) )
911             netMask = IN_CLASSB_NET;
912         else if ( IN_CLASSC(myAddr) )
913             netMask = IN_CLASSC_NET;
914         else
915             netMask = 0;
916
917         myNet    =  myAddr & netMask;
918         mySubnet =  myAddr & cm_SubnetMask[i];
919
920         if ( (serverAddr & netMask) == myNet )
921         {
922             if ( (serverAddr & cm_SubnetMask[i]) == mySubnet)
923             {
924                 if ( serverAddr == myAddr ) {
925                     serverp->ipRank = min(serverp->ipRank,
926                                            CM_IPRANK_TOP);/* same machine */
927                 } else {
928                     serverp->ipRank = min(serverp->ipRank,
929                                           CM_IPRANK_HI); /* same subnet */
930                 }
931             } else {
932                 serverp->ipRank = min(serverp->ipRank, CM_IPRANK_MED); /* same net */
933             }
934         }
935     } /* and of for loop */
936     lock_ReleaseRead(&cm_syscfgLock);
937 }
938
939 cm_server_t *cm_NewServer(struct sockaddr_in *socketp, int type, cm_cell_t *cellp, afsUUID *uuidp, afs_uint32 flags) {
940     cm_server_t *tsp;
941     char hoststr[16];
942
943     osi_assertx(socketp->sin_family == AF_INET, "unexpected socket family");
944
945     lock_ObtainWrite(&cm_serverLock);   /* get server lock */
946     tsp = cm_FindServer(socketp, type, TRUE);
947     if (tsp) {
948         /* we might have found a server created by set server prefs */
949         if (uuidp && !afs_uuid_is_nil(uuidp) &&
950             !(tsp->flags & CM_SERVERFLAG_UUID))
951         {
952             tsp->uuid = *uuidp;
953             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_UUID);
954         }
955
956         if (cellp != NULL && tsp->cellp == NULL) {
957             tsp->cellp = cellp;
958             afs_inet_ntoa_r(tsp->addr.sin_addr.s_addr, hoststr);
959             osi_Log3(afsd_logp, "cm_NewServer assigning server %s to cell (%u) %s",
960                      osi_LogSaveString(afsd_logp,hoststr),
961                      cellp->cellID,
962                      osi_LogSaveString(afsd_logp,cellp->name));
963         }
964         else if (tsp->cellp != cellp) {
965             afs_inet_ntoa_r(tsp->addr.sin_addr.s_addr, hoststr);
966             osi_Log5(afsd_logp,
967                      "cm_NewServer found a server %s associated with two cells (%u) %s and (%u) %s",
968                      osi_LogSaveString(afsd_logp,hoststr),
969                      tsp->cellp->cellID,
970                      osi_LogSaveString(afsd_logp,tsp->cellp->name),
971                      cellp->cellID,
972                      osi_LogSaveString(afsd_logp,cellp->name));
973         }
974         lock_ReleaseWrite(&cm_serverLock);
975         return tsp;
976     }
977
978     tsp = malloc(sizeof(*tsp));
979     if (tsp) {
980         memset(tsp, 0, sizeof(*tsp));
981         tsp->type = type;
982         if (uuidp && !afs_uuid_is_nil(uuidp)) {
983             tsp->uuid = *uuidp;
984             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_UUID);
985         }
986         tsp->refCount = 1;
987         lock_InitializeMutex(&tsp->mx, "cm_server_t mutex", LOCK_HIERARCHY_SERVER);
988         tsp->addr = *socketp;
989
990         osi_QAddH((osi_queue_t **)&cm_serversAllFirstp,
991                   (osi_queue_t **)&cm_serversAllLastp, &tsp->allq);
992
993         switch (type) {
994         case CM_SERVER_VLDB:
995             cm_numVldbServers++;
996             break;
997         case CM_SERVER_FILE:
998             cm_numFileServers++;
999             break;
1000         }
1001
1002         if (cellp != NULL) {
1003             tsp->cellp = cellp;
1004             afs_inet_ntoa_r(tsp->addr.sin_addr.s_addr, hoststr);
1005             osi_Log3(afsd_logp, "cm_NewServer new server %s in cell (%u) %s",
1006                      osi_LogSaveString(afsd_logp,hoststr),
1007                      cellp->cellID,
1008                      osi_LogSaveString(afsd_logp,cellp->name));
1009         }
1010     }
1011     lock_ReleaseWrite(&cm_serverLock);  /* release server lock */
1012
1013     if (tsp) {
1014         if (!(flags & CM_FLAG_NOPROBE)) {
1015             _InterlockedOr(&tsp->flags, CM_SERVERFLAG_DOWN);    /* assume down; ping will mark up if available */
1016             lock_ObtainMutex(&tsp->mx);
1017             cm_RankServer(tsp);
1018             lock_ReleaseMutex(&tsp->mx);
1019             cm_PingServer(tsp);                                 /* Obtain Capabilities and check up/down state */
1020         } else {
1021             pthread_t phandle;
1022             pthread_attr_t tattr;
1023             int pstatus;
1024
1025             /* Probe the server in the background to determine if it is up or down */
1026             pthread_attr_init(&tattr);
1027             pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1028
1029             lock_ObtainMutex(&tsp->mx);
1030             cm_RankServer(tsp);
1031             lock_ReleaseMutex(&tsp->mx);
1032             pstatus = pthread_create(&phandle, &tattr, cm_PingServer, tsp);
1033
1034             pthread_attr_destroy(&tattr);
1035         }
1036     }
1037     return tsp;
1038 }
1039
1040 cm_server_t *
1041 cm_FindServerByIP(afs_uint32 ipaddr, unsigned short port, int type, int locked)
1042 {
1043     cm_server_t *tsp;
1044
1045     if (!locked)
1046         lock_ObtainRead(&cm_serverLock);
1047
1048     for (tsp = cm_serversAllFirstp;
1049          tsp;
1050          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
1051         if (tsp->type == type &&
1052             tsp->addr.sin_addr.S_un.S_addr == ipaddr &&
1053             (tsp->addr.sin_port == port || tsp->addr.sin_port == 0))
1054             break;
1055     }
1056
1057     /* bump ref count if we found the server */
1058     if (tsp)
1059         cm_GetServerNoLock(tsp);
1060
1061     if (!locked)
1062         lock_ReleaseRead(&cm_serverLock);
1063
1064     return tsp;
1065 }
1066
1067 cm_server_t *
1068 cm_FindServerByUuid(afsUUID *serverUuid, int type, int locked)
1069 {
1070     cm_server_t *tsp;
1071
1072     if (!locked)
1073         lock_ObtainRead(&cm_serverLock);
1074
1075     for (tsp = cm_serversAllFirstp;
1076          tsp;
1077          tsp = (cm_server_t *)osi_QNext(&tsp->allq)) {
1078         if (tsp->type == type && afs_uuid_equal(&tsp->uuid, serverUuid))
1079             break;
1080     }
1081
1082     /* bump ref count if we found the server */
1083     if (tsp)
1084         cm_GetServerNoLock(tsp);
1085
1086     if (!locked)
1087         lock_ReleaseRead(&cm_serverLock);
1088
1089     return tsp;
1090 }
1091
1092 /* find a server based on its properties */
1093 cm_server_t *cm_FindServer(struct sockaddr_in *addrp, int type, int locked)
1094 {
1095     osi_assertx(addrp->sin_family == AF_INET, "unexpected socket value");
1096
1097     return cm_FindServerByIP(addrp->sin_addr.s_addr, addrp->sin_port, type, locked);
1098 }
1099
1100 cm_server_vols_t *cm_NewServerVols(void) {
1101     cm_server_vols_t *tsvp;
1102
1103     tsvp = malloc(sizeof(*tsvp));
1104     if (tsvp)
1105         memset(tsvp, 0, sizeof(*tsvp));
1106
1107     return tsvp;
1108 }
1109
1110 /*
1111  * cm_NewServerRef() returns with the allocated cm_serverRef_t
1112  * with a refCount of 1.
1113  */
1114 cm_serverRef_t *cm_NewServerRef(cm_server_t *serverp, afs_uint32 volID)
1115 {
1116     cm_serverRef_t *tsrp;
1117     cm_server_vols_t **tsrvpp = NULL;
1118     afs_uint32 *slotp = NULL;
1119     int found = 0;
1120
1121     cm_GetServer(serverp);
1122     tsrp = malloc(sizeof(*tsrp));
1123     tsrp->server = serverp;
1124     tsrp->status = srv_not_busy;
1125     tsrp->next = NULL;
1126     tsrp->volID = volID;
1127     tsrp->refCount = 1;
1128
1129     /* if we have a non-zero volID, we need to add it to the list
1130      * of volumes maintained by the server.  There are two phases:
1131      * (1) see if the volID is already in the list and (2) insert
1132      * it into the first empty slot if it is not.
1133      */
1134     if (volID) {
1135         lock_ObtainMutex(&serverp->mx);
1136
1137         tsrvpp = &serverp->vols;
1138         while (*tsrvpp) {
1139             int i;
1140
1141             for (i=0; i<NUM_SERVER_VOLS; i++) {
1142                 if ((*tsrvpp)->ids[i] == volID) {
1143                     found = 1;
1144                     break;
1145                 } else if (!slotp && (*tsrvpp)->ids[i] == 0) {
1146                     slotp = &(*tsrvpp)->ids[i];
1147                 }
1148             }
1149
1150             if (found)
1151                 break;
1152
1153             tsrvpp = &(*tsrvpp)->nextp;
1154         }
1155
1156         if (!found) {
1157             if (slotp) {
1158                 *slotp = volID;
1159             } else {
1160                 /* if we didn't find an empty slot in a current
1161                  * page we must need a new page */
1162                 *tsrvpp = cm_NewServerVols();
1163                 if (*tsrvpp)
1164                     (*tsrvpp)->ids[0] = volID;
1165             }
1166         }
1167
1168         lock_ReleaseMutex(&serverp->mx);
1169     }
1170
1171     return tsrp;
1172 }
1173
1174 void cm_GetServerRef(cm_serverRef_t *tsrp, int locked)
1175 {
1176     afs_int32 refCount;
1177
1178     if (!locked)
1179         lock_ObtainRead(&cm_serverLock);
1180     refCount = InterlockedIncrement(&tsrp->refCount);
1181     if (!locked)
1182         lock_ReleaseRead(&cm_serverLock);
1183 }
1184
1185 afs_int32 cm_PutServerRef(cm_serverRef_t *tsrp, int locked)
1186 {
1187     afs_int32 refCount;
1188
1189     if (!locked)
1190         lock_ObtainRead(&cm_serverLock);
1191     refCount = InterlockedDecrement(&tsrp->refCount);
1192     osi_assertx(refCount >= 0, "cm_serverRef_t refCount underflow");
1193
1194     if (!locked)
1195         lock_ReleaseRead(&cm_serverLock);
1196
1197     return refCount;
1198 }
1199
1200 afs_uint32
1201 cm_ServerListSize(cm_serverRef_t* serversp)
1202 {
1203     afs_uint32 count = 0;
1204     cm_serverRef_t *tsrp;
1205
1206     lock_ObtainRead(&cm_serverLock);
1207     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
1208         if (tsrp->status == srv_deleted)
1209             continue;
1210         count++;
1211     }
1212     lock_ReleaseRead(&cm_serverLock);
1213     return count;
1214 }
1215
1216 LONG_PTR cm_ChecksumServerList(cm_serverRef_t *serversp)
1217 {
1218     LONG_PTR sum = 0;
1219     int first = 1;
1220     cm_serverRef_t *tsrp;
1221
1222     lock_ObtainRead(&cm_serverLock);
1223     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
1224         if (tsrp->status == srv_deleted)
1225             continue;
1226         if (first)
1227             first = 0;
1228         else
1229             sum <<= 1;
1230         sum ^= (LONG_PTR) tsrp->server;
1231     }
1232
1233     lock_ReleaseRead(&cm_serverLock);
1234     return sum;
1235 }
1236
1237 /*
1238 ** Insert a server into the server list keeping the list sorted in
1239 ** ascending order of ipRank.
1240 **
1241 ** The refCount of the cm_serverRef_t is not altered.
1242 */
1243 void cm_InsertServerList(cm_serverRef_t** list, cm_serverRef_t* element)
1244 {
1245     cm_serverRef_t      *current;
1246     unsigned short rank;
1247
1248     lock_ObtainWrite(&cm_serverLock);
1249     /*
1250      * Since we are grabbing the serverLock exclusively remove any
1251      * deleted serverRef objects with a zero refcount before
1252      * inserting the new item.
1253      */
1254     if (*list) {
1255         cm_serverRef_t  **currentp = list;
1256         cm_serverRef_t  **nextp = NULL;
1257         cm_serverRef_t  * next = NULL;
1258         cm_server_t     * serverp = NULL;
1259
1260         for (currentp = list; *currentp; currentp = nextp)
1261         {
1262             nextp = &(*currentp)->next;
1263             /* obtain a refcnt on next in case cm_serverLock is dropped */
1264             if (*nextp)
1265                 cm_GetServerRef(*nextp, TRUE);
1266             if ((*currentp)->refCount == 0 &&
1267                 (*currentp)->status == srv_deleted) {
1268                 next = *nextp;
1269
1270                 if ((*currentp)->volID)
1271                     cm_RemoveVolumeFromServer((*currentp)->server, (*currentp)->volID);
1272                 serverp = (*currentp)->server;
1273                 free(*currentp);
1274                 nextp = &next;
1275                 /* cm_FreeServer will drop cm_serverLock if serverp->refCount == 0 */
1276                 cm_FreeServer(serverp);
1277             }
1278             /* drop the next refcnt obtained above. */
1279             if (*nextp)
1280                 cm_PutServerRef(*nextp, TRUE);
1281         }
1282     }
1283
1284     /* insertion into empty list  or at the beginning of the list */
1285     if (!(*list))
1286     {
1287         element->next = NULL;
1288         *list = element;
1289         goto done;
1290     }
1291
1292     /*
1293      * Now that deleted entries have been removed and we know that the
1294      * list was not empty, look for duplicates.  If the element we are
1295      * inserting already exists, discard it.
1296      */
1297     for ( current = *list; current; current = current->next)
1298     {
1299         cm_server_t * server1 = current->server;
1300         cm_server_t * server2 = element->server;
1301
1302         if (current->status == srv_deleted)
1303             continue;
1304
1305         if (server1->type != server2->type)
1306             continue;
1307
1308         if (server1->addr.sin_addr.s_addr != server2->addr.sin_addr.s_addr)
1309             continue;
1310
1311         if ((server1->flags & CM_SERVERFLAG_UUID) != (server2->flags & CM_SERVERFLAG_UUID))
1312             continue;
1313
1314         if ((server1->flags & CM_SERVERFLAG_UUID) &&
1315             !afs_uuid_equal(&server1->uuid, &server2->uuid))
1316             continue;
1317
1318         /* we must have a match, discard the new element */
1319         free(element);
1320         goto done;
1321     }
1322
1323     rank = element->server->activeRank;
1324
1325         /* insertion at the beginning of the list */
1326     if ((*list)->server->activeRank > rank)
1327     {
1328         element->next = *list;
1329         *list = element;
1330         goto done;
1331     }
1332
1333     /* find appropriate place to insert */
1334     for ( current = *list; current->next; current = current->next)
1335     {
1336         if ( current->next->server->activeRank > rank )
1337             break;
1338     }
1339     element->next = current->next;
1340     current->next = element;
1341
1342   done:
1343     lock_ReleaseWrite(&cm_serverLock);
1344 }
1345 /*
1346 ** Re-sort the server list with the modified rank
1347 ** returns 0 if element was changed successfully.
1348 ** returns 1 if  list remained unchanged.
1349 */
1350 long cm_ChangeRankServer(cm_serverRef_t** list, cm_server_t*    server)
1351 {
1352     cm_serverRef_t  **current;
1353     cm_serverRef_t   *element;
1354
1355     lock_ObtainWrite(&cm_serverLock);
1356     current=list;
1357     element=0;
1358
1359     /* if there is max of one element in the list, nothing to sort */
1360     if ( (!*current) || !((*current)->next)  ) {
1361         lock_ReleaseWrite(&cm_serverLock);
1362         return 1;               /* list unchanged: return success */
1363     }
1364
1365     /* if the server is on the list, delete it from list */
1366     while ( *current )
1367     {
1368         if ( (*current)->server == server)
1369         {
1370             element = (*current);
1371             *current = element->next; /* delete it */
1372             break;
1373         }
1374         current = & ( (*current)->next);
1375     }
1376     lock_ReleaseWrite(&cm_serverLock);
1377
1378     /* if this volume is not replicated on this server  */
1379     if (!element)
1380         return 1;       /* server is not on list */
1381
1382     /* re-insert deleted element into the list with modified rank*/
1383     cm_InsertServerList(list, element);
1384
1385     return 0;
1386 }
1387 /*
1388 ** If there are more than one server on the list and the first n servers on
1389 ** the list have the same rank( n>1), then randomise among the first n servers.
1390 */
1391 void cm_RandomizeServer(cm_serverRef_t** list)
1392 {
1393     int                 count, picked;
1394     cm_serverRef_t*     tsrp, *lastTsrp;
1395     unsigned short      lowestRank;
1396
1397     lock_ObtainWrite(&cm_serverLock);
1398     tsrp = *list;
1399
1400     /* an empty list or a list with only one element */
1401     if ( !tsrp || ! tsrp->next ) {
1402         lock_ReleaseWrite(&cm_serverLock);
1403         return ;
1404     }
1405
1406     /* count the number of servers with the lowest rank */
1407     lowestRank = tsrp->server->activeRank;
1408     for ( count=1, tsrp=tsrp->next; tsrp; tsrp=tsrp->next)
1409     {
1410         if ( tsrp->server->activeRank != lowestRank)
1411             break;
1412         else
1413             count++;
1414     }
1415
1416     /* if there is only one server with the lowest rank, we are done */
1417     if ( count <= 1 ) {
1418         lock_ReleaseWrite(&cm_serverLock);
1419         return ;
1420     }
1421
1422     picked = rand() % count;
1423     if ( !picked ) {
1424         lock_ReleaseWrite(&cm_serverLock);
1425         return ;
1426     }
1427
1428     tsrp = *list;
1429     while (--picked >= 0)
1430     {
1431         lastTsrp = tsrp;
1432         tsrp = tsrp->next;
1433     }
1434     lastTsrp->next = tsrp->next;  /* delete random element from list*/
1435     tsrp->next     = *list; /* insert element at the beginning of list */
1436     *list          = tsrp;
1437     lock_ReleaseWrite(&cm_serverLock);
1438 }
1439
1440 /* call cm_FreeServer while holding a write lock on cm_serverLock */
1441 void cm_FreeServer(cm_server_t* serverp)
1442 {
1443     cm_server_vols_t * tsrvp, *nextp;
1444     int delserver = 0;
1445
1446     cm_PutServerNoLock(serverp);
1447     if (serverp->refCount == 0)
1448     {
1449         /*
1450          * we need to check to ensure that all of the connections
1451          * for this server have a 0 refCount; otherwise, they will
1452          * not be garbage collected
1453          *
1454          * must drop the cm_serverLock because cm_GCConnections
1455          * obtains the cm_connLock and that comes first in the
1456          * lock hierarchy.
1457          */
1458         lock_ReleaseWrite(&cm_serverLock);
1459         cm_GCConnections(serverp);  /* connsp */
1460         lock_ObtainWrite(&cm_serverLock);
1461     }
1462
1463
1464     /*
1465      * Once we have the cm_serverLock locked check to make
1466      * sure the refCount is still zero before removing the
1467      * server entirely.
1468      */
1469     if (serverp->refCount == 0) {
1470         if (!(serverp->flags & CM_SERVERFLAG_PREF_SET)) {
1471             osi_QRemoveHT((osi_queue_t **)&cm_serversAllFirstp,
1472                           (osi_queue_t **)&cm_serversAllLastp,
1473                           &serverp->allq);
1474
1475             switch (serverp->type) {
1476             case CM_SERVER_VLDB:
1477                 cm_numVldbServers--;
1478                 break;
1479             case CM_SERVER_FILE:
1480                 cm_numFileServers--;
1481                 break;
1482             }
1483
1484             lock_FinalizeMutex(&serverp->mx);
1485
1486             /* free the volid list */
1487             for ( tsrvp = serverp->vols; tsrvp; tsrvp = nextp) {
1488                 nextp = tsrvp->nextp;
1489                 free(tsrvp);
1490             }
1491
1492             free(serverp);
1493         }
1494     }
1495 }
1496
1497 /* Called with cm_serverLock write locked */
1498 void cm_RemoveVolumeFromServer(cm_server_t * serverp, afs_uint32 volID)
1499 {
1500     cm_server_vols_t * tsrvp;
1501     int i;
1502
1503     if (volID == 0)
1504         return;
1505
1506     for (tsrvp = serverp->vols; tsrvp; tsrvp = tsrvp->nextp) {
1507         for (i=0; i<NUM_SERVER_VOLS; i++) {
1508             if (tsrvp->ids[i] == volID) {
1509                 tsrvp->ids[i] = 0;;
1510                 break;
1511             }
1512         }
1513     }
1514 }
1515
1516 int cm_IsServerListEmpty(cm_serverRef_t *serversp)
1517 {
1518     cm_serverRef_t *tsrp;
1519     int allDeleted = 1;
1520
1521     if (serversp == NULL)
1522         return CM_ERROR_EMPTY;
1523
1524     lock_ObtainRead(&cm_serverLock);
1525     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
1526         if (tsrp->status == srv_deleted)
1527             continue;
1528         allDeleted = 0;
1529         break;
1530     }
1531     lock_ReleaseRead(&cm_serverLock);
1532
1533     return ( allDeleted ? CM_ERROR_EMPTY : 0 );
1534 }
1535
1536 void cm_FreeServerList(cm_serverRef_t** list, afs_uint32 flags)
1537 {
1538     cm_serverRef_t  **current;
1539     cm_serverRef_t  **nextp;
1540     cm_serverRef_t  * next;
1541     cm_server_t     * serverp;
1542     afs_int32         refCount;
1543
1544     lock_ObtainWrite(&cm_serverLock);
1545     current = list;
1546     nextp = 0;
1547     next = 0;
1548
1549     if (*list == NULL)
1550         goto done;
1551
1552     while (*current)
1553     {
1554         nextp = &(*current)->next;
1555         /* obtain a refcnt on next in case cm_serverLock is dropped */
1556         if (*nextp)
1557             cm_GetServerRef(*nextp, TRUE);
1558         refCount = cm_PutServerRef(*current, TRUE);
1559         if (refCount == 0) {
1560             next = *nextp;
1561
1562             if ((*current)->volID)
1563                 cm_RemoveVolumeFromServer((*current)->server, (*current)->volID);
1564             serverp = (*current)->server;
1565             free(*current);
1566             *current = next;
1567             /* cm_FreeServer will drop cm_serverLock if serverp->refCount == 0 */
1568             cm_FreeServer(serverp);
1569         } else {
1570             if (flags & CM_FREESERVERLIST_DELETE) {
1571                 (*current)->status = srv_deleted;
1572                 if ((*current)->volID)
1573                     cm_RemoveVolumeFromServer((*current)->server, (*current)->volID);
1574             }
1575             current = nextp;
1576         }
1577         /* drop the next refcnt obtained above. */
1578         if (*current)
1579             cm_PutServerRef(*current, TRUE);
1580     }
1581
1582   done:
1583
1584     lock_ReleaseWrite(&cm_serverLock);
1585 }
1586
1587 /* dump all servers to a file.
1588  * cookie is used to identify this batch for easy parsing,
1589  * and it a string provided by a caller
1590  */
1591 int cm_DumpServers(FILE *outputFile, char *cookie, int lock)
1592 {
1593     int zilch;
1594     cm_server_t *tsp;
1595     char output[1024];
1596     char uuidstr[128];
1597     char hoststr[16];
1598
1599     if (lock)
1600         lock_ObtainRead(&cm_serverLock);
1601
1602     sprintf(output,
1603             "%s - dumping servers - cm_numFileServers=%d, cm_numVldbServers=%d\r\n",
1604             cookie, cm_numFileServers, cm_numVldbServers);
1605     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1606
1607     for (tsp = cm_serversAllFirstp;
1608          tsp;
1609          tsp = (cm_server_t *)osi_QNext(&tsp->allq))
1610     {
1611         char * type;
1612         char * down;
1613
1614         switch (tsp->type) {
1615         case CM_SERVER_VLDB:
1616             type = "vldb";
1617             break;
1618         case CM_SERVER_FILE:
1619             type = "file";
1620             break;
1621         default:
1622             type = "unknown";
1623         }
1624
1625         afsUUID_to_string(&tsp->uuid, uuidstr, sizeof(uuidstr));
1626         afs_inet_ntoa_r(tsp->addr.sin_addr.s_addr, hoststr);
1627         down = ctime(&tsp->downTime);
1628         down[strlen(down)-1] = '\0';
1629
1630         sprintf(output,
1631                  "%s - tsp=0x%p cell=%s addr=%-15s port=%u uuid=%s type=%s caps=0x%x "
1632                  "flags=0x%x waitCount=%u rank=%u downTime=\"%s\" refCount=%u\r\n",
1633                  cookie, tsp, tsp->cellp ? tsp->cellp->name : "", hoststr,
1634                  ntohs(tsp->addr.sin_port), uuidstr, type,
1635                  tsp->capabilities, tsp->flags, tsp->waitCount, tsp->activeRank,
1636                  (tsp->flags & CM_SERVERFLAG_DOWN) ?  "down" : "up",
1637                  tsp->refCount);
1638         WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1639     }
1640     sprintf(output, "%s - Done dumping servers.\r\n", cookie);
1641     WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1642
1643     if (lock)
1644         lock_ReleaseRead(&cm_serverLock);
1645
1646     return (0);
1647 }
1648
1649 /*
1650  * Determine if two servers are in fact the same.
1651  *
1652  * Returns 1 if they match, 0 if they do not
1653  */
1654 int cm_ServerEqual(cm_server_t *srv1, cm_server_t *srv2)
1655 {
1656     RPC_STATUS status;
1657
1658     if (srv1 == NULL || srv2 == NULL)
1659         return 0;
1660
1661     if (srv1 == srv2)
1662         return 1;
1663
1664     if (srv1->flags & CM_SERVERFLAG_UUID) {
1665         if (!(srv2->flags & CM_SERVERFLAG_UUID))
1666             return 0;
1667
1668         /* Both support UUID */
1669         if (UuidEqual((UUID *)&srv1->uuid, (UUID *)&srv2->uuid, &status))
1670             return 1;
1671     } else {
1672         if (srv2->flags & CM_SERVERFLAG_UUID)
1673             return 0;
1674
1675         /* Neither support UUID so perform an addr/port comparison */
1676         if ( srv1->addr.sin_family == srv2->addr.sin_family &&
1677              srv1->addr.sin_addr.s_addr == srv2->addr.sin_addr.s_addr &&
1678              srv1->addr.sin_port == srv2->addr.sin_port )
1679             return 1;
1680     }
1681
1682     return 0;
1683 }
1684