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>
21 #include "afs/sysincludes.h"
22 #include "afsincludes.h"
27 #include "afs/rxgen_consts.h"
29 #include "afs/pthread_glock.h"
30 #else /* defined(UKERNEL) */
32 #include <afs/pthread_glock.h>
42 #include <netinet/in.h>
44 #include <afs/rxgen_consts.h>
46 #endif /* defined(UKERNEL) */
49 afs_int32 ubik_CallIter();
50 short ubik_initializationState; /* initial state is zero */
54 * parse list for clients
57 ubik_ParseClientList(int argc, char **argv, afs_int32 * aothers)
61 register struct hostent *th;
62 afs_int32 temp, counter;
65 inServer = 0; /* haven't seen -servers yet */
67 for (i = 1; i < argc; i++) {
68 /* look for -servers argument */
74 /* otherwise this is a new host name */
76 th = gethostbyname(tp);
81 memmove((void *)&temp, (const void *)th->h_addr,
84 if (counter++ >= MAXSERVERS)
88 /* haven't seen a -server yet */
89 if (!strcmp(tp, "-servers")) {
95 /* never saw a -server */
98 if (counter < MAXSERVERS)
99 *aothers++ = 0; /* null terminate if room */
103 #ifdef AFS_PTHREAD_ENV
107 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
108 static int called_afs_random_once;
109 static pthread_key_t random_number_key;
112 afs_random_once(void)
114 assert(pthread_key_create(&random_number_key, NULL) == 0);
115 called_afs_random_once = 1;
120 * Random number generator and constants from KnuthV2 2d ed, p170
124 * m is a power of two
126 * a is 0.73m should be 0.01m .. 0.99m
127 * c is more or less immaterial. 1 or a is suggested.
129 * NB: LOW ORDER BITS are not very random. To get small random numbers,
130 * treat result as <1, with implied binary point, and multiply by
132 * NB: Has to be unsigned, since shifts on signed quantities may preserve
135 * In this case, m == 2^32, the mod operation is implicit. a == pi, which
136 * is used because it has some interesting characteristics (lacks any
137 * interesting bit-patterns).
142 * use time and pid to try to get some initial randomness.
144 #if !defined(UKERNEL)
145 #define ranstage(x) (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
150 #ifdef AFS_PTHREAD_ENV
153 (called_afs_random_once || pthread_once(&random_once, afs_random_once));
154 state = (afs_uint32) pthread_getspecific(random_number_key);
156 static afs_uint32 state = 0;
161 state = time(0) + getpid();
162 for (i = 0; i < 15; i++) {
168 #ifdef AFS_PTHREAD_ENV
169 pthread_setspecific(random_number_key, (const void *)state);
176 * returns int 0..14 using the high bits of a pseudo-random number instead of
177 * the low bits, as the low bits are "less random" than the high ones...
178 * slight roundoff error exists, an excercise for the reader.
179 * need to multiply by something with lots of ones in it, so multiply by
180 * 8 or 16 is right out.
184 afs_randomMod15(void)
188 temp = afs_random() >> 4;
189 temp = (temp * 15) >> 28;
193 #endif /* !defined(UKERNEL) */
198 #define abs(a) ((a) < 0 ? -1*(a) : (a))
200 ubik_ClientInit(register struct rx_connection **serverconns,
201 struct ubik_client **aclient)
206 register struct ubik_client *tc;
208 initialize_U_error_table();
210 if (*aclient) { /* the application is doing a re-initialization */
211 LOCK_UBIK_CLIENT((*aclient));
212 /* this is an important defensive check */
213 if (!((*aclient)->initializationState)) {
214 UNLOCK_UBIK_CLIENT((*aclient));
215 return UREINITIALIZE;
218 /* release all existing connections */
219 for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
220 struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
223 #ifdef AFS_PTHREAD_ENV
224 rx_ReleaseCachedConnection(rxConn);
226 rx_DestroyConnection(rxConn);
229 UNLOCK_UBIK_CLIENT((*aclient));
230 #ifdef AFS_PTHREAD_ENV
231 if (pthread_mutex_destroy(&((*aclient)->cm)))
232 return UMUTEXDESTROY;
235 tc = (struct ubik_client *)malloc(sizeof(struct ubik_client));
239 memset((void *)tc, 0, sizeof(*tc));
240 #ifdef AFS_PTHREAD_ENV
241 if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
245 tc->initializationState = ++ubik_initializationState;
247 /* first count the # of server conns so we can randomize properly */
249 for (i = 0; i < MAXSERVERS; i++) {
250 if (serverconns[i] == (struct rx_connection *)0)
255 /* here count is the # of servers we're actually passed in. Compute
256 * offset, a number between 0..count-1, where we'll start copying from the
257 * client-provided array. */
258 for (i = 0; i < count; i++) {
259 offset = afs_randomMod15() % count;
260 for (j = abs(offset); j < 2 * count; j++) {
261 if (!tc->conns[abs(j % count)]) {
262 tc->conns[abs(j % count)] = serverconns[i];
273 * ubik_ClientDestroy - destroys a ubik connection. It calls rx to destroy the
274 * component rx connections, then frees the ubik connection structure.
278 ubik_ClientDestroy(struct ubik_client * aclient)
284 LOCK_UBIK_CLIENT(aclient);
285 for (c = 0; c < MAXSERVERS; c++) {
286 struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
289 #ifdef AFS_PTHREAD_ENV
290 rx_ReleaseCachedConnection(rxConn);
292 rx_DestroyConnection(rxConn);
295 aclient->initializationState = 0; /* client in not initialized */
296 UNLOCK_UBIK_CLIENT(aclient);
297 #ifdef AFS_PTHREAD_ENV
298 pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
305 * RefreshConn -- So that intermittent failures that cause connections to die
306 * don't kill whole ubik connection, refresh them when the connection is in
310 static struct rx_connection *
311 RefreshConn(struct rx_connection *tc)
316 struct rx_securityClass *sc;
318 struct rx_connection *newTc;
320 host = rx_HostOf(rx_PeerOf(tc));
321 port = rx_PortOf(rx_PeerOf(tc));
322 service = rx_ServiceIdOf(tc);
323 sc = rx_SecurityObjectOf(tc);
324 si = rx_SecurityClassOf(tc);
327 * destroy old one after creating new one so that refCount on security
328 * object cannot reach zero.
330 newTc = rx_NewConnection(host, port, service, sc, si);
331 rx_DestroyConnection(tc);
335 #ifdef AFS_PTHREAD_ENV
337 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
338 pthread_mutex_t ubik_client_mutex;
339 #define LOCK_UCLNT_CACHE \
340 assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
341 pthread_mutex_lock(&ubik_client_mutex)==0)
342 #define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0)
345 ubik_client_init_mutex()
347 assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
352 #define LOCK_UCLNT_CACHE
353 #define UNLOCK_UCLNT_CACHE
358 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
359 static int synccount = 0;
362 * call this instead of stub and we'll guarantee to find a host that's up.
363 * in the future, we should also put in a protocol to find the sync site
366 ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
367 p11, p12, p13, p14, p15, p16)
369 register struct ubik_client *aclient;
388 afs_int32 rcode, code, newHost, thisHost, i, count;
389 int chaseCount, pass, needsync, inlist, j;
390 struct rx_connection *tc;
396 LOCK_UBIK_CLIENT(aclient);
399 origLevel = aclient->initializationState;
401 chaseCount = inlist = needsync = 0;
404 for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
405 if (calls_needsync[j] == (int *)aproc) {
406 inlist = needsync = 1;
412 * First pass, we try all servers that are up.
413 * Second pass, we try all servers.
415 for (pass = 0; pass < 2; pass++) { /*p */
416 /* For each entry in our servers list */
417 for (count = 0;; count++) { /*s */
420 /* Need a sync site. Lets try to quickly find it */
421 if (aclient->syncSite) {
422 newHost = aclient->syncSite; /* already in network order */
423 aclient->syncSite = 0; /* Will reset if it works */
424 } else if (aclient->conns[3]) {
425 /* If there are fewer than four db servers in a cell,
426 * there's no point in making the GetSyncSite call.
427 * At best, it's a wash. At worst, it results in more
428 * RPCs than you would otherwise make.
430 tc = aclient->conns[count];
431 if (tc && rx_ConnError(tc)) {
432 aclient->conns[count] = tc = RefreshConn(tc);
436 code = VOTE_GetSyncSite(tc, &newHost);
437 if (aclient->initializationState != origLevel)
438 goto restart; /* somebody did a ubik_ClientInit */
441 newHost = htonl(newHost); /* convert to network order */
446 /* position count at the appropriate slot in the client
447 * structure and retry. If we can't find in slot, we'll
448 * just continue through the whole list
450 for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
451 rxp = rx_PeerOf(aclient->conns[i]);
452 thisHost = rx_HostOf(rxp);
455 if (thisHost == newHost) {
456 if (chaseCount++ > 2)
457 break; /* avoid loop asking */
458 count = i; /* this index is the sync site */
465 tc = aclient->conns[count];
466 if (tc && rx_ConnError(tc)) {
467 aclient->conns[count] = tc = RefreshConn(tc);
472 if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
473 continue; /* this guy's down */
477 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
478 p12, p13, p14, p15, p16);
479 if (aclient->initializationState != origLevel) {
480 /* somebody did a ubik_ClientInit */
482 goto restart; /* call failed */
484 goto done; /* call suceeded */
486 if (rcode < 0) { /* network errors */
487 aclient->states[count] |= CFLastFailed; /* Mark serer down */
488 } else if (rcode == UNOTSYNC) {
490 } else if (rcode != UNOQUORUM) {
491 /* either misc ubik code, or misc appl code, or success. */
492 aclient->states[count] &= ~CFLastFailed; /* mark server up */
493 goto done; /* all done */
500 if (!inlist) { /* Remember proc call that needs sync site */
502 calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
507 if (!rcode) { /* Remember the sync site - cmd successful */
508 rxp = rx_PeerOf(aclient->conns[count]);
509 aclient->syncSite = rx_HostOf(rxp);
512 UNLOCK_UBIK_CLIENT(aclient);
519 * call this after getting back a UNOTSYNC
520 * note that getting a UNOTSYNC error code back does *not* guarantee
521 * that there is a sync site yet elected. However, if there is a sync
522 * site out there somewhere, and you're trying an operation that
523 * requires a sync site, ubik will return UNOTSYNC, indicating the
524 * operation won't work until you find a sync site
527 try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
532 afs_int32 thisHost, newHost;
533 struct rx_connection *tc;
536 origLevel = aclient->initializationState;
539 tc = aclient->conns[apos];
540 if (tc && rx_ConnError(tc)) {
541 aclient->conns[apos] = (tc = RefreshConn(tc));
547 /* now see if we can find the sync site host */
548 code = VOTE_GetSyncSite(tc, &newHost);
549 if (aclient->initializationState != origLevel) {
550 return -1; /* somebody did a ubik_ClientInit */
553 if (!code && newHost) {
554 newHost = htonl(newHost); /* convert back to network order */
557 * position count at the appropriate slot in the client
558 * structure and retry. If we can't find in slot, we'll just
559 * continue through the whole list
561 for (i = 0; i < MAXSERVERS; i++) {
562 rxp = rx_PeerOf(aclient->conns[i]);
563 thisHost = rx_HostOf(rxp);
566 } else if (thisHost == newHost) {
567 return i; /* we were told to use this one */
575 * Create an internal version of ubik_CallIter that takes an additional
576 * parameter - to indicate whether the ubik client handle has already
584 CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7, p8, p9,
585 p10, p11, p12, p13, p14, p15, p16, needlock)
587 register struct ubik_client *aclient;
608 register afs_int32 code;
609 struct rx_connection *tc;
613 LOCK_UBIK_CLIENT(aclient);
615 origLevel = aclient->initializationState;
619 while (*apos < MAXSERVERS) {
620 /* tc is the next conn to try */
621 tc = aclient->conns[*apos];
624 UNLOCK_UBIK_CLIENT(aclient);
629 if (rx_ConnError(tc)) {
630 tc = RefreshConn(tc);
631 aclient->conns[*apos] = tc;
634 if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
635 (*apos)++; /* try another one if this server is down */
637 break; /* this is the desired path */
640 if (*apos >= MAXSERVERS) {
642 UNLOCK_UBIK_CLIENT(aclient);
648 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
650 if (aclient->initializationState != origLevel) {
652 UNLOCK_UBIK_CLIENT(aclient);
654 return code; /* somebody did a ubik_ClientInit */
657 /* what should I do in case of UNOQUORUM ? */
659 aclient->states[*apos] |= CFLastFailed; /* network errors */
661 /* either misc ubik code, or misc application code or success. */
662 aclient->states[*apos] &= ~CFLastFailed; /* operation worked */
667 UNLOCK_UBIK_CLIENT(aclient);
673 * call this instead of stub and we'll guarantee to find a host that's up.
674 * in the future, we should also put in a protocol to find the sync site
677 ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
678 p11, p12, p13, p14, p15, p16)
680 register struct ubik_client *aclient;
699 afs_int32 code, rcode;
706 LOCK_UBIK_CLIENT(aclient);
709 origLevel = aclient->initializationState;
711 /* Do two passes. First pass only checks servers known running */
712 for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
713 pass++, aflags &= ~UPUBIKONLY) {
718 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
719 p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
721 if (code && (aclient->initializationState != origLevel)) {
724 if (code == UNOSERVERS) {
727 rcode = code; /* remember code from last good call */
729 if (code == UNOTSYNC) { /* means this requires a sync site */
730 if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
731 temp = try_GetSyncSite(aclient, count);
732 if (aclient->initializationState != origLevel) {
733 goto restart; /* somebody did a ubik_ClientInit */
735 if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
736 count = temp; /* generally try to make progress */
739 } else if ((code >= 0) && (code != UNOQUORUM)) {
740 UNLOCK_UBIK_CLIENT(aclient);
741 return code; /* success or global error condition */
745 UNLOCK_UBIK_CLIENT(aclient);
750 * This is part of an iterator. It doesn't handle finding sync sites
753 ubik_CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7, p8,
754 p9, p10, p11, p12, p13, p14, p15, p16)
756 register struct ubik_client *aclient;
776 return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
777 p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);