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