d8747cbe551e927e645082813bf642502220f868
[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         return UMUTEXINIT;
232     }
233 #endif
234     tc->initializationState = ++ubik_initializationState;
235
236     /* first count the # of server conns so we can randomize properly */
237     count = 0;
238     for (i = 0; i < MAXSERVERS; i++) {
239         if (serverconns[i] == (struct rx_connection *)0)
240             break;
241         count++;
242     }
243
244     /* here count is the # of servers we're actually passed in.  Compute
245      * offset, a number between 0..count-1, where we'll start copying from the
246      * client-provided array. */
247     for (i = 0; i < count; i++) {
248         offset = afs_randomMod15() % count;
249         for (j = abs(offset); j < 2 * count; j++) {
250             if (!tc->conns[abs(j % count)]) {
251                 tc->conns[abs(j % count)] = serverconns[i];
252                 break;
253             }
254         }
255     }
256
257     *aclient = tc;
258     return 0;
259 }
260
261 /*!
262  * \brief Destroy an ubik connection.
263  *
264  * It calls rx to destroy the component rx connections, then frees the ubik
265  * connection structure.
266  */
267 afs_int32
268 ubik_ClientDestroy(struct ubik_client * aclient)
269 {
270     int c;
271
272     if (aclient == 0)
273         return 0;
274     LOCK_UBIK_CLIENT(aclient);
275     for (c = 0; c < MAXSERVERS; c++) {
276         struct rx_connection *rxConn = ubik_GetRPCConn(aclient, c);
277         if (rxConn == 0)
278             break;
279 #ifdef AFS_PTHREAD_ENV
280         rx_ReleaseCachedConnection(rxConn);
281 #else
282         rx_DestroyConnection(rxConn);
283 #endif
284     }
285     aclient->initializationState = 0;   /* client in not initialized */
286     UNLOCK_UBIK_CLIENT(aclient);
287 #ifdef AFS_PTHREAD_ENV
288     pthread_mutex_destroy(&(aclient->cm));      /* ignore failure */
289 #endif
290     free(aclient);
291     return 0;
292 }
293
294 /*!
295  * \brief So that intermittent failures that cause connections to die
296  *     don't kill whole ubik connection, refresh them when the connection is in
297  *     error.
298  */
299 struct rx_connection *
300 ubik_RefreshConn(struct rx_connection *tc)
301 {
302     afs_uint32 host;
303     u_short port;
304     u_short service;
305     struct rx_securityClass *sc;
306     int si;
307     struct rx_connection *newTc;
308
309     host = rx_HostOf(rx_PeerOf(tc));
310     port = rx_PortOf(rx_PeerOf(tc));
311     service = rx_ServiceIdOf(tc);
312     sc = rx_SecurityObjectOf(tc);
313     si = rx_SecurityClassOf(tc);
314
315     /*
316      * destroy old one after creating new one so that refCount on security
317      * object cannot reach zero.
318      */
319     newTc = rx_NewConnection(host, port, service, sc, si);
320     rx_DestroyConnection(tc);
321     return newTc;
322 }
323
324 #ifdef AFS_PTHREAD_ENV
325
326 pthread_once_t ubik_client_once = PTHREAD_ONCE_INIT;
327 pthread_mutex_t ubik_client_mutex;
328 #define LOCK_UCLNT_CACHE do { \
329     opr_Verify(pthread_once(&ubik_client_once, ubik_client_init_mutex) == 0); \
330     MUTEX_ENTER(&ubik_client_mutex); \
331   } while (0)
332 #define UNLOCK_UCLNT_CACHE MUTEX_EXIT(&ubik_client_mutex)
333
334 void
335 ubik_client_init_mutex(void)
336 {
337     MUTEX_INIT(&ubik_client_mutex, "client init", MUTEX_DEFAULT, 0);
338 }
339
340 #else
341
342 #define LOCK_UCLNT_CACHE
343 #define UNLOCK_UCLNT_CACHE
344
345 #endif
346
347 #define SYNCCOUNT 10
348 static int *calls_needsync[SYNCCOUNT];  /* proc calls that need the sync site */
349 static int synccount = 0;
350
351
352
353 /*!
354  * \brief Call this after getting back a #UNOTSYNC.
355  *
356  * \note Getting a #UNOTSYNC error code back does \b not guarantee
357  * that there is a sync site yet elected.  However, if there is a sync
358  * site out there somewhere, and you're trying an operation that
359  * requires a sync site, ubik will return #UNOTSYNC, indicating the
360  * operation won't work until you find a sync site
361  */
362 static int
363 try_GetSyncSite(struct ubik_client *aclient, afs_int32 apos)
364 {
365     struct rx_peer *rxp;
366     afs_int32 code;
367     int i;
368     afs_int32 thisHost, newHost;
369     struct rx_connection *tc;
370     short origLevel;
371
372     origLevel = aclient->initializationState;
373
374     /* get this conn */
375     tc = aclient->conns[apos];
376     if (tc && rx_ConnError(tc)) {
377         aclient->conns[apos] = (tc = ubik_RefreshConn(tc));
378     }
379     if (!tc) {
380         return -1;
381     }
382
383     /* now see if we can find the sync site host */
384     code = VOTE_GetSyncSite(tc, &newHost);
385     if (aclient->initializationState != origLevel) {
386         return -1;              /* somebody did a ubik_ClientInit */
387     }
388
389     if (!code && newHost) {
390         newHost = htonl(newHost);       /* convert back to network order */
391
392         /*
393          * position count at the appropriate slot in the client
394          * structure and retry. If we can't find in slot, we'll just
395          * continue through the whole list
396          */
397         for (i = 0; i < MAXSERVERS; i++) {
398             rxp = rx_PeerOf(aclient->conns[i]);
399             thisHost = rx_HostOf(rxp);
400             if (!thisHost) {
401                 return -1;
402             } else if (thisHost == newHost) {
403                 return i;       /* we were told to use this one */
404             }
405         }
406     }
407     return -1;
408 }
409
410 #define NEED_LOCK 1
411 #define NO_LOCK 0
412
413 /*!
414  * \brief Create an internal version of ubik_CallIter that takes an additional
415  * parameter - to indicate whether the ubik client handle has already
416  * been locked.
417  */
418 static afs_int32
419 CallIter(int (*aproc) (), struct ubik_client *aclient,
420          afs_int32 aflags, int *apos, long p1, long p2, long p3, long p4,
421          long p5, long p6, long p7, long p8, long p9, long p10, long p11,
422          long p12, long p13, long p14, long p15, long p16, int needlock)
423 {
424     afs_int32 code;
425     struct rx_connection *tc;
426     short origLevel;
427
428     if (needlock) {
429         LOCK_UBIK_CLIENT(aclient);
430     }
431     origLevel = aclient->initializationState;
432
433     code = UNOSERVERS;
434
435     while (*apos < MAXSERVERS) {
436         /* tc is the next conn to try */
437         tc = aclient->conns[*apos];
438         if (!tc)
439             goto errout;
440
441         if (rx_ConnError(tc)) {
442             tc = ubik_RefreshConn(tc);
443             aclient->conns[*apos] = tc;
444         }
445
446         if ((aflags & UPUBIKONLY) && (aclient->states[*apos] & CFLastFailed)) {
447             (*apos)++;          /* try another one if this server is down */
448         } else {
449             break;              /* this is the desired path */
450         }
451     }
452     if (*apos >= MAXSERVERS)
453         goto errout;
454
455     code =
456         (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13,
457                   p14, p15, p16);
458     if (aclient->initializationState != origLevel)
459         /* somebody did a ubik_ClientInit */
460         goto errout;
461
462     /* what should I do in case of UNOQUORUM ? */
463     if (code < 0) {
464         aclient->states[*apos] |= CFLastFailed; /* network errors */
465     } else {
466         /* either misc ubik code, or misc application code or success. */
467         aclient->states[*apos] &= ~CFLastFailed;        /* operation worked */
468     }
469
470     (*apos)++;
471 errout:
472     if (needlock) {
473         UNLOCK_UBIK_CLIENT(aclient);
474     }
475     return code;
476 }
477
478 /*!
479  * \brief This is part of an iterator.  It doesn't handle finding sync sites.
480  */
481 afs_int32
482 ubik_CallIter(int (*aproc) (), struct ubik_client *aclient,
483                                afs_int32 aflags, int *apos, long p1, long p2,
484                                long p3, long p4, long p5, long p6, long p7,
485                                long p8, long p9, long p10, long p11, long p12,
486                                long p13, long p14, long p15, long p16)
487 {
488     return CallIter(aproc, aclient, aflags, apos, p1, p2, p3, p4, p5, p6, p7,
489                     p8, p9, p10, p11, p12, p13, p14, p15, p16, NEED_LOCK);
490 }
491
492 /*!
493  * \brief Call this instead of stub and we'll guarantee to find a host that's up.
494  *
495  * \todo In the future, we should also put in a protocol to find the sync site.
496  */
497 afs_int32
498 ubik_Call_New(int (*aproc) (), struct ubik_client *aclient,
499               afs_int32 aflags, long p1, long p2, long p3, long p4, long p5,
500               long p6, long p7, long p8, long p9, long p10, long p11,
501               long p12, long p13, long p14, long p15, long p16)
502 {
503     afs_int32 code, rcode;
504     afs_int32 count;
505     afs_int32 temp;
506     int pass;
507     int stepBack;
508     short origLevel;
509
510     LOCK_UBIK_CLIENT(aclient);
511   restart:
512     rcode = UNOSERVERS;
513     origLevel = aclient->initializationState;
514
515     /* Do two passes. First pass only checks servers known running */
516     for (aflags |= UPUBIKONLY, pass = 0; pass < 2;
517          pass++, aflags &= ~UPUBIKONLY) {
518         stepBack = 0;
519         count = 0;
520         while (1) {
521             code =
522                 CallIter(aproc, aclient, aflags, &count, p1, p2, p3, p4, p5,
523                          p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16,
524                          NO_LOCK);
525             if (code && (aclient->initializationState != origLevel)) {
526                 goto restart;
527             }
528             if (code == UNOSERVERS) {
529                 break;
530             }
531             rcode = code;       /* remember code from last good call */
532
533             if (code == UNOTSYNC) {     /* means this requires a sync site */
534                 if (aclient->conns[3]) {        /* don't bother unless 4 or more srv */
535                     temp = try_GetSyncSite(aclient, count);
536                     if (aclient->initializationState != origLevel) {
537                         goto restart;   /* somebody did a ubik_ClientInit */
538                     }
539                     if ((temp >= 0) && ((temp > count) || (stepBack++ <= 2))) {
540                         count = temp;   /* generally try to make progress */
541                     }
542                 }
543             } else if ((code >= 0) && (code != UNOQUORUM)) {
544                 UNLOCK_UBIK_CLIENT(aclient);
545                 return code;    /* success or global error condition */
546             }
547         }
548     }
549     UNLOCK_UBIK_CLIENT(aclient);
550     return rcode;
551 }
552
553 /*!
554  * call this instead of stub and we'll guarantee to find a host that's up.
555  *
556  * \todo In the future, we should also put in a protocol to find the sync site.
557  */
558 afs_int32
559 ubik_Call(int (*aproc) (), struct ubik_client *aclient,
560           afs_int32 aflags, long p1, long p2, long p3, long p4,
561           long p5, long p6, long p7, long p8, long p9, long p10,
562           long p11, long p12, long p13, long p14, long p15, long p16)
563 {
564     afs_int32 rcode, code, newHost, thisHost, i, count;
565     int chaseCount, pass, needsync, inlist, j;
566     struct rx_connection *tc;
567     struct rx_peer *rxp;
568     short origLevel;
569
570     if (aflags & UBIK_CALL_NEW)
571         return ubik_Call_New(aproc, aclient, aflags, p1, p2, p3, p4,
572                              p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
573                              p16);
574
575     if (!aclient)
576         return UNOENT;
577     LOCK_UBIK_CLIENT(aclient);
578
579   restart:
580     origLevel = aclient->initializationState;
581     rcode = UNOSERVERS;
582     chaseCount = inlist = needsync = 0;
583
584     LOCK_UCLNT_CACHE;
585     for (j = 0; ((j < SYNCCOUNT) && calls_needsync[j]); j++) {
586         if (calls_needsync[j] == (int *)aproc) {
587             inlist = needsync = 1;
588             break;
589         }
590     }
591     UNLOCK_UCLNT_CACHE;
592     /*
593      * First  pass, we try all servers that are up.
594      * Second pass, we try all servers.
595      */
596     for (pass = 0; pass < 2; pass++) {  /*p */
597         /* For each entry in our servers list */
598         for (count = 0;; count++) {     /*s */
599
600             if (needsync) {
601                 /* Need a sync site. Lets try to quickly find it */
602                 if (aclient->syncSite) {
603                     newHost = aclient->syncSite;        /* already in network order */
604                     aclient->syncSite = 0;      /* Will reset if it works */
605                 } else if (aclient->conns[3]) {
606                     /* If there are fewer than four db servers in a cell,
607                      * there's no point in making the GetSyncSite call.
608                      * At best, it's a wash. At worst, it results in more
609                      * RPCs than you would otherwise make.
610                      */
611                     tc = aclient->conns[count];
612                     if (tc && rx_ConnError(tc)) {
613                         aclient->conns[count] = tc = ubik_RefreshConn(tc);
614                     }
615                     if (!tc)
616                         break;
617                     code = VOTE_GetSyncSite(tc, &newHost);
618                     if (aclient->initializationState != origLevel)
619                         goto restart;   /* somebody did a ubik_ClientInit */
620                     if (code)
621                         newHost = 0;
622                     newHost = htonl(newHost);   /* convert to network order */
623                 } else {
624                     newHost = 0;
625                 }
626                 if (newHost) {
627                     /* position count at the appropriate slot in the client
628                      * structure and retry. If we can't find in slot, we'll
629                      * just continue through the whole list
630                      */
631                     for (i = 0; i < MAXSERVERS && aclient->conns[i]; i++) {
632                         rxp = rx_PeerOf(aclient->conns[i]);
633                         thisHost = rx_HostOf(rxp);
634                         if (!thisHost)
635                             break;
636                         if (thisHost == newHost) {
637                             if (chaseCount++ > 2)
638                                 break;  /* avoid loop asking */
639                             count = i;  /* this index is the sync site */
640                             break;
641                         }
642                     }
643                 }
644             }
645             /*needsync */
646             tc = aclient->conns[count];
647             if (tc && rx_ConnError(tc)) {
648                 aclient->conns[count] = tc = ubik_RefreshConn(tc);
649             }
650             if (!tc)
651                 break;
652
653             if ((pass == 0) && (aclient->states[count] & CFLastFailed)) {
654                 continue;       /* this guy's down */
655             }
656
657             rcode =
658                 (*aproc) (tc, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11,
659                           p12, p13, p14, p15, p16);
660             if (aclient->initializationState != origLevel) {
661                 /* somebody did a ubik_ClientInit */
662                 if (rcode)
663                     goto restart;       /* call failed */
664                 else
665                     goto done;  /* call suceeded */
666             }
667             if (rcode < 0) {    /* network errors */
668                 aclient->states[count] |= CFLastFailed; /* Mark serer down */
669             } else if (rcode == UNOTSYNC) {
670                 needsync = 1;
671             } else if (rcode != UNOQUORUM) {
672                 /* either misc ubik code, or misc appl code, or success. */
673                 aclient->states[count] &= ~CFLastFailed;        /* mark server up */
674                 goto done;      /* all done */
675             }
676         }                       /*s */
677     }                           /*p */
678
679   done:
680     if (needsync) {
681         if (!inlist) {          /* Remember proc call that needs sync site */
682             LOCK_UCLNT_CACHE;
683             calls_needsync[synccount % SYNCCOUNT] = (int *)aproc;
684             synccount++;
685             UNLOCK_UCLNT_CACHE;
686             inlist = 1;
687         }
688         if (!rcode) {           /* Remember the sync site - cmd successful */
689             rxp = rx_PeerOf(aclient->conns[count]);
690             aclient->syncSite = rx_HostOf(rxp);
691         }
692     }
693     UNLOCK_UBIK_CLIENT(aclient);
694     return rcode;
695 }