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