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>
17 #ifdef IGNORE_SOME_GCC_WARNINGS
18 # pragma GCC diagnostic warning "-Wstrict-prototypes"
22 #include "afs/sysincludes.h"
23 #include "afsincludes.h"
28 #include "afs/rxgen_consts.h"
29 #define UBIK_LEGACY_CALLITER 1
31 #include "afs/pthread_glock.h"
33 #else /* defined(UKERNEL) */
35 #include <afs/pthread_glock.h>
46 #include <netinet/in.h>
48 #include <afs/rxgen_consts.h>
50 #endif /* defined(UKERNEL) */
53 short ubik_initializationState; /*!< initial state is zero */
57 * \brief Parse list for clients.
60 ubik_ParseClientList(int argc, char **argv, afs_int32 * aothers)
64 register struct hostent *th;
65 afs_int32 temp, counter;
68 inServer = 0; /* haven't seen -servers yet */
70 for (i = 1; i < argc; i++) {
71 /* look for -servers argument */
77 /* otherwise this is a new host name */
79 th = gethostbyname(tp);
84 memmove((void *)&temp, (const void *)th->h_addr,
87 if (counter++ >= MAXSERVERS)
91 /* haven't seen a -server yet */
92 if (!strcmp(tp, "-servers")) {
98 /* never saw a -server */
101 if (counter < MAXSERVERS)
102 *aothers++ = 0; /* null terminate if room */
106 #ifdef AFS_PTHREAD_ENV
110 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
111 static int called_afs_random_once;
112 static pthread_key_t random_number_key;
115 afs_random_once(void)
117 assert(pthread_key_create(&random_number_key, NULL) == 0);
118 called_afs_random_once = 1;
123 #if !defined(UKERNEL)
125 * \brief use time and pid to try to get some initial randomness.
127 #define ranstage(x) (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
130 * \brief Random number generator and constants from KnuthV2 2d ed, p170
133 * X = (aX + c) % m \n
134 * m is a power of two \n
136 * a is 0.73m should be 0.01m .. 0.99m \n
137 * c is more or less immaterial. 1 or a is suggested. \n
139 * NB: LOW ORDER BITS are not very random. To get small random numbers,
140 * treat result as <1, with implied binary point, and multiply by
143 * NB: Has to be unsigned, since shifts on signed quantities may preserve
146 * In this case, m == 2^32, the mod operation is implicit. a == pi, which
147 * is used because it has some interesting characteristics (lacks any
148 * interesting bit-patterns).
153 #ifdef AFS_PTHREAD_ENV
156 if (!called_afs_random_once)
157 pthread_once(&random_once, afs_random_once);
159 state = (uintptr_t) pthread_getspecific(random_number_key);
161 static afs_uint32 state = 0;
166 state = time(0) + getpid();
167 for (i = 0; i < 15; i++) {
173 #ifdef AFS_PTHREAD_ENV
174 pthread_setspecific(random_number_key, (const void *)(uintptr_t)state);
181 * \brief Returns int 0..14 using the high bits of a pseudo-random number instead of
182 * the low bits, as the low bits are "less random" than the high ones...
184 * \todo Slight roundoff error exists, an excercise for the reader.
186 * Need to multiply by something with lots of ones in it, so multiply by
187 * 8 or 16 is right out.
190 afs_randomMod15(void)
194 temp = afs_random() >> 4;
195 temp = (temp * 15) >> 28;
199 #endif /* !defined(UKERNEL) */
204 #define abs(a) ((a) < 0 ? -1*(a) : (a))
206 ubik_ClientInit(register struct rx_connection **serverconns,
207 struct ubik_client **aclient)
212 register struct ubik_client *tc;
214 initialize_U_error_table();
216 if (*aclient) { /* the application is doing a re-initialization */
217 LOCK_UBIK_CLIENT((*aclient));
218 /* this is an important defensive check */
219 if (!((*aclient)->initializationState)) {
220 UNLOCK_UBIK_CLIENT((*aclient));
221 return UREINITIALIZE;
224 /* release all existing connections */
225 for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
226 struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
229 #ifdef AFS_PTHREAD_ENV
230 rx_ReleaseCachedConnection(rxConn);
232 rx_DestroyConnection(rxConn);
235 UNLOCK_UBIK_CLIENT((*aclient));
236 #ifdef AFS_PTHREAD_ENV
237 if (pthread_mutex_destroy(&((*aclient)->cm)))
238 return UMUTEXDESTROY;
241 tc = (struct ubik_client *)malloc(sizeof(struct ubik_client));
245 memset((void *)tc, 0, sizeof(*tc));
246 #ifdef AFS_PTHREAD_ENV
247 if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
251 tc->initializationState = ++ubik_initializationState;
253 /* first count the # of server conns so we can randomize properly */
255 for (i = 0; i < MAXSERVERS; i++) {
256 if (serverconns[i] == (struct rx_connection *)0)
261 /* here count is the # of servers we're actually passed in. Compute
262 * offset, a number between 0..count-1, where we'll start copying from the
263 * client-provided array. */
264 for (i = 0; i < count; i++) {
265 offset = afs_randomMod15() % count;
266 for (j = abs(offset); j < 2 * count; j++) {
267 if (!tc->conns[abs(j % count)]) {
268 tc->conns[abs(j % count)] = serverconns[i];
279 * \brief Destroy an ubik connection.
281 * It calls rx to destroy the component rx connections, then frees the ubik
282 * connection structure.
285 ubik_ClientDestroy(struct ubik_client * aclient)
291 LOCK_UBIK_CLIENT(aclient);
292 for (c = 0; c < MAXSERVERS; c++) {
293 struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
296 #ifdef AFS_PTHREAD_ENV
297 rx_ReleaseCachedConnection(rxConn);
299 rx_DestroyConnection(rxConn);
302 aclient->initializationState = 0; /* client in not initialized */
303 UNLOCK_UBIK_CLIENT(aclient);
304 #ifdef AFS_PTHREAD_ENV
305 pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
312 * \brief So that intermittent failures that cause connections to die
313 * don't kill whole ubik connection, refresh them when the connection is in
316 struct rx_connection *
317 ubik_RefreshConn(struct rx_connection *tc)
322 struct rx_securityClass *sc;
324 struct rx_connection *newTc;
326 host = rx_HostOf(rx_PeerOf(tc));
327 port = rx_PortOf(rx_PeerOf(tc));
328 service = rx_ServiceIdOf(tc);
329 sc = rx_SecurityObjectOf(tc);
330 si = rx_SecurityClassOf(tc);
333 * destroy old one after creating new one so that refCount on security
334 * object cannot reach zero.
336 newTc = rx_NewConnection(host, port, service, sc, si);
337 rx_DestroyConnection(tc);
341 #ifdef AFS_PTHREAD_ENV
343 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
344 pthread_mutex_t ubik_client_mutex;
345 #define LOCK_UCLNT_CACHE \
346 assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
347 pthread_mutex_lock(&ubik_client_mutex)==0)
348 #define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0)
351 ubik_client_init_mutex(void)
353 assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
358 #define LOCK_UCLNT_CACHE
359 #define UNLOCK_UCLNT_CACHE
364 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
365 static int synccount = 0;
370 * \brief Call this after getting back a #UNOTSYNC.
372 * \note Getting a #UNOTSYNC error code back does \b not guarantee
373 * that there is a sync site yet elected. However, if there is a sync
374 * site out there somewhere, and you're trying an operation that
375 * requires a sync site, ubik will return #UNOTSYNC, indicating the
376 * operation won't work until you find a sync site
379 try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
384 afs_int32 thisHost, newHost;
385 struct rx_connection *tc;
388 origLevel = aclient->initializationState;
391 tc = aclient->conns[apos];
392 if (tc && rx_ConnError(tc)) {
393 aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
399 /* now see if we can find the sync site host */
400 code = VOTE_GetSyncSite(tc, &newHost);
401 if (aclient->initializationState != origLevel) {
402 return -1; /* somebody did a ubik_ClientInit */
405 if (!code && newHost) {
406 newHost = htonl(newHost); /* convert back to network order */
409 * position count at the appropriate slot in the client
410 * structure and retry. If we can't find in slot, we'll just
411 * continue through the whole list
413 for (i = 0; i < MAXSERVERS; i++) {
414 rxp = rx_PeerOf(aclient->conns[i]);
415 thisHost = rx_HostOf(rxp);
418 } else if (thisHost == newHost) {
419 return i; /* we were told to use this one */
430 * \brief Create an internal version of ubik_CallIter that takes an additional
431 * parameter - to indicate whether the ubik client handle has already
435 CallIter(int (*aproc) (), register struct ubik_client *aclient,
436 afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4,
437 long p5, long p6, long p7, long p8, long p9, long p10, long p11,
438 long p12, long p13, long p14, long p15, long p16, int needlock)
440 register afs_int32 code;
441 struct rx_connection *tc;
445 LOCK_UBIK_CLIENT(aclient);
447 origLevel = aclient->initializationState;
451 while (*apos < MAXSERVERS) {
452 /* tc is the next conn to try */
453 tc = aclient->conns[*apos];
456 UNLOCK_UBIK_CLIENT(aclient);
461 if (rx_ConnError(tc)) {
462 tc = ubik_RefreshConn(tc);
463 aclient->conns[*apos] = tc;
466 if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
467 (*apos)++; /* try another one if this server is down */
469 break; /* this is the desired path */
472 if (*apos >= MAXSERVERS) {
474 UNLOCK_UBIK_CLIENT(aclient);
480 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
482 if (aclient->initializationState != origLevel) {
484 UNLOCK_UBIK_CLIENT(aclient);
486 return code; /* somebody did a ubik_ClientInit */
489 /* what should I do in case of UNOQUORUM ? */
491 aclient->states[*apos] |= CFLastFailed; /* network errors */
493 /* either misc ubik code, or misc application code or success. */
494 aclient->states[*apos] &= ~CFLastFailed; /* operation worked */
499 UNLOCK_UBIK_CLIENT(aclient);
505 * \brief This is part of an iterator. It doesn't handle finding sync sites.
508 ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
509 afs_int32 aflags, int *apos, long p1, long p2,
510 long p3, long p4, long p5, long p6, long p7,
511 long p8, long p9, long p10, long p11, long p12,
512 long p13, long p14, long p15, long p16)
514 return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
515 p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);
519 * \brief Call this instead of stub and we'll guarantee to find a host that's up.
521 * \todo In the future, we should also put in a protocol to find the sync site.
524 ubik_Call_New(int (*aproc) (), register struct ubik_client *aclient,
525 afs_int32 aflags, long p1, long p2, long p3, long p4, long p5,
526 long p6, long p7, long p8, long p9, long p10, long p11,
527 long p12, long p13, long p14, long p15, long p16)
529 afs_int32 code, rcode;
536 LOCK_UBIK_CLIENT(aclient);
539 origLevel = aclient->initializationState;
541 /* Do two passes. First pass only checks servers known running */
542 for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
543 pass++, aflags &= ~UPUBIKONLY) {
548 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
549 p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
551 if (code && (aclient->initializationState != origLevel)) {
554 if (code == UNOSERVERS) {
557 rcode = code; /* remember code from last good call */
559 if (code == UNOTSYNC) { /* means this requires a sync site */
560 if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
561 temp = try_GetSyncSite(aclient, count);
562 if (aclient->initializationState != origLevel) {
563 goto restart; /* somebody did a ubik_ClientInit */
565 if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
566 count = temp; /* generally try to make progress */
569 } else if ((code >= 0) && (code != UNOQUORUM)) {
570 UNLOCK_UBIK_CLIENT(aclient);
571 return code; /* success or global error condition */
575 UNLOCK_UBIK_CLIENT(aclient);
580 * call this instead of stub and we'll guarantee to find a host that's up.
582 * \todo In the future, we should also put in a protocol to find the sync site.
585 ubik_Call(int (*aproc) (), register struct ubik_client *aclient,
586 afs_int32 aflags, long p1, long p2, long p3, long p4,
587 long p5, long p6, long p7, long p8, long p9, long p10,
588 long p11, long p12, long p13, long p14, long p15, long p16)
590 afs_int32 rcode, code, newHost, thisHost, i, count;
591 int chaseCount, pass, needsync, inlist, j;
592 struct rx_connection *tc;
596 if (aflags & UBIK_CALL_NEW)
597 return ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4,
598 p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
603 LOCK_UBIK_CLIENT(aclient);
606 origLevel = aclient->initializationState;
608 chaseCount = inlist = needsync = 0;
611 for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
612 if (calls_needsync[j] == (int *)aproc) {
613 inlist = needsync = 1;
619 * First pass, we try all servers that are up.
620 * Second pass, we try all servers.
622 for (pass = 0; pass < 2; pass++) { /*p */
623 /* For each entry in our servers list */
624 for (count = 0;; count++) { /*s */
627 /* Need a sync site. Lets try to quickly find it */
628 if (aclient->syncSite) {
629 newHost = aclient->syncSite; /* already in network order */
630 aclient->syncSite = 0; /* Will reset if it works */
631 } else if (aclient->conns[3]) {
632 /* If there are fewer than four db servers in a cell,
633 * there's no point in making the GetSyncSite call.
634 * At best, it's a wash. At worst, it results in more
635 * RPCs than you would otherwise make.
637 tc = aclient->conns[count];
638 if (tc && rx_ConnError(tc)) {
639 aclient->conns[count] = tc = ubik_RefreshConn(tc);
643 code = VOTE_GetSyncSite(tc, &newHost);
644 if (aclient->initializationState != origLevel)
645 goto restart; /* somebody did a ubik_ClientInit */
648 newHost = htonl(newHost); /* convert to network order */
653 /* position count at the appropriate slot in the client
654 * structure and retry. If we can't find in slot, we'll
655 * just continue through the whole list
657 for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
658 rxp = rx_PeerOf(aclient->conns[i]);
659 thisHost = rx_HostOf(rxp);
662 if (thisHost == newHost) {
663 if (chaseCount++ > 2)
664 break; /* avoid loop asking */
665 count = i; /* this index is the sync site */
672 tc = aclient->conns[count];
673 if (tc && rx_ConnError(tc)) {
674 aclient->conns[count] = tc = ubik_RefreshConn(tc);
679 if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
680 continue; /* this guy's down */
684 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
685 p12, p13, p14, p15, p16);
686 if (aclient->initializationState != origLevel) {
687 /* somebody did a ubik_ClientInit */
689 goto restart; /* call failed */
691 goto done; /* call suceeded */
693 if (rcode < 0) { /* network errors */
694 aclient->states[count] |= CFLastFailed; /* Mark serer down */
695 } else if (rcode == UNOTSYNC) {
697 } else if (rcode != UNOQUORUM) {
698 /* either misc ubik code, or misc appl code, or success. */
699 aclient->states[count] &= ~CFLastFailed; /* mark server up */
700 goto done; /* all done */
707 if (!inlist) { /* Remember proc call that needs sync site */
709 calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
714 if (!rcode) { /* Remember the sync site - cmd successful */
715 rxp = rx_PeerOf(aclient->conns[count]);
716 aclient->syncSite = rx_HostOf(rxp);
719 UNLOCK_UBIK_CLIENT(aclient);