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