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