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