2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
12 #include "afs/param.h"
14 #include <afs/param.h>
20 #include "afs/sysincludes.h"
21 #include "afsincludes.h"
26 #include "afs/rxgen_consts.h"
28 #include "afs/pthread_glock.h"
29 #else /* defined(UKERNEL) */
31 #include <afs/pthread_glock.h>
41 #include <netinet/in.h>
43 #include <afs/rxgen_consts.h>
45 #endif /* defined(UKERNEL) */
48 afs_int32 ubik_CallIter();
49 short ubik_initializationState; /* initial state is zero */
53 * parse list for clients
55 int ubik_ParseClientList(
62 register struct hostent *th;
63 afs_int32 temp, counter;
66 inServer = 0; /* haven't seen -servers yet */
68 for(i=1; i<argc; i++) {
69 /* look for -servers argument */
73 if (*tp == '-') break; /* done */
74 /* otherwise this is a new host name */
76 th = gethostbyname(tp);
81 memmove((void *) &temp, (const void *) th->h_addr, sizeof(afs_int32));
83 if (counter++ >= MAXSERVERS) return UNHOSTS;
87 /* haven't seen a -server yet */
88 if (!strcmp(tp, "-servers")) {
94 /* never saw a -server */
97 if (counter < MAXSERVERS) *aothers++ = 0; /* null terminate if room */
101 #ifdef AFS_PTHREAD_ENV
105 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
106 static int called_afs_random_once;
107 static pthread_key_t random_number_key;
109 static void afs_random_once(void)
111 assert(pthread_key_create(&random_number_key, NULL)==0);
112 called_afs_random_once = 1;
117 * Random number generator and constants from KnuthV2 2d ed, p170
121 * m is a power of two
123 * a is 0.73m should be 0.01m .. 0.99m
124 * c is more or less immaterial. 1 or a is suggested.
126 * NB: LOW ORDER BITS are not very random. To get small random numbers,
127 * treat result as <1, with implied binary point, and multiply by
129 * NB: Has to be unsigned, since shifts on signed quantities may preserve
132 * In this case, m == 2^32, the mod operation is implicit. a == pi, which
133 * is used because it has some interesting characteristics (lacks any
134 * interesting bit-patterns).
139 * use time and pid to try to get some initial randomness.
141 #if !defined(UKERNEL)
142 #define ranstage(x) (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
144 unsigned int afs_random(void)
146 #ifdef AFS_PTHREAD_ENV
149 (called_afs_random_once || pthread_once(&random_once, afs_random_once));
150 state = (afs_uint32) pthread_getspecific(random_number_key);
152 static afs_uint32 state = 0;
157 state = time(0) + getpid();
164 #ifdef AFS_PTHREAD_ENV
165 pthread_setspecific(random_number_key, (const void *) state);
172 * returns int 0..14 using the high bits of a pseudo-random number instead of
173 * the low bits, as the low bits are "less random" than the high ones...
174 * slight roundoff error exists, an excercise for the reader.
175 * need to multiply by something with lots of ones in it, so multiply by
176 * 8 or 16 is right out.
179 static unsigned int afs_randomMod15(void)
183 temp = afs_random() >> 4;
184 temp = (temp *15) >> 28;
188 #endif /* !defined(UKERNEL) */
193 #define abs(a) ((a) < 0 ? -1*(a) : (a))
195 register struct rx_connection **serverconns,
196 struct ubik_client **aclient)
201 register struct ubik_client *tc;
203 initialize_U_error_table();
205 if ( *aclient ) { /* the application is doing a re-initialization*/
206 LOCK_UBIK_CLIENT((*aclient))
207 /* this is an important defensive check */
208 if ( ! ((*aclient)->initializationState) ) {
209 UNLOCK_UBIK_CLIENT((*aclient))
210 return UREINITIALIZE;
213 /* release all existing connections */
214 for (tc = *aclient, i=0; i<MAXSERVERS; i++)
216 struct rx_connection *rxConn = ubik_GetRPCConn(tc,i);
217 if (rxConn == 0) break;
218 #ifdef AFS_PTHREAD_ENV
219 rx_ReleaseCachedConnection(rxConn);
221 rx_DestroyConnection (rxConn);
224 UNLOCK_UBIK_CLIENT((*aclient))
225 #ifdef AFS_PTHREAD_ENV
226 if (pthread_mutex_destroy(&((*aclient)->cm))) return UMUTEXDESTROY;
229 tc = (struct ubik_client *) malloc(sizeof(struct ubik_client));
231 if (tc == NULL) return UNOMEM;
232 memset((void *) tc, 0, sizeof(*tc));
233 #ifdef AFS_PTHREAD_ENV
234 if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t*)0)) {
238 tc->initializationState = ++ubik_initializationState;
240 /* first count the # of server conns so we can randomize properly */
242 for(i=0;i<MAXSERVERS;i++) {
243 if (serverconns[i] == (struct rx_connection *) 0) break;
247 /* here count is the # of servers we're actually passed in. Compute
248 * offset, a number between 0..count-1, where we'll start copying from the
249 * client-provided array. */
250 for (i=0; i< count; i++) {
251 offset = afs_randomMod15() % count;
252 for (j=abs(offset); j<2*count; j++) {
253 if (!tc->conns[abs(j%count)]) {
254 tc->conns[abs(j%count)] = serverconns[i];
265 * ubik_ClientDestroy - destroys a ubik connection. It calls rx to destroy the
266 * component rx connections, then frees the ubik connection structure.
269 afs_int32 ubik_ClientDestroy(struct ubik_client *aclient)
273 if (aclient == 0) return 0;
274 LOCK_UBIK_CLIENT(aclient);
275 for (c=0; c<MAXSERVERS; c++) {
276 struct rx_connection *rxConn = ubik_GetRPCConn(aclient,c);
277 if (rxConn == 0) break;
278 #ifdef AFS_PTHREAD_ENV
279 rx_ReleaseCachedConnection(rxConn);
281 rx_DestroyConnection (rxConn);
284 aclient->initializationState = 0; /* client in not initialized*/
285 UNLOCK_UBIK_CLIENT(aclient);
286 #ifdef AFS_PTHREAD_ENV
287 pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
294 * RefreshConn -- So that intermittent failures that cause connections to die
295 * don't kill whole ubik connection, refresh them when the connection is in
299 static struct rx_connection *RefreshConn(struct rx_connection *tc)
304 struct rx_securityClass *sc;
306 struct rx_connection *newTc;
308 host = rx_HostOf(rx_PeerOf(tc));
309 port = rx_PortOf(rx_PeerOf(tc));
310 service = rx_ServiceIdOf(tc);
311 sc = rx_SecurityObjectOf(tc);
312 si = rx_SecurityClassOf(tc);
315 * destroy old one after creating new one so that refCount on security
316 * object cannot reach zero.
318 newTc = rx_NewConnection (host, port, service, sc, si);
319 rx_DestroyConnection (tc);
323 #ifdef AFS_PTHREAD_ENV
325 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
326 pthread_mutex_t ubik_client_mutex;
327 #define LOCK_UCLNT_CACHE \
328 assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
329 pthread_mutex_lock(&ubik_client_mutex)==0);
330 #define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0);
332 void ubik_client_init_mutex() {
333 assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
338 #define LOCK_UCLNT_CACHE
339 #define UNLOCK_UCLNT_CACHE
344 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
345 static int synccount=0;
348 * call this instead of stub and we'll guarantee to find a host that's up.
349 * in the future, we should also put in a protocol to find the sync site
351 afs_int32 ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9,
352 p10, p11, p12, p13, p14, p15, p16)
354 register struct ubik_client *aclient;
373 afs_int32 rcode, code, newHost, thisHost, i, count;
374 int chaseCount, pass, needsync, inlist, j;
375 struct rx_connection *tc;
379 if (!aclient) return UNOENT;
380 LOCK_UBIK_CLIENT(aclient);
383 origLevel = aclient->initializationState;
385 chaseCount = inlist = needsync = 0;
388 for (j=0; ((j<SYNCCOUNT) && calls_needsync[j]); j++) {
389 if (calls_needsync[j] == (int *)aproc) {
390 inlist = needsync = 1;
397 * First pass, we try all servers that are up.
398 * Second pass, we try all servers.
400 for (pass=0; pass<2; pass++) { /*p*/
401 /* For each entry in our servers list */
402 for (count=0; ;count++) { /*s*/
405 /* Need a sync site. Lets try to quickly find it */
406 if (aclient->syncSite) {
407 newHost = aclient->syncSite; /* already in network order */
408 aclient->syncSite = 0; /* Will reset if it works */
409 } else if (aclient->conns[3]) {
410 /* If there are fewer than four db servers in a cell,
411 * there's no point in making the GetSyncSite call.
412 * At best, it's a wash. At worst, it results in more
413 * RPCs than you would otherwise make.
415 tc = aclient->conns[count];
416 if (tc && rx_ConnError(tc)) {
417 aclient->conns[count] = tc = RefreshConn(tc);
420 code = VOTE_GetSyncSite(tc, &newHost);
421 if ( aclient->initializationState != origLevel)
422 goto restart; /* somebody did a ubik_ClientInit */
423 if (code) newHost = 0;
424 newHost = htonl(newHost); /* convert to network order */
429 /* position count at the appropriate slot in the client
430 * structure and retry. If we can't find in slot, we'll
431 * just continue through the whole list
433 for (i=0; i<MAXSERVERS && aclient->conns[i]; i++) {
434 rxp = rx_PeerOf(aclient->conns[i]);
435 thisHost = rx_HostOf(rxp);
436 if (!thisHost) break;
437 if (thisHost == newHost) {
438 if (chaseCount++ > 2) break; /* avoid loop asking */
439 count = i; /* this index is the sync site */
446 tc = aclient->conns[count];
447 if (tc && rx_ConnError(tc)) {
448 aclient->conns[count] = tc = RefreshConn(tc);
452 if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
453 continue; /* this guy's down */
456 rcode = (*aproc)(tc, p1, p2, p3, p4, p5, p6, p7, p8, p9,
457 p10, p11, p12, p13, p14, p15, p16);
458 if ( aclient->initializationState != origLevel) {
459 /* somebody did a ubik_ClientInit */
460 if ( rcode ) goto restart; /* call failed */
461 else goto done; /* call suceeded */
463 if (rcode < 0) { /* network errors */
464 aclient->states[count] |= CFLastFailed; /* Mark serer down */
466 else if (rcode == UNOTSYNC) {
469 else if (rcode != UNOQUORUM) {
470 /* either misc ubik code, or misc appl code, or success. */
471 aclient->states[count] &= ~CFLastFailed; /* mark server up */
472 goto done; /* all done */
479 if (!inlist) { /* Remember proc call that needs sync site */
481 calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
486 if (!rcode) { /* Remember the sync site - cmd successful */
487 rxp = rx_PeerOf(aclient->conns[count]);
488 aclient->syncSite = rx_HostOf(rxp);
491 UNLOCK_UBIK_CLIENT(aclient);
498 * call this after getting back a UNOTSYNC
499 * note that getting a UNOTSYNC error code back does *not* guarantee
500 * that there is a sync site yet elected. However, if there is a sync
501 * site out there somewhere, and you're trying an operation that
502 * requires a sync site, ubik will return UNOTSYNC, indicating the
503 * operation won't work until you find a sync site
505 static int try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos) {
509 afs_int32 thisHost, newHost;
510 struct rx_connection *tc;
513 origLevel = aclient->initializationState;
516 tc = aclient->conns[apos];
517 if (tc && rx_ConnError (tc)) {
518 aclient->conns[apos] = (tc = RefreshConn (tc));
524 /* now see if we can find the sync site host */
525 code = VOTE_GetSyncSite(tc, &newHost);
526 if ( aclient->initializationState != origLevel) {
527 return -1; /* somebody did a ubik_ClientInit */
530 if ( !code && newHost ) {
531 newHost = htonl(newHost); /* convert back to network order */
534 * position count at the appropriate slot in the client
535 * structure and retry. If we can't find in slot, we'll just
536 * continue through the whole list
538 for(i=0;i<MAXSERVERS;i++) {
539 rxp = rx_PeerOf(aclient->conns[i]);
540 thisHost = rx_HostOf(rxp);
544 else if (thisHost == newHost) {
545 return i; /* we were told to use this one */
553 * Create an internal version of ubik_CallIter that takes an additional
554 * parameter - to indicate whether the ubik client handle has already
561 static afs_int32 CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6,
562 p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, needlock)
564 register struct ubik_client *aclient;
585 register afs_int32 code;
586 struct rx_connection *tc;
590 LOCK_UBIK_CLIENT(aclient)
592 origLevel = aclient->initializationState;
596 while (*apos < MAXSERVERS)
598 /* tc is the next conn to try */
599 tc = aclient->conns[*apos];
602 UNLOCK_UBIK_CLIENT(aclient)
607 if (rx_ConnError (tc)) {
608 tc = RefreshConn (tc);
609 aclient->conns[*apos] = tc;
612 if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
613 (*apos)++; /* try another one if this server is down */
616 break; /* this is the desired path */
619 if (*apos >= MAXSERVERS) {
621 UNLOCK_UBIK_CLIENT(aclient)
626 code = (*aproc)(tc,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16);
627 if ( aclient->initializationState != origLevel) {
629 UNLOCK_UBIK_CLIENT(aclient)
631 return code; /* somebody did a ubik_ClientInit */
634 /* what should I do in case of UNOQUORUM ? */
636 aclient->states[*apos] |= CFLastFailed; /* network errors */
639 /* either misc ubik code, or misc application code or success. */
640 aclient->states[*apos] &= ~CFLastFailed; /* operation worked */
645 UNLOCK_UBIK_CLIENT(aclient)
651 * call this instead of stub and we'll guarantee to find a host that's up.
652 * in the future, we should also put in a protocol to find the sync site
654 afs_int32 ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8,
655 p9, p10, p11, p12, p13, p14, p15, p16)
657 register struct ubik_client *aclient;
676 afs_int32 code, rcode;
683 LOCK_UBIK_CLIENT(aclient)
686 origLevel = aclient->initializationState;
688 /* Do two passes. First pass only checks servers known running */
689 for (aflags |= UPUBIKONLY, pass=0; pass<2; pass++, aflags &= ~UPUBIKONLY) {
693 code = CallIter(aproc, aclient, aflags, &count, p1,p2,p3,p4,
694 p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,NO_LOCK);
695 if ( code && ( aclient->initializationState != origLevel)) {
698 if (code == UNOSERVERS) {
701 rcode = code; /* remember code from last good call */
703 if (code == UNOTSYNC) { /* means this requires a sync site */
704 if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
705 temp = try_GetSyncSite(aclient, count);
706 if ( aclient->initializationState != origLevel) {
707 goto restart; /* somebody did a ubik_ClientInit */
709 if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
710 count = temp; /* generally try to make progress */
714 else if ((code >= 0) && (code != UNOQUORUM)) {
715 UNLOCK_UBIK_CLIENT(aclient)
716 return code; /* success or global error condition */
720 UNLOCK_UBIK_CLIENT(aclient)
725 * This is part of an iterator. It doesn't handle finding sync sites
727 afs_int32 ubik_CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
728 p8, p9, p10, p11, p12, p13, p14, p15, p16)
730 register struct ubik_client *aclient;
750 return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5,
751 p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,