ubik: refactor error exits in internal CallIter()
[openafs.git] / src / ubik / ubikclient.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #ifdef IGNORE_SOME_GCC_WARNINGS
17 # pragma GCC diagnostic warning "-Wstrict-prototypes"
18 #endif
19
20 #ifdef UKERNEL
21 #include "afsincludes.h"
22 #endif
23
24 #include <afs/pthread_glock.h>
25 #include <rx/xdr.h>
26 #include <rx/rx.h>
27 #include <lock.h>
28 #include <afs/rxgen_consts.h>
29 #define UBIK_LEGACY_CALLITER
30 #include "ubik.h"
31
32 short ubik_initializationState; /*!< initial state is zero */
33
34
35 /*!
36  * \brief Parse list for clients.
37  */
38 int
39 ubik_ParseClientList(int argc, char **argv, afs_uint32 * aothers)
40 {
41     afs_int32 i;
42     char *tp;
43     struct hostent *th;
44     afs_uint32 temp;
45     afs_int32 counter;
46     int inServer;
47
48     inServer = 0;               /* haven't seen -servers yet */
49     counter = 0;
50     for (i = 1; i < argc; i++) {
51         /* look for -servers argument */
52         tp = argv[i];
53
54         if (inServer) {
55             if (*tp == '-')
56                 break;          /* done */
57             /* otherwise this is a new host name */
58             LOCK_GLOBAL_MUTEX;
59             th = gethostbyname(tp);
60             if (!th) {
61                 UNLOCK_GLOBAL_MUTEX;
62                 return UBADHOST;
63             }
64             memmove((void *)&temp, (const void *)th->h_addr,
65                     sizeof(afs_int32));
66             UNLOCK_GLOBAL_MUTEX;
67             if (counter++ >= MAXSERVERS)
68                 return UNHOSTS;
69             *aothers++ = temp;
70         } else {
71             /* haven't seen a -server yet */
72             if (!strcmp(tp, "-servers")) {
73                 inServer = 1;
74             }
75         }
76     }
77     if (!inServer) {
78         /* never saw a -server */
79         return UNOENT;
80     }
81     if (counter < MAXSERVERS)
82         *aothers++ = 0;         /* null terminate if room */
83     return 0;
84 }
85
86 #ifdef AFS_PTHREAD_ENV
87 #include <pthread.h>
88
89 static pthread_once_t random_once = PTHREAD_ONCE_INIT;
90 static int called_afs_random_once;
91 static pthread_key_t random_number_key;
92
93 static void
94 afs_random_once(void)
95 {
96     osi_Assert(pthread_key_create(&random_number_key, NULL) == 0);
97     called_afs_random_once = 1;
98 }
99
100 #endif
101
102 #if !defined(UKERNEL)
103 /*!
104  * \brief use time and pid to try to get some initial randomness.
105  */
106 #define ranstage(x)     (x)= (afs_uint32) (3141592621U*((afs_uint32)x)+1)
107
108 /*!
109  * \brief Random number generator and constants from KnuthV2 2d ed, p170
110  *
111  * Rules: \n
112  * X = (aX + c) % m \n
113  * m is a power of two \n
114  * a % 8 is 5 \n
115  * a is 0.73m  should be 0.01m .. 0.99m \n
116  * c is more or less immaterial.  1 or a is suggested. \n
117  *
118  * NB:  LOW ORDER BITS are not very random.  To get small random numbers,
119  *      treat result as <1, with implied binary point, and multiply by
120  *      desired modulus.
121  *
122  * NB:  Has to be unsigned, since shifts on signed quantities may preserve
123  *      the sign bit.
124  *
125  * In this case, m == 2^32, the mod operation is implicit. a == pi, which
126  * is used because it has some interesting characteristics (lacks any
127  * interesting bit-patterns).
128  */
129 unsigned int
130 afs_random(void)
131 {
132 #ifdef AFS_PTHREAD_ENV
133     afs_uint32 state;
134
135     if (!called_afs_random_once)
136         pthread_once(&random_once, afs_random_once);
137
138     state = (uintptr_t) pthread_getspecific(random_number_key);
139 #else
140     static afs_uint32 state = 0;
141 #endif
142
143     if (!state) {
144         int i;
145         state = time(0) + getpid();
146         for (i = 0; i < 15; i++) {
147             ranstage(state);
148         }
149     }
150
151     ranstage(state);
152 #ifdef AFS_PTHREAD_ENV
153     pthread_setspecific(random_number_key, (const void *)(uintptr_t)state);
154 #endif
155     return (state);
156
157 }
158
159 /*!
160  * \brief Returns int 0..14 using the high bits of a pseudo-random number instead of
161  * the low bits, as the low bits are "less random" than the high ones...
162  *
163  * \todo Slight roundoff error exists, an excercise for the reader.
164  *
165  * Need to multiply by something with lots of ones in it, so multiply by
166  * 8 or 16 is right out.
167  */
168 static unsigned int
169 afs_randomMod15(void)
170 {
171     afs_uint32 temp;
172
173     temp = afs_random() >> 4;
174     temp = (temp * 15) >> 28;
175
176     return temp;
177 }
178 #endif /* !defined(UKERNEL) */
179
180 #ifdef abs
181 #undef abs
182 #endif /* abs */
183 #define abs(a) ((a) < 0 ? -1*(a) : (a))
184 int
185 ubik_ClientInit(struct rx_connection **serverconns,
186                 struct ubik_client **aclient)
187 {
188     int i, j;
189     int count;
190     int offset;
191     struct ubik_client *tc;
192
193     initialize_U_error_table();
194
195     if (*aclient) {             /* the application is doing a re-initialization */
196         LOCK_UBIK_CLIENT((*aclient));
197         /* this is an important defensive check */
198         if (!((*aclient)->initializationState)) {
199             UNLOCK_UBIK_CLIENT((*aclient));
200             return UREINITIALIZE;
201         }
202
203         /* release all existing connections */
204         for (tc = *aclient, i = 0; i < MAXSERVERS; i++) {
205             struct rx_connection *rxConn = ubik_GetRPCConn(tc, i);
206             if (rxConn == 0)
207                 break;
208 #ifdef AFS_PTHREAD_ENV
209             rx_ReleaseCachedConnection(rxConn);
210 #else
211             rx_DestroyConnection(rxConn);
212 #endif
213         }
214         UNLOCK_UBIK_CLIENT((*aclient));
215 #ifdef AFS_PTHREAD_ENV
216         if (pthread_mutex_destroy(&((*aclient)->cm)))
217             return UMUTEXDESTROY;
218 #endif
219     } else {
220         tc = malloc(sizeof(struct ubik_client));
221     }
222     if (tc == NULL)
223         return UNOMEM;
224     memset((void *)tc, 0, sizeof(*tc));
225 #ifdef AFS_PTHREAD_ENV
226     if (pthread_mutex_init(&(tc->cm), (const pthread_mutexattr_t *)0)) {
227         return UMUTEXINIT;
228     }
229 #endif
230     tc->initializationState = ++ubik_initializationState;
231
232     /* first count the # of server conns so we can randomize properly */
233     count = 0;
234     for (i = 0; i < MAXSERVERS; i++) {
235         if (serverconns[i] == (struct rx_connection *)0)
236             break;
237         count++;
238     }
239
240     /* here count is the # of servers we're actually passed in.  Compute
241      * offset, a number between 0..count-1, where we'll start copying from the
242      * client-provided array. */
243     for (i = 0; i < count; i++) {
244         offset = afs_randomMod15() % count;
245         for (j = abs(offset); j < 2 * count; j++) {
246             if (!tc->conns[abs(j % count)]) {
247                 tc->conns[abs(j % count)] = serverconns[i];
248                 break;
249             }
250         }
251     }
252
253     *aclient = tc;
254     return 0;
255 }
256
257 /*!
258  * \brief Destroy an ubik connection.
259  *
260  * It calls rx to destroy the component rx connections, then frees the ubik
261  * connection structure.
262  */
263 afs_int32
264 ubik_ClientDestroy(struct ubik_client * aclient)
265 {
266     int c;
267
268     if (aclient == 0)
269         return 0;
270     LOCK_UBIK_CLIENT(aclient);
271     for (c = 0; c < MAXSERVERS; c++) {
272         struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
273         if (rxConn == 0)
274             break;
275 #ifdef AFS_PTHREAD_ENV
276         rx_ReleaseCachedConnection(rxConn);
277 #else
278         rx_DestroyConnection(rxConn);
279 #endif
280     }
281     aclient->initializationState = 0;   /* client in not initialized */
282     UNLOCK_UBIK_CLIENT(aclient);
283 #ifdef AFS_PTHREAD_ENV
284     pthread_mutex_destroy(&(aclient->cm));      /* ignore failure */
285 #endif
286     free(aclient);
287     return 0;
288 }
289
290 /*!
291  * \brief So that intermittent failures that cause connections to die
292  *     don't kill whole ubik connection, refresh them when the connection is in
293  *     error.
294  */
295 struct rx_connection *
296 ubik_RefreshConn(struct rx_connection *tc)
297 {
298     afs_uint32 host;
299     u_short port;
300     u_short service;
301     struct rx_securityClass *sc;
302     int si;
303     struct rx_connection *newTc;
304
305     host = rx_HostOf(rx_PeerOf(tc));
306     port = rx_PortOf(rx_PeerOf(tc));
307     service = rx_ServiceIdOf(tc);
308     sc = rx_SecurityObjectOf(tc);
309     si = rx_SecurityClassOf(tc);
310
311     /*
312      * destroy old one after creating new one so that refCount on security
313      * object cannot reach zero.
314      */
315     newTc = rx_NewConnection(host, port, service, sc, si);
316     rx_DestroyConnection(tc);
317     return newTc;
318 }
319
320 #ifdef AFS_PTHREAD_ENV
321
322 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
323 pthread_mutex_t ubik_client_mutex;
324 #define LOCK_UCLNT_CACHE do { \
325     osi_Assert(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0); \
326     MUTEX_ENTER(&ubik_client_mutex); \
327   } while (0)
328 #define UNLOCK_UCLNT_CACHE MUTEX_EXIT(&ubik_client_mutex)
329
330 void
331 ubik_client_init_mutex(void)
332 {
333     MUTEX_INIT(&ubik_client_mutex, "client init", MUTEX_DEFAULT, 0);
334 }
335
336 #else
337
338 #define LOCK_UCLNT_CACHE
339 #define UNLOCK_UCLNT_CACHE
340
341 #endif
342
343 #define SYNCCOUNT 10
344 static int *calls_needsync[SYNCCOUNT];  /* proc calls that need the sync site */
345 static int synccount = 0;
346
347
348
349 /*!
350  * \brief Call this after getting back a #UNOTSYNC.
351  *
352  * \note Getting a #UNOTSYNC error code back does \b not guarantee
353  * that there is a sync site yet elected.  However, if there is a sync
354  * site out there somewhere, and you're trying an operation that
355  * requires a sync site, ubik will return #UNOTSYNC, indicating the
356  * operation won't work until you find a sync site
357  */
358 static int
359 try_GetSyncSite(struct ubik_client *aclient, afs_int32 apos)
360 {
361     struct rx_peer *rxp;
362     afs_int32 code;
363     int i;
364     afs_int32 thisHost, newHost;
365     struct rx_connection *tc;
366     short origLevel;
367
368     origLevel = aclient->initializationState;
369
370     /* get this conn */
371     tc = aclient->conns[apos];
372     if (tc && rx_ConnError(tc)) {
373         aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
374     }
375     if (!tc) {
376         return -1;
377     }
378
379     /* now see if we can find the sync site host */
380     code = VOTE_GetSyncSite(tc, &newHost);
381     if (aclient->initializationState != origLevel) {
382         return -1;              /* somebody did a ubik_ClientInit */
383     }
384
385     if (!code && newHost) {
386         newHost = htonl(newHost);       /* convert back to network order */
387
388         /*
389          * position count at the appropriate slot in the client
390          * structure and retry. If we can't find in slot, we'll just
391          * continue through the whole list
392          */
393         for (i = 0; i < MAXSERVERS; i++) {
394             rxp = rx_PeerOf(aclient->conns[i]);
395             thisHost = rx_HostOf(rxp);
396             if (!thisHost) {
397                 return -1;
398             } else if (thisHost == newHost) {
399                 return i;       /* we were told to use this one */
400             }
401         }
402     }
403     return -1;
404 }
405
406 #define NEED_LOCK 1
407 #define NO_LOCK 0
408
409 /*!
410  * \brief Create an internal version of ubik_CallIter that takes an additional
411  * parameter - to indicate whether the ubik client handle has already
412  * been locked.
413  */
414 static afs_int32
415 CallIter(int (*aproc) (), struct ubik_client *aclient,
416          afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4,
417          long p5, long p6, long p7, long p8, long p9, long p10, long p11,
418          long p12, long p13, long p14, long p15, long p16, int needlock)
419 {
420     afs_int32 code;
421     struct rx_connection *tc;
422     short origLevel;
423
424     if (needlock) {
425         LOCK_UBIK_CLIENT(aclient);
426     }
427     origLevel = aclient->initializationState;
428
429     code = UNOSERVERS;
430
431     while (*apos < MAXSERVERS) {
432         /* tc is the next conn to try */
433         tc = aclient->conns[*apos];
434         if (!tc)
435             goto errout;
436
437         if (rx_ConnError(tc)) {
438             tc = ubik_RefreshConn(tc);
439             aclient->conns[*apos] = tc;
440         }
441
442         if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
443             (*apos)++;          /* try another one if this server is down */
444         } else {
445             break;              /* this is the desired path */
446         }
447     }
448     if (*apos >= MAXSERVERS)
449         goto errout;
450
451     code =
452         (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
453                   p14, p15, p16);
454     if (aclient->initializationState != origLevel) {
455         /* somebody did a ubik_ClientInit */
456         if (code == 0)
457             code = UINTERNAL;   /* no more specific error was returned */
458         goto errout;
459     }
460
461     /* what should I do in case of UNOQUORUM ? */
462     if (code < 0) {
463         aclient->states[*apos] |= CFLastFailed; /* network errors */
464     } else {
465         /* either misc ubik code, or misc application code or success. */
466         aclient->states[*apos] &= ~CFLastFailed;        /* operation worked */
467     }
468
469     (*apos)++;
470 errout:
471     if (needlock) {
472         UNLOCK_UBIK_CLIENT(aclient);
473     }
474     return code;
475 }
476
477 /*!
478  * \brief This is part of an iterator.  It doesn't handle finding sync sites.
479  */
480 afs_int32
481 ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
482                                afs_int32 aflags, int *apos, long p1, long p2,
483                                long p3, long p4, long p5, long p6, long p7,
484                                long p8, long p9, long p10, long p11, long p12,
485                                long p13, long p14, long p15, long p16)
486 {
487     return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
488                     p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);
489 }
490
491 /*!
492  * \brief Call this instead of stub and we'll guarantee to find a host that's up.
493  *
494  * \todo In the future, we should also put in a protocol to find the sync site.
495  */
496 afs_int32
497 ubik_Call_New(int (*aproc) (), struct ubik_client *aclient,
498               afs_int32 aflags, long p1, long p2, long p3, long p4, long p5,
499               long p6, long p7, long p8, long p9, long p10, long p11,
500               long p12, long p13, long p14, long p15, long p16)
501 {
502     afs_int32 code, rcode;
503     afs_int32 count;
504     afs_int32 temp;
505     int pass;
506     int stepBack;
507     short origLevel;
508
509     LOCK_UBIK_CLIENT(aclient);
510   restart:
511     rcode = UNOSERVERS;
512     origLevel = aclient->initializationState;
513
514     /* Do two passes. First pass only checks servers known running */
515     for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
516          pass++, aflags &= ~UPUBIKONLY) {
517         stepBack = 0;
518         count = 0;
519         while (1) {
520             code =
521                 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
522                          p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
523                          NO_LOCK);
524             if (code && (aclient->initializationState != origLevel)) {
525                 goto restart;
526             }
527             if (code == UNOSERVERS) {
528                 break;
529             }
530             rcode = code;       /* remember code from last good call */
531
532             if (code == UNOTSYNC) {     /* means this requires a sync site */
533                 if (aclient->conns[3]) {        /* don't bother unless 4 or more srv */
534                     temp = try_GetSyncSite(aclient, count);
535                     if (aclient->initializationState != origLevel) {
536                         goto restart;   /* somebody did a ubik_ClientInit */
537                     }
538                     if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
539                         count = temp;   /* generally try to make progress */
540                     }
541                 }
542             } else if ((code >= 0) && (code != UNOQUORUM)) {
543                 UNLOCK_UBIK_CLIENT(aclient);
544                 return code;    /* success or global error condition */
545             }
546         }
547     }
548     UNLOCK_UBIK_CLIENT(aclient);
549     return rcode;
550 }
551
552 /*!
553  * call this instead of stub and we'll guarantee to find a host that's up.
554  *
555  * \todo In the future, we should also put in a protocol to find the sync site.
556  */
557 afs_int32
558 ubik_Call(int (*aproc) (), struct ubik_client *aclient,
559           afs_int32 aflags, long p1, long p2, long p3, long p4,
560           long p5, long p6, long p7, long p8, long p9, long p10,
561           long p11, long p12, long p13, long p14, long p15, long p16)
562 {
563     afs_int32 rcode, code, newHost, thisHost, i, count;
564     int chaseCount, pass, needsync, inlist, j;
565     struct rx_connection *tc;
566     struct rx_peer *rxp;
567     short origLevel;
568
569     if (aflags & UBIK_CALL_NEW)
570         return ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4,
571                              p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
572                              p16);
573
574     if (!aclient)
575         return UNOENT;
576     LOCK_UBIK_CLIENT(aclient);
577
578   restart:
579     origLevel = aclient->initializationState;
580     rcode = UNOSERVERS;
581     chaseCount = inlist = needsync = 0;
582
583     LOCK_UCLNT_CACHE;
584     for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
585         if (calls_needsync[j] == (int *)aproc) {
586             inlist = needsync = 1;
587             break;
588         }
589     }
590     UNLOCK_UCLNT_CACHE;
591     /*
592      * First  pass, we try all servers that are up.
593      * Second pass, we try all servers.
594      */
595     for (pass = 0; pass < 2; pass++) {  /*p */
596         /* For each entry in our servers list */
597         for (count = 0;; count++) {     /*s */
598
599             if (needsync) {
600                 /* Need a sync site. Lets try to quickly find it */
601                 if (aclient->syncSite) {
602                     newHost = aclient->syncSite;        /* already in network order */
603                     aclient->syncSite = 0;      /* Will reset if it works */
604                 } else if (aclient->conns[3]) {
605                     /* If there are fewer than four db servers in a cell,
606                      * there's no point in making the GetSyncSite call.
607                      * At best, it's a wash. At worst, it results in more
608                      * RPCs than you would otherwise make.
609                      */
610                     tc = aclient->conns[count];
611                     if (tc && rx_ConnError(tc)) {
612                         aclient->conns[count] = tc = ubik_RefreshConn(tc);
613                     }
614                     if (!tc)
615                         break;
616                     code = VOTE_GetSyncSite(tc, &newHost);
617                     if (aclient->initializationState != origLevel)
618                         goto restart;   /* somebody did a ubik_ClientInit */
619                     if (code)
620                         newHost = 0;
621                     newHost = htonl(newHost);   /* convert to network order */
622                 } else {
623                     newHost = 0;
624                 }
625                 if (newHost) {
626                     /* position count at the appropriate slot in the client
627                      * structure and retry. If we can't find in slot, we'll
628                      * just continue through the whole list
629                      */
630                     for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
631                         rxp = rx_PeerOf(aclient->conns[i]);
632                         thisHost = rx_HostOf(rxp);
633                         if (!thisHost)
634                             break;
635                         if (thisHost == newHost) {
636                             if (chaseCount++ > 2)
637                                 break;  /* avoid loop asking */
638                             count = i;  /* this index is the sync site */
639                             break;
640                         }
641                     }
642                 }
643             }
644             /*needsync */
645             tc = aclient->conns[count];
646             if (tc && rx_ConnError(tc)) {
647                 aclient->conns[count] = tc = ubik_RefreshConn(tc);
648             }
649             if (!tc)
650                 break;
651
652             if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
653                 continue;       /* this guy's down */
654             }
655
656             rcode =
657                 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
658                           p12, p13, p14, p15, p16);
659             if (aclient->initializationState != origLevel) {
660                 /* somebody did a ubik_ClientInit */
661                 if (rcode)
662                     goto restart;       /* call failed */
663                 else
664                     goto done;  /* call suceeded */
665             }
666             if (rcode < 0) {    /* network errors */
667                 aclient->states[count] |= CFLastFailed; /* Mark serer down */
668             } else if (rcode == UNOTSYNC) {
669                 needsync = 1;
670             } else if (rcode != UNOQUORUM) {
671                 /* either misc ubik code, or misc appl code, or success. */
672                 aclient->states[count] &= ~CFLastFailed;        /* mark server up */
673                 goto done;      /* all done */
674             }
675         }                       /*s */
676     }                           /*p */
677
678   done:
679     if (needsync) {
680         if (!inlist) {          /* Remember proc call that needs sync site */
681             LOCK_UCLNT_CACHE;
682             calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
683             synccount++;
684             UNLOCK_UCLNT_CACHE;
685             inlist = 1;
686         }
687         if (!rcode) {           /* Remember the sync site - cmd successful */
688             rxp = rx_PeerOf(aclient->conns[count]);
689             aclient->syncSite = rx_HostOf(rxp);
690         }
691     }
692     UNLOCK_UBIK_CLIENT(aclient);
693     return rcode;
694 }