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