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