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