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 short ubik_initializationState; /* initial state is zero */
53 * parse list for clients
56 ubik_ParseClientList(int argc, char **argv, afs_int32 * aothers)
60 register struct hostent *th;
61 afs_int32 temp, counter;
64 inServer = 0; /* haven't seen -servers yet */
66 for (i = 1; i < argc; i++) {
67 /* look for -servers argument */
73 /* otherwise this is a new host name */
75 th = gethostbyname(tp);
80 memmove((void *)&temp, (const void *)th->h_addr,
83 if (counter++ >= MAXSERVERS)
87 /* haven't seen a -server yet */
88 if (!strcmp(tp, "-servers")) {
94 /* never saw a -server */
97 if (counter < MAXSERVERS)
98 *aothers++ = 0; /* null terminate if room */
102 #ifdef AFS_PTHREAD_ENV
106 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
107 static int called_afs_random_once;
108 static pthread_key_t random_number_key;
111 afs_random_once(void)
113 assert(pthread_key_create(&random_number_key, NULL) == 0);
114 called_afs_random_once = 1;
119 * Random number generator and constants from KnuthV2 2d ed, p170
123 * m is a power of two
125 * a is 0.73m should be 0.01m .. 0.99m
126 * c is more or less immaterial. 1 or a is suggested.
128 * NB: LOW ORDER BITS are not very random. To get small random numbers,
129 * treat result as <1, with implied binary point, and multiply by
131 * NB: Has to be unsigned, since shifts on signed quantities may preserve
134 * In this case, m == 2^32, the mod operation is implicit. a == pi, which
135 * is used because it has some interesting characteristics (lacks any
136 * interesting bit-patterns).
141 * use time and pid to try to get some initial randomness.
143 #if !defined(UKERNEL)
144 #define ranstage(x) (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
149 #ifdef AFS_PTHREAD_ENV
152 (called_afs_random_once || pthread_once(&random_once, afs_random_once));
153 state = (afs_uint32) pthread_getspecific(random_number_key);
155 static afs_uint32 state = 0;
160 state = time(0) + getpid();
161 for (i = 0; i < 15; i++) {
167 #ifdef AFS_PTHREAD_ENV
168 pthread_setspecific(random_number_key, (const void *)state);
175 * returns int 0..14 using the high bits of a pseudo-random number instead of
176 * the low bits, as the low bits are "less random" than the high ones...
177 * slight roundoff error exists, an excercise for the reader.
178 * need to multiply by something with lots of ones in it, so multiply by
179 * 8 or 16 is right out.
183 afs_randomMod15(void)
187 temp = afs_random() >> 4;
188 temp = (temp * 15) >> 28;
192 #endif /* !defined(UKERNEL) */
197 #define abs(a) ((a) < 0 ? -1*(a) : (a))
199 ubik_ClientInit(register struct rx_connection **serverconns,
200 struct ubik_client **aclient)
205 register struct ubik_client *tc;
207 initialize_U_error_table();
209 if (*aclient) { /* the application is doing a re-initialization */
210 LOCK_UBIK_CLIENT((*aclient));
211 /* this is an important defensive check */
212 if (!((*aclient)->initializationState)) {
213 UNLOCK_UBIK_CLIENT((*aclient));
214 return UREINITIALIZE;
217 /* release all existing connections */
218 for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
219 struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
222 #ifdef AFS_PTHREAD_ENV
223 rx_ReleaseCachedConnection(rxConn);
225 rx_DestroyConnection(rxConn);
228 UNLOCK_UBIK_CLIENT((*aclient));
229 #ifdef AFS_PTHREAD_ENV
230 if (pthread_mutex_destroy(&((*aclient)->cm)))
231 return UMUTEXDESTROY;
234 tc = (struct ubik_client *)malloc(sizeof(struct ubik_client));
238 memset((void *)tc, 0, sizeof(*tc));
239 #ifdef AFS_PTHREAD_ENV
240 if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
244 tc->initializationState = ++ubik_initializationState;
246 /* first count the # of server conns so we can randomize properly */
248 for (i = 0; i < MAXSERVERS; i++) {
249 if (serverconns[i] == (struct rx_connection *)0)
254 /* here count is the # of servers we're actually passed in. Compute
255 * offset, a number between 0..count-1, where we'll start copying from the
256 * client-provided array. */
257 for (i = 0; i < count; i++) {
258 offset = afs_randomMod15() % count;
259 for (j = abs(offset); j < 2 * count; j++) {
260 if (!tc->conns[abs(j % count)]) {
261 tc->conns[abs(j % count)] = serverconns[i];
272 * ubik_ClientDestroy - destroys a ubik connection. It calls rx to destroy the
273 * component rx connections, then frees the ubik connection structure.
277 ubik_ClientDestroy(struct ubik_client * aclient)
283 LOCK_UBIK_CLIENT(aclient);
284 for (c = 0; c < MAXSERVERS; c++) {
285 struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
288 #ifdef AFS_PTHREAD_ENV
289 rx_ReleaseCachedConnection(rxConn);
291 rx_DestroyConnection(rxConn);
294 aclient->initializationState = 0; /* client in not initialized */
295 UNLOCK_UBIK_CLIENT(aclient);
296 #ifdef AFS_PTHREAD_ENV
297 pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
304 * RefreshConn -- So that intermittent failures that cause connections to die
305 * don't kill whole ubik connection, refresh them when the connection is in
309 struct rx_connection *
310 ubik_RefreshConn(struct rx_connection *tc)
315 struct rx_securityClass *sc;
317 struct rx_connection *newTc;
319 host = rx_HostOf(rx_PeerOf(tc));
320 port = rx_PortOf(rx_PeerOf(tc));
321 service = rx_ServiceIdOf(tc);
322 sc = rx_SecurityObjectOf(tc);
323 si = rx_SecurityClassOf(tc);
326 * destroy old one after creating new one so that refCount on security
327 * object cannot reach zero.
329 newTc = rx_NewConnection(host, port, service, sc, si);
330 rx_DestroyConnection(tc);
334 #ifdef AFS_PTHREAD_ENV
336 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
337 pthread_mutex_t ubik_client_mutex;
338 #define LOCK_UCLNT_CACHE \
339 assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
340 pthread_mutex_lock(&ubik_client_mutex)==0)
341 #define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0)
344 ubik_client_init_mutex()
346 assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
351 #define LOCK_UCLNT_CACHE
352 #define UNLOCK_UCLNT_CACHE
357 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
358 static int synccount = 0;
361 * call this instead of stub and we'll guarantee to find a host that's up.
362 * in the future, we should also put in a protocol to find the sync site
365 ubik_Call(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
366 p11, p12, p13, p14, p15, p16)
368 register struct ubik_client *aclient;
387 afs_int32 rcode, code, newHost, thisHost, i, count;
388 int chaseCount, pass, needsync, inlist, j;
389 struct rx_connection *tc;
395 LOCK_UBIK_CLIENT(aclient);
398 origLevel = aclient->initializationState;
400 chaseCount = inlist = needsync = 0;
403 for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
404 if (calls_needsync[j] == (int *)aproc) {
405 inlist = needsync = 1;
411 * First pass, we try all servers that are up.
412 * Second pass, we try all servers.
414 for (pass = 0; pass < 2; pass++) { /*p */
415 /* For each entry in our servers list */
416 for (count = 0;; count++) { /*s */
419 /* Need a sync site. Lets try to quickly find it */
420 if (aclient->syncSite) {
421 newHost = aclient->syncSite; /* already in network order */
422 aclient->syncSite = 0; /* Will reset if it works */
423 } else if (aclient->conns[3]) {
424 /* If there are fewer than four db servers in a cell,
425 * there's no point in making the GetSyncSite call.
426 * At best, it's a wash. At worst, it results in more
427 * RPCs than you would otherwise make.
429 tc = aclient->conns[count];
430 if (tc && rx_ConnError(tc)) {
431 aclient->conns[count] = tc = ubik_RefreshConn(tc);
435 code = VOTE_GetSyncSite(tc, &newHost);
436 if (aclient->initializationState != origLevel)
437 goto restart; /* somebody did a ubik_ClientInit */
440 newHost = htonl(newHost); /* convert to network order */
445 /* position count at the appropriate slot in the client
446 * structure and retry. If we can't find in slot, we'll
447 * just continue through the whole list
449 for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
450 rxp = rx_PeerOf(aclient->conns[i]);
451 thisHost = rx_HostOf(rxp);
454 if (thisHost == newHost) {
455 if (chaseCount++ > 2)
456 break; /* avoid loop asking */
457 count = i; /* this index is the sync site */
464 tc = aclient->conns[count];
465 if (tc && rx_ConnError(tc)) {
466 aclient->conns[count] = tc = ubik_RefreshConn(tc);
471 if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
472 continue; /* this guy's down */
476 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
477 p12, p13, p14, p15, p16);
478 if (aclient->initializationState != origLevel) {
479 /* somebody did a ubik_ClientInit */
481 goto restart; /* call failed */
483 goto done; /* call suceeded */
485 if (rcode < 0) { /* network errors */
486 aclient->states[count] |= CFLastFailed; /* Mark serer down */
487 } else if (rcode == UNOTSYNC) {
489 } else if (rcode != UNOQUORUM) {
490 /* either misc ubik code, or misc appl code, or success. */
491 aclient->states[count] &= ~CFLastFailed; /* mark server up */
492 goto done; /* all done */
499 if (!inlist) { /* Remember proc call that needs sync site */
501 calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
506 if (!rcode) { /* Remember the sync site - cmd successful */
507 rxp = rx_PeerOf(aclient->conns[count]);
508 aclient->syncSite = rx_HostOf(rxp);
511 UNLOCK_UBIK_CLIENT(aclient);
518 * call this after getting back a UNOTSYNC
519 * note that getting a UNOTSYNC error code back does *not* guarantee
520 * that there is a sync site yet elected. However, if there is a sync
521 * site out there somewhere, and you're trying an operation that
522 * requires a sync site, ubik will return UNOTSYNC, indicating the
523 * operation won't work until you find a sync site
526 try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
531 afs_int32 thisHost, newHost;
532 struct rx_connection *tc;
535 origLevel = aclient->initializationState;
538 tc = aclient->conns[apos];
539 if (tc && rx_ConnError(tc)) {
540 aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
546 /* now see if we can find the sync site host */
547 code = VOTE_GetSyncSite(tc, &newHost);
548 if (aclient->initializationState != origLevel) {
549 return -1; /* somebody did a ubik_ClientInit */
552 if (!code && newHost) {
553 newHost = htonl(newHost); /* convert back to network order */
556 * position count at the appropriate slot in the client
557 * structure and retry. If we can't find in slot, we'll just
558 * continue through the whole list
560 for (i = 0; i < MAXSERVERS; i++) {
561 rxp = rx_PeerOf(aclient->conns[i]);
562 thisHost = rx_HostOf(rxp);
565 } else if (thisHost == newHost) {
566 return i; /* we were told to use this one */
574 * Create an internal version of ubik_CallIter that takes an additional
575 * parameter - to indicate whether the ubik client handle has already
583 CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7, p8, p9,
584 p10, p11, p12, p13, p14, p15, p16, needlock)
586 register struct ubik_client *aclient;
607 register afs_int32 code;
608 struct rx_connection *tc;
612 LOCK_UBIK_CLIENT(aclient);
614 origLevel = aclient->initializationState;
618 while (*apos < MAXSERVERS) {
619 /* tc is the next conn to try */
620 tc = aclient->conns[*apos];
623 UNLOCK_UBIK_CLIENT(aclient);
628 if (rx_ConnError(tc)) {
629 tc = ubik_RefreshConn(tc);
630 aclient->conns[*apos] = tc;
633 if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
634 (*apos)++; /* try another one if this server is down */
636 break; /* this is the desired path */
639 if (*apos >= MAXSERVERS) {
641 UNLOCK_UBIK_CLIENT(aclient);
647 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
649 if (aclient->initializationState != origLevel) {
651 UNLOCK_UBIK_CLIENT(aclient);
653 return code; /* somebody did a ubik_ClientInit */
656 /* what should I do in case of UNOQUORUM ? */
658 aclient->states[*apos] |= CFLastFailed; /* network errors */
660 /* either misc ubik code, or misc application code or success. */
661 aclient->states[*apos] &= ~CFLastFailed; /* operation worked */
666 UNLOCK_UBIK_CLIENT(aclient);
672 * call this instead of stub and we'll guarantee to find a host that's up.
673 * in the future, we should also put in a protocol to find the sync site
676 ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
677 p11, p12, p13, p14, p15, p16)
679 register struct ubik_client *aclient;
698 afs_int32 code, rcode;
705 LOCK_UBIK_CLIENT(aclient);
708 origLevel = aclient->initializationState;
710 /* Do two passes. First pass only checks servers known running */
711 for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
712 pass++, aflags &= ~UPUBIKONLY) {
717 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
718 p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
720 if (code && (aclient->initializationState != origLevel)) {
723 if (code == UNOSERVERS) {
726 rcode = code; /* remember code from last good call */
728 if (code == UNOTSYNC) { /* means this requires a sync site */
729 if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
730 temp = try_GetSyncSite(aclient, count);
731 if (aclient->initializationState != origLevel) {
732 goto restart; /* somebody did a ubik_ClientInit */
734 if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
735 count = temp; /* generally try to make progress */
738 } else if ((code >= 0) && (code != UNOQUORUM)) {
739 UNLOCK_UBIK_CLIENT(aclient);
740 return code; /* success or global error condition */
744 UNLOCK_UBIK_CLIENT(aclient);
749 * This is part of an iterator. It doesn't handle finding sync sites
752 ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
753 afs_int32 aflags, int *apos, long p1, long p2,
754 long p3, long p4, long p5, long p6, long p7,
755 long p8, long p9, long p10, long p11, long p12,
756 long p13, long p14, long p15, long p16)
758 return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
759 p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);