windows-new-server-ping-20060213
[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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #include <nb30.h>
17 #else
18 #include <sys/socket.h>
19 #endif /* !DJGPP */
20 #include <stdlib.h>
21 #include <malloc.h>
22 #include <string.h>
23
24 #include <osi.h>
25 #include <rx/rx.h>
26 #include "afsd.h"
27
28 osi_rwlock_t cm_serverLock;
29
30 cm_server_t *cm_allServersp;
31
32 void 
33 cm_PingServer(cm_server_t *tsp)
34 {
35     long code;
36     int wasDown = 0;
37     cm_conn_t *connp;
38     struct rx_connection * callp;
39     long secs;
40     long usecs;
41     Capabilities caps = {0, 0};
42
43     code = cm_ConnByServer(tsp, cm_rootUserp, &connp);
44     if (code == 0) {
45         /* now call the appropriate ping call.  Drop the timeout if
46         * the server is known to be down, so that we don't waste a
47         * lot of time retiming out down servers.
48         */
49         wasDown = tsp->flags & CM_SERVERFLAG_DOWN;
50         if (wasDown)
51             rx_SetConnDeadTime(connp->callp, 10);
52         if (tsp->type == CM_SERVER_VLDB) {
53             code = VL_ProbeServer(connp->callp);
54         }
55         else {
56             /* file server */
57             callp = cm_GetRxConn(connp);
58             code = RXAFS_GetCapabilities(callp, &caps);
59             if (code == RXGEN_OPCODE)
60                 code = RXAFS_GetTime(callp, &secs, &usecs);
61             rx_PutConnection(callp);
62         }
63         if (wasDown)
64             rx_SetConnDeadTime(connp->callp, ConnDeadtimeout);
65         cm_PutConn(connp);
66     }   /* got an unauthenticated connection to this server */
67
68     lock_ObtainMutex(&tsp->mx);
69     if (code >= 0) {
70         /* mark server as up */
71         tsp->flags &= ~CM_SERVERFLAG_DOWN;
72
73         /* we currently handle 32-bits of capabilities */
74         if (caps.Capabilities_len > 0) {
75             tsp->capabilities = caps.Capabilities_val[0];
76             free(caps.Capabilities_val);
77             caps.Capabilities_len = 0;
78             caps.Capabilities_val = 0;
79         } else {
80             tsp->capabilities = 0;
81         }
82     }
83     else {
84         /* mark server as down */
85         tsp->flags |= CM_SERVERFLAG_DOWN;
86         if (code != VRESTARTING)
87             cm_ForceNewConnections(tsp);
88     }
89     lock_ReleaseMutex(&tsp->mx);
90 }
91
92
93 void cm_CheckServers(long flags, cm_cell_t *cellp)
94 {
95     /* ping all file servers, up or down, with unauthenticated connection,
96      * to find out whether we have all our callbacks from the server still.
97      * Also, ping down VLDBs.
98      */
99     cm_server_t *tsp;
100     int doPing;
101     int isDown;
102
103     lock_ObtainWrite(&cm_serverLock);
104     for (tsp = cm_allServersp; tsp; tsp = tsp->allNextp) {
105         cm_GetServerNoLock(tsp);
106         lock_ReleaseWrite(&cm_serverLock);
107
108         /* now process the server */
109         lock_ObtainMutex(&tsp->mx);
110
111         doPing = 0;
112         isDown = tsp->flags & CM_SERVERFLAG_DOWN;
113
114         /* only do the ping if the cell matches the requested cell, or we're
115          * matching all cells (cellp == NULL), and if we've requested to ping
116          * this type of {up, down} servers.
117          */
118         if ((cellp == NULL || cellp == tsp->cellp) &&
119              ((isDown && (flags & CM_FLAG_CHECKDOWNSERVERS)) ||
120                (!isDown && (flags & CM_FLAG_CHECKUPSERVERS)))) {
121             doPing = 1;
122         }       /* we're supposed to check this up/down server */
123         lock_ReleaseMutex(&tsp->mx);
124
125         /* at this point, we've adjusted the server state, so do the ping and
126          * adjust things.
127          */
128         if (doPing) 
129             cm_PingServer(tsp);
130
131         /* also, run the GC function for connections on all of the
132          * server's connections.
133          */
134         cm_GCConnections(tsp);
135
136         lock_ObtainWrite(&cm_serverLock);
137         cm_PutServerNoLock(tsp);
138     }
139     lock_ReleaseWrite(&cm_serverLock);
140 }       
141
142 void cm_InitServer(void)
143 {
144     static osi_once_t once;
145         
146     if (osi_Once(&once)) {
147         lock_InitializeRWLock(&cm_serverLock, "cm_serverLock");
148         osi_EndOnce(&once);
149     }
150 }
151
152 void cm_GetServer(cm_server_t *serverp)
153 {
154     lock_ObtainWrite(&cm_serverLock);
155     serverp->refCount++;
156     lock_ReleaseWrite(&cm_serverLock);
157 }
158
159 void cm_GetServerNoLock(cm_server_t *serverp)
160 {
161     serverp->refCount++;
162 }
163
164 void cm_PutServer(cm_server_t *serverp)
165 {
166     lock_ObtainWrite(&cm_serverLock);
167     osi_assert(serverp->refCount-- > 0);
168     lock_ReleaseWrite(&cm_serverLock);
169 }
170
171 void cm_PutServerNoLock(cm_server_t *serverp)
172 {
173     osi_assert(serverp->refCount-- > 0);
174 }
175
176 void cm_SetServerPrefs(cm_server_t * serverp)
177 {
178     unsigned long       serverAddr;     /* in host byte order */
179     unsigned long       myAddr, myNet, mySubnet;/* in host byte order */
180     unsigned long       netMask;
181     int                 i;
182
183     int cm_noIPAddr;         /* number of client network interfaces */
184     int cm_IPAddr[CM_MAXINTERFACE_ADDR];    /* client's IP address in host order */
185     int cm_SubnetMask[CM_MAXINTERFACE_ADDR];/* client's subnet mask in host order*/
186     int cm_NetMtu[CM_MAXINTERFACE_ADDR];    /* client's MTU sizes */
187     int cm_NetFlags[CM_MAXINTERFACE_ADDR];  /* network flags */
188     long code;
189
190     /* get network related info */
191     cm_noIPAddr = CM_MAXINTERFACE_ADDR;
192     code = syscfg_GetIFInfo(&cm_noIPAddr,
193                             cm_IPAddr, cm_SubnetMask,
194                             cm_NetMtu, cm_NetFlags);
195
196     serverAddr = ntohl(serverp->addr.sin_addr.s_addr);
197     serverp->ipRank  = CM_IPRANK_LOW;   /* default setings */
198
199     for ( i=0; i < cm_noIPAddr; i++)
200     {
201         /* loop through all the client's IP address and compare
202         ** each of them against the server's IP address */
203
204         myAddr = cm_IPAddr[i];
205         if ( IN_CLASSA(myAddr) )
206             netMask = IN_CLASSA_NET;
207         else if ( IN_CLASSB(myAddr) )
208             netMask = IN_CLASSB_NET;
209         else if ( IN_CLASSC(myAddr) )
210             netMask = IN_CLASSC_NET;
211         else
212             netMask = 0;
213
214         myNet    =  myAddr & netMask;
215         mySubnet =  myAddr & cm_SubnetMask[i];
216
217         if ( (serverAddr & netMask) == myNet ) 
218         {
219             if ( (serverAddr & cm_SubnetMask[i]) == mySubnet)
220             {
221                 if ( serverAddr == myAddr ) 
222                     serverp->ipRank = min(serverp->ipRank,
223                                            CM_IPRANK_TOP);/* same machine */
224                 else serverp->ipRank = min(serverp->ipRank,
225                                             CM_IPRANK_HI); /* same subnet */
226             }
227             else serverp->ipRank = min(serverp->ipRank,CM_IPRANK_MED);
228             /* same net */
229         }       
230         /* random between 0..15*/
231         serverp->ipRank += min(serverp->ipRank, rand() % 0x000f);
232     } /* and of for loop */
233 }
234
235 cm_server_t *cm_NewServer(struct sockaddr_in *socketp, int type, cm_cell_t *cellp) {
236     cm_server_t *tsp;
237
238     osi_assert(socketp->sin_family == AF_INET);
239
240     tsp = malloc(sizeof(*tsp));
241     memset(tsp, 0, sizeof(*tsp));
242     tsp->type = type;
243     tsp->cellp = cellp;
244     tsp->refCount = 1;
245     lock_InitializeMutex(&tsp->mx, "cm_server_t mutex");
246     tsp->addr = *socketp;
247     tsp->flags = CM_SERVERFLAG_DOWN;    /* assume down; ping will mark up if available */
248
249     cm_SetServerPrefs(tsp); 
250
251     lock_ObtainWrite(&cm_serverLock);   /* get server lock */
252     tsp->allNextp = cm_allServersp;
253     cm_allServersp = tsp;
254     lock_ReleaseWrite(&cm_serverLock);  /* release server lock */
255
256     cm_PingServer(tsp);                 /* Obtain Capabilities and check up/down state */
257     return tsp;
258 }
259
260 /* find a server based on its properties */
261 cm_server_t *cm_FindServer(struct sockaddr_in *addrp, int type)
262 {
263     cm_server_t *tsp;
264
265     osi_assert(addrp->sin_family == AF_INET);
266         
267     lock_ObtainWrite(&cm_serverLock);
268     for (tsp = cm_allServersp; tsp; tsp=tsp->allNextp) {
269         if (tsp->type == type &&
270             tsp->addr.sin_addr.s_addr == addrp->sin_addr.s_addr) 
271             break;
272     }       
273
274     /* bump ref count if we found the server */
275     if (tsp) 
276         cm_GetServerNoLock(tsp);
277
278     /* drop big table lock */
279     lock_ReleaseWrite(&cm_serverLock);
280         
281     /* return what we found */
282     return tsp;
283 }       
284
285 cm_serverRef_t *cm_NewServerRef(cm_server_t *serverp)
286 {
287     cm_serverRef_t *tsrp;
288
289     cm_GetServer(serverp);
290     tsrp = malloc(sizeof(*tsrp));
291     tsrp->server = serverp;
292     tsrp->status = not_busy;
293     tsrp->next = NULL;
294     tsrp->refCount = 1;
295
296     return tsrp;
297 }
298
299 LONG_PTR cm_ChecksumServerList(cm_serverRef_t *serversp)
300 {
301     LONG_PTR sum = 0;
302     int first = 1;
303     cm_serverRef_t *tsrp;
304
305     lock_ObtainWrite(&cm_serverLock);
306     for (tsrp = serversp; tsrp; tsrp=tsrp->next) {
307         if (first)
308             first = 0;
309         else
310             sum <<= 1;
311         sum ^= (LONG_PTR) tsrp->server;
312     }
313
314     lock_ReleaseWrite(&cm_serverLock);
315     return sum;
316 }
317
318 /*
319 ** Insert a server into the server list keeping the list sorted in 
320 ** asending order of ipRank. 
321 ** 
322 ** The refCount of the cm_serverRef_t is increased
323 */
324 void cm_InsertServerList(cm_serverRef_t** list, cm_serverRef_t* element)
325 {
326     cm_serverRef_t      *current=*list;
327     unsigned short ipRank = element->server->ipRank;
328
329     lock_ObtainWrite(&cm_serverLock);
330     element->refCount++;                /* increase refCount */
331
332     /* insertion into empty list  or at the beginning of the list */
333     if ( !current || (current->server->ipRank > ipRank) )
334     {
335         element->next = *list;
336         *list = element;
337         lock_ReleaseWrite(&cm_serverLock);
338         return ;        
339     }
340         
341     while ( current->next ) /* find appropriate place to insert */
342     {
343         if ( current->next->server->ipRank > ipRank )
344             break;
345         else current = current->next;
346     }
347     element->next = current->next;
348     current->next = element;
349     lock_ReleaseWrite(&cm_serverLock);
350 }       
351 /*
352 ** Re-sort the server list with the modified rank
353 ** returns 0 if element was changed successfully. 
354 ** returns 1 if  list remained unchanged.
355 */
356 long cm_ChangeRankServer(cm_serverRef_t** list, cm_server_t*    server)
357 {
358     cm_serverRef_t  **current=list;
359     cm_serverRef_t      *element=0;
360
361     /* if there is max of one element in the list, nothing to sort */
362     if ( (!*current) || !((*current)->next)  )
363         return 1;               /* list unchanged: return success */
364
365     lock_ObtainWrite(&cm_serverLock);
366     /* if the server is on the list, delete it from list */
367     while ( *current )
368     {
369         if ( (*current)->server == server)
370         {
371             element = (*current);
372             *current = (*current)->next; /* delete it */
373             break;
374         }
375         current = & ( (*current)->next);        
376     }
377     lock_ReleaseWrite(&cm_serverLock);
378
379     /* if this volume is not replicated on this server  */
380     if (!element)
381         return 1;       /* server is not on list */
382
383     /* re-insert deleted element into the list with modified rank*/
384     cm_InsertServerList(list, element);
385
386     /* reduce refCount which was increased by cm_InsertServerList */
387     lock_ObtainWrite(&cm_serverLock);
388     element->refCount--;
389     lock_ReleaseWrite(&cm_serverLock);
390     return 0;
391 }
392 /*
393 ** If there are more than one server on the list and the first n servers on 
394 ** the list have the same rank( n>1), then randomise among the first n servers.
395 */
396 void cm_RandomizeServer(cm_serverRef_t** list)
397 {
398     int                 count, picked;
399     cm_serverRef_t*     tsrp = *list, *lastTsrp;
400     unsigned short      lowestRank;
401
402     /* an empty list or a list with only one element */
403     if ( !tsrp || ! tsrp->next )
404         return ; 
405
406     lock_ObtainWrite(&cm_serverLock);
407
408     /* count the number of servers with the lowest rank */
409     lowestRank = tsrp->server->ipRank;
410     for ( count=1, tsrp=tsrp->next; tsrp; tsrp=tsrp->next)
411     {
412         if ( tsrp->server->ipRank != lowestRank)
413             break;
414         else
415             count++;
416     }           
417
418     /* if there is only one server with the lowest rank, we are done */
419     if ( count <= 1 ) {
420         lock_ReleaseWrite(&cm_serverLock);
421         return ;
422     }   
423
424     picked = rand() % count;
425     if ( !picked ) {
426         lock_ReleaseWrite(&cm_serverLock);
427         return ;
428     }   
429
430     tsrp = *list;
431     while (--picked >= 0)
432     {
433         lastTsrp = tsrp;
434         tsrp = tsrp->next;
435     }
436     lastTsrp->next = tsrp->next;  /* delete random element from list*/
437     tsrp->next     = *list; /* insert element at the beginning of list */
438     *list          = tsrp;
439     lock_ReleaseWrite(&cm_serverLock);
440 }       
441
442 /* call cm_FreeServer while holding a write lock on cm_serverLock */
443 void cm_FreeServer(cm_server_t* serverp)
444 {
445     cm_PutServerNoLock(serverp);
446     if (serverp->refCount == 0)
447     {
448         /* we need to check to ensure that all of the connections
449          * for this server have a 0 refCount; otherwise, they will
450          * not be garbage collected 
451          */
452         cm_GCConnections(serverp);  /* connsp */
453
454         if (!(serverp->flags & CM_SERVERFLAG_PREF_SET)) {
455             lock_FinalizeMutex(&serverp->mx);
456             if ( cm_allServersp == serverp )
457                 cm_allServersp = serverp->allNextp;
458             else {
459                 cm_server_t *tsp;
460
461                 for(tsp = cm_allServersp; tsp->allNextp; tsp=tsp->allNextp) {
462                     if ( tsp->allNextp == serverp ) {
463                         tsp->allNextp = serverp->allNextp;
464                         break;
465                     }
466                 }
467             }
468             free(serverp);
469         }
470     }
471 }
472
473 void cm_FreeServerList(cm_serverRef_t** list)
474 {
475     cm_serverRef_t  **current = list;
476     cm_serverRef_t  **nextp = 0;
477     cm_serverRef_t  * next = 0;
478
479     lock_ObtainWrite(&cm_serverLock);
480
481     while (*current)
482     {
483         nextp = &(*current)->next;
484         if (--((*current)->refCount) == 0) {
485             next = *nextp;
486             cm_FreeServer((*current)->server);
487             free(*current);
488             *current = next;
489         } else {
490            current = nextp;
491         }
492     }
493   
494     lock_ReleaseWrite(&cm_serverLock);
495 }
496