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>
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
25 #include "afs/rxgen_consts.h"
26 #define UBIK_LEGACY_CALLITER 1
28 #include "afs/pthread_glock.h"
30 #else /* defined(UKERNEL) */
32 #include <afs/pthread_glock.h>
43 #include <netinet/in.h>
45 #include <afs/rxgen_consts.h>
47 #endif /* defined(UKERNEL) */
50 short ubik_initializationState; /*!< initial state is zero */
54 * \brief 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 #if !defined(UKERNEL)
122 * \brief use time and pid to try to get some initial randomness.
124 #define ranstage(x) (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
127 * \brief Random number generator and constants from KnuthV2 2d ed, p170
130 * X = (aX + c) % m \n
131 * m is a power of two \n
133 * a is 0.73m should be 0.01m .. 0.99m \n
134 * c is more or less immaterial. 1 or a is suggested. \n
136 * NB: LOW ORDER BITS are not very random. To get small random numbers,
137 * treat result as <1, with implied binary point, and multiply by
140 * NB: Has to be unsigned, since shifts on signed quantities may preserve
143 * In this case, m == 2^32, the mod operation is implicit. a == pi, which
144 * is used because it has some interesting characteristics (lacks any
145 * interesting bit-patterns).
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 * \brief 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...
179 * \todo Slight roundoff error exists, an excercise for the reader.
181 * Need to multiply by something with lots of ones in it, so multiply by
182 * 8 or 16 is right out.
185 afs_randomMod15(void)
189 temp = afs_random() >> 4;
190 temp = (temp * 15) >> 28;
194 #endif /* !defined(UKERNEL) */
199 #define abs(a) ((a) < 0 ? -1*(a) : (a))
201 ubik_ClientInit(register struct rx_connection **serverconns,
202 struct ubik_client **aclient)
207 register struct ubik_client *tc;
209 initialize_U_error_table();
211 if (*aclient) { /* the application is doing a re-initialization */
212 LOCK_UBIK_CLIENT((*aclient));
213 /* this is an important defensive check */
214 if (!((*aclient)->initializationState)) {
215 UNLOCK_UBIK_CLIENT((*aclient));
216 return UREINITIALIZE;
219 /* release all existing connections */
220 for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
221 struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
224 #ifdef AFS_PTHREAD_ENV
225 rx_ReleaseCachedConnection(rxConn);
227 rx_DestroyConnection(rxConn);
230 UNLOCK_UBIK_CLIENT((*aclient));
231 #ifdef AFS_PTHREAD_ENV
232 if (pthread_mutex_destroy(&((*aclient)->cm)))
233 return UMUTEXDESTROY;
236 tc = (struct ubik_client *)malloc(sizeof(struct ubik_client));
240 memset((void *)tc, 0, sizeof(*tc));
241 #ifdef AFS_PTHREAD_ENV
242 if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
246 tc->initializationState = ++ubik_initializationState;
248 /* first count the # of server conns so we can randomize properly */
250 for (i = 0; i < MAXSERVERS; i++) {
251 if (serverconns[i] == (struct rx_connection *)0)
256 /* here count is the # of servers we're actually passed in. Compute
257 * offset, a number between 0..count-1, where we'll start copying from the
258 * client-provided array. */
259 for (i = 0; i < count; i++) {
260 offset = afs_randomMod15() % count;
261 for (j = abs(offset); j < 2 * count; j++) {
262 if (!tc->conns[abs(j % count)]) {
263 tc->conns[abs(j % count)] = serverconns[i];
274 * \brief Destroy an ubik connection.
276 * It calls rx to destroy the component rx connections, then frees the ubik
277 * connection structure.
280 ubik_ClientDestroy(struct ubik_client * aclient)
286 LOCK_UBIK_CLIENT(aclient);
287 for (c = 0; c < MAXSERVERS; c++) {
288 struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
291 #ifdef AFS_PTHREAD_ENV
292 rx_ReleaseCachedConnection(rxConn);
294 rx_DestroyConnection(rxConn);
297 aclient->initializationState = 0; /* client in not initialized */
298 UNLOCK_UBIK_CLIENT(aclient);
299 #ifdef AFS_PTHREAD_ENV
300 pthread_mutex_destroy(&(aclient->cm)); /* ignore failure */
307 * \brief So that intermittent failures that cause connections to die
308 * don't kill whole ubik connection, refresh them when the connection is in
311 struct rx_connection *
312 ubik_RefreshConn(struct rx_connection *tc)
317 struct rx_securityClass *sc;
319 struct rx_connection *newTc;
321 host = rx_HostOf(rx_PeerOf(tc));
322 port = rx_PortOf(rx_PeerOf(tc));
323 service = rx_ServiceIdOf(tc);
324 sc = rx_SecurityObjectOf(tc);
325 si = rx_SecurityClassOf(tc);
328 * destroy old one after creating new one so that refCount on security
329 * object cannot reach zero.
331 newTc = rx_NewConnection(host, port, service, sc, si);
332 rx_DestroyConnection(tc);
336 #ifdef AFS_PTHREAD_ENV
338 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
339 pthread_mutex_t ubik_client_mutex;
340 #define LOCK_UCLNT_CACHE \
341 assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0 && \
342 pthread_mutex_lock(&ubik_client_mutex)==0)
343 #define UNLOCK_UCLNT_CACHE assert(pthread_mutex_unlock(&ubik_client_mutex)==0)
346 ubik_client_init_mutex(void)
348 assert(pthread_mutex_init(&ubik_client_mutex, NULL) == 0);
353 #define LOCK_UCLNT_CACHE
354 #define UNLOCK_UCLNT_CACHE
359 static int *calls_needsync[SYNCCOUNT]; /* proc calls that need the sync site */
360 static int synccount = 0;
363 * call this instead of stub and we'll guarantee to find a host that's up.
365 * \todo In the future, we should also put in a protocol to find the sync site.
368 ubik_Call(int (*aproc) (), register struct ubik_client *aclient,
369 afs_int32 aflags, long p1, long p2, long p3, long p4,
370 long p5, long p6, long p7, long p8, long p9, long p10,
371 long p11, long p12, long p13, long p14, long p15, long p16)
373 afs_int32 rcode, code, newHost, thisHost, i, count;
374 int chaseCount, pass, needsync, inlist, j;
375 struct rx_connection *tc;
381 LOCK_UBIK_CLIENT(aclient);
384 origLevel = aclient->initializationState;
386 chaseCount = inlist = needsync = 0;
389 for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
390 if (calls_needsync[j] == (int *)aproc) {
391 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 = ubik_RefreshConn(tc);
421 code = VOTE_GetSyncSite(tc, &newHost);
422 if (aclient->initializationState != origLevel)
423 goto restart; /* somebody did a ubik_ClientInit */
426 newHost = htonl(newHost); /* convert to network order */
431 /* position count at the appropriate slot in the client
432 * structure and retry. If we can't find in slot, we'll
433 * just continue through the whole list
435 for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
436 rxp = rx_PeerOf(aclient->conns[i]);
437 thisHost = rx_HostOf(rxp);
440 if (thisHost == newHost) {
441 if (chaseCount++ > 2)
442 break; /* avoid loop asking */
443 count = i; /* this index is the sync site */
450 tc = aclient->conns[count];
451 if (tc && rx_ConnError(tc)) {
452 aclient->conns[count] = tc = ubik_RefreshConn(tc);
457 if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
458 continue; /* this guy's down */
462 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
463 p12, p13, p14, p15, p16);
464 if (aclient->initializationState != origLevel) {
465 /* somebody did a ubik_ClientInit */
467 goto restart; /* call failed */
469 goto done; /* call suceeded */
471 if (rcode < 0) { /* network errors */
472 aclient->states[count] |= CFLastFailed; /* Mark serer down */
473 } else if (rcode == UNOTSYNC) {
475 } else if (rcode != UNOQUORUM) {
476 /* either misc ubik code, or misc appl code, or success. */
477 aclient->states[count] &= ~CFLastFailed; /* mark server up */
478 goto done; /* all done */
485 if (!inlist) { /* Remember proc call that needs sync site */
487 calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
492 if (!rcode) { /* Remember the sync site - cmd successful */
493 rxp = rx_PeerOf(aclient->conns[count]);
494 aclient->syncSite = rx_HostOf(rxp);
497 UNLOCK_UBIK_CLIENT(aclient);
504 * \brief Call this after getting back a #UNOTSYNC.
506 * \note Getting a #UNOTSYNC error code back does \b not guarantee
507 * that there is a sync site yet elected. However, if there is a sync
508 * site out there somewhere, and you're trying an operation that
509 * requires a sync site, ubik will return #UNOTSYNC, indicating the
510 * operation won't work until you find a sync site
513 try_GetSyncSite(register struct ubik_client *aclient, afs_int32 apos)
518 afs_int32 thisHost, newHost;
519 struct rx_connection *tc;
522 origLevel = aclient->initializationState;
525 tc = aclient->conns[apos];
526 if (tc && rx_ConnError(tc)) {
527 aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
533 /* now see if we can find the sync site host */
534 code = VOTE_GetSyncSite(tc, &newHost);
535 if (aclient->initializationState != origLevel) {
536 return -1; /* somebody did a ubik_ClientInit */
539 if (!code && newHost) {
540 newHost = htonl(newHost); /* convert back to network order */
543 * position count at the appropriate slot in the client
544 * structure and retry. If we can't find in slot, we'll just
545 * continue through the whole list
547 for (i = 0; i < MAXSERVERS; i++) {
548 rxp = rx_PeerOf(aclient->conns[i]);
549 thisHost = rx_HostOf(rxp);
552 } else if (thisHost == newHost) {
553 return i; /* we were told to use this one */
564 * \brief Create an internal version of ubik_CallIter that takes an additional
565 * parameter - to indicate whether the ubik client handle has already
569 CallIter(int (*aproc) (), register struct ubik_client *aclient,
570 afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4,
571 long p5, long p6, long p7, long p8, long p9, long p10, long p11,
572 long p12, long p13, long p14, long p15, long p16, int needlock)
574 register afs_int32 code;
575 struct rx_connection *tc;
579 LOCK_UBIK_CLIENT(aclient);
581 origLevel = aclient->initializationState;
585 while (*apos < MAXSERVERS) {
586 /* tc is the next conn to try */
587 tc = aclient->conns[*apos];
590 UNLOCK_UBIK_CLIENT(aclient);
595 if (rx_ConnError(tc)) {
596 tc = ubik_RefreshConn(tc);
597 aclient->conns[*apos] = tc;
600 if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
601 (*apos)++; /* try another one if this server is down */
603 break; /* this is the desired path */
606 if (*apos >= MAXSERVERS) {
608 UNLOCK_UBIK_CLIENT(aclient);
614 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
616 if (aclient->initializationState != origLevel) {
618 UNLOCK_UBIK_CLIENT(aclient);
620 return code; /* somebody did a ubik_ClientInit */
623 /* what should I do in case of UNOQUORUM ? */
625 aclient->states[*apos] |= CFLastFailed; /* network errors */
627 /* either misc ubik code, or misc application code or success. */
628 aclient->states[*apos] &= ~CFLastFailed; /* operation worked */
633 UNLOCK_UBIK_CLIENT(aclient);
639 * \brief Call this instead of stub and we'll guarantee to find a host that's up.
641 * \todo In the future, we should also put in a protocol to find the sync site.
644 ubik_Call_New(int (*aproc) (), register struct ubik_client *aclient,
645 afs_int32 aflags, long p1, long p2, long p3, long p4, long p5,
646 long p6, long p7, long p8, long p9, long p10, long p11,
647 long p12, long p13, long p14, long p15, long p16)
649 afs_int32 code, rcode;
656 LOCK_UBIK_CLIENT(aclient);
659 origLevel = aclient->initializationState;
661 /* Do two passes. First pass only checks servers known running */
662 for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
663 pass++, aflags &= ~UPUBIKONLY) {
668 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
669 p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
671 if (code && (aclient->initializationState != origLevel)) {
674 if (code == UNOSERVERS) {
677 rcode = code; /* remember code from last good call */
679 if (code == UNOTSYNC) { /* means this requires a sync site */
680 if (aclient->conns[3]) { /* don't bother unless 4 or more srv */
681 temp = try_GetSyncSite(aclient, count);
682 if (aclient->initializationState != origLevel) {
683 goto restart; /* somebody did a ubik_ClientInit */
685 if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
686 count = temp; /* generally try to make progress */
689 } else if ((code >= 0) && (code != UNOQUORUM)) {
690 UNLOCK_UBIK_CLIENT(aclient);
691 return code; /* success or global error condition */
695 UNLOCK_UBIK_CLIENT(aclient);
700 * \brief This is part of an iterator. It doesn't handle finding sync sites.
703 ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
704 afs_int32 aflags, int *apos, long p1, long p2,
705 long p3, long p4, long p5, long p6, long p7,
706 long p8, long p9, long p10, long p11, long p12,
707 long p13, long p14, long p15, long p16)
709 return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
710 p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);