Annotation of embedaddon/ntp/libntp/adjtime.c, revision 1.1

1.1     ! misho       1: #ifdef HAVE_CONFIG_H
        !             2: # include <config.h>
        !             3: #endif
        !             4: 
        !             5: #ifdef MPE 
        !             6: /*
        !             7:  * MPE lacks adjtime(), so we define our own.  But note that time slewing has
        !             8:  * a sub-second accuracy bug documented in SR 5003462838 which prevents ntpd
        !             9:  * from being able to maintain clock synch.  Because of the bug, this adjtime()
        !            10:  * implementation as used by ntpd has a side-effect of screwing up the hardware
        !            11:  * PDC clock, which will need to be reset with a reboot.
        !            12:  *
        !            13:  * This problem affects all versions of MPE at the time of this writing (when
        !            14:  * MPE/iX 7.0 is the most current).  It only causes bad things to happen when
        !            15:  * doing continuous clock synchronization with ntpd; note that you CAN run ntpd
        !            16:  * with "disable ntp" in ntp.conf if you wish to provide a time server.
        !            17:  *
        !            18:  * The one-time clock adjustment functionality of ntpdate and ntp_timeset can
        !            19:  * be used without screwing up the PDC clock.
        !            20:  * 
        !            21:  */
        !            22: #include <time.h>
        !            23: 
        !            24: int adjtime(struct timeval *delta, struct timeval *olddelta);
        !            25: 
        !            26: int adjtime(struct timeval *delta, struct timeval *olddelta)
        !            27: 
        !            28: {
        !            29: /* Documented, supported MPE system intrinsics. */
        !            30: 
        !            31: extern void GETPRIVMODE(void);
        !            32: extern void GETUSERMODE(void);
        !            33: 
        !            34: /* Undocumented, unsupported MPE internal functions. */
        !            35: 
        !            36: extern long long current_correction_usecs(void);
        !            37: extern long long get_time(void);
        !            38: extern void get_time_change_info(long long *, char *, char *);
        !            39: extern long long pdc_time(int *);
        !            40: extern void set_time_correction(long long, int, int);
        !            41: extern long long ticks_to_micro(long long);
        !            42: 
        !            43: long long big_sec, big_usec, new_correction = 0LL;
        !            44: long long prev_correction;
        !            45: 
        !            46: if (delta != NULL) {
        !            47:   /* Adjustment required.  Convert delta to 64-bit microseconds. */
        !            48:   big_sec = (long)delta->tv_sec;
        !            49:   big_usec = delta->tv_usec;
        !            50:   new_correction = (big_sec * 1000000LL) + big_usec;
        !            51: }
        !            52: 
        !            53: GETPRIVMODE();
        !            54: 
        !            55: /* Determine how much of a previous correction (if any) we're interrupting. */
        !            56: prev_correction = current_correction_usecs();
        !            57: 
        !            58: if (delta != NULL) {
        !            59:   /* Adjustment required. */
        !            60: 
        !            61: #if 0
        !            62:   /* Speculative code disabled until bug SR 5003462838 is fixed.  This bug
        !            63:      prevents accurate time slewing, and indeed renders ntpd inoperable. */
        !            64: 
        !            65:   if (prev_correction != 0LL) {
        !            66:     /* A previous adjustment did not complete.  Since the PDC UTC clock was
        !            67:     immediately jumped at the start of the previous adjustment, we must
        !            68:     explicitly reset it to the value of the MPE local time clock minus the
        !            69:     time zone offset. */
        !            70: 
        !            71:     char pwf_since_boot, recover_pwf_time;
        !            72:     long long offset_ticks, offset_usecs, pdc_usecs_current, pdc_usecs_wanted;
        !            73:     int hpe_status;
        !            74: 
        !            75:     get_time_change_info(&offset_ticks, &pwf_since_boot, &recover_pwf_time);
        !            76:     offset_usecs = ticks_to_micro(offset_ticks);
        !            77:     pdc_usecs_wanted = get_time() - offset_usecs;
        !            78:     pdc_usecs_current = pdc_time(&hpe_status);
        !            79:     if (hpe_status == 0) 
        !            80:       /* Force new PDC time by starting an extra correction. */
        !            81:       set_time_correction(pdc_usecs_wanted - pdc_usecs_current,0,1);
        !            82:   }
        !            83: #endif /* 0 */
        !            84:     
        !            85:   /* Immediately jump the PDC time to the new value, and then initiate a 
        !            86:      gradual MPE time correction slew. */
        !            87:   set_time_correction(new_correction,0,1);
        !            88: }
        !            89: 
        !            90: GETUSERMODE();
        !            91: 
        !            92: if (olddelta != NULL) {
        !            93:   /* Caller wants to know remaining amount of previous correction. */
        !            94:   (long)olddelta->tv_sec = prev_correction / 1000000LL;
        !            95:   olddelta->tv_usec = prev_correction % 1000000LL;
        !            96: }
        !            97: 
        !            98: return 0;
        !            99: }
        !           100: #endif /* MPE */
        !           101: 
        !           102: #ifdef NEED_HPUX_ADJTIME
        !           103: /*************************************************************************/
        !           104: /* (c) Copyright Tai Jin, 1988.  All Rights Reserved.                    */
        !           105: /*     Hewlett-Packard Laboratories.                                     */
        !           106: /*                                                                       */
        !           107: /* Permission is hereby granted for unlimited modification, use, and     */
        !           108: /* distribution.  This software is made available with no warranty of    */
        !           109: /* any kind, express or implied.  This copyright notice must remain      */
        !           110: /* intact in all versions of this software.                              */
        !           111: /*                                                                       */
        !           112: /* The author would appreciate it if any bug fixes and enhancements were */
        !           113: /* to be sent back to him for incorporation into future versions of this */
        !           114: /* software.  Please send changes to tai@iag.hp.com or ken@sdd.hp.com.   */
        !           115: /*************************************************************************/
        !           116: 
        !           117: /*
        !           118:  * Revision history
        !           119:  *
        !           120:  * 9 Jul 94    David L. Mills, Unibergity of Delabunch
        !           121:  *             Implemented variable threshold to limit age of
        !           122:  *             corrections; reformatted code for readability.
        !           123:  */
        !           124: 
        !           125: #ifndef lint
        !           126: static char RCSid[] = "adjtime.c,v 3.1 1993/07/06 01:04:42 jbj Exp";
        !           127: #endif
        !           128: 
        !           129: #include <sys/types.h>
        !           130: #include <sys/ipc.h>
        !           131: #include <sys/msg.h>
        !           132: #include <time.h>
        !           133: #include <signal.h>
        !           134: #include "adjtime.h"
        !           135: 
        !           136: #define abs(x)  ((x) < 0 ? -(x) : (x))
        !           137: 
        !           138: /*
        !           139:  * The following paramters are appropriate for an NTP adjustment
        !           140:  * interval of one second.
        !           141:  */
        !           142: #define ADJ_THRESH 200         /* initial threshold */
        !           143: #define ADJ_DELTA 4            /* threshold decrement */
        !           144: 
        !           145: static long adjthresh;         /* adjustment threshold */
        !           146: static long saveup;            /* corrections accumulator */
        !           147: 
        !           148: /*
        !           149:  * clear_adjtime - reset accumulator and threshold variables
        !           150:  */
        !           151: void
        !           152: _clear_adjtime(void)
        !           153: {
        !           154:        saveup = 0;
        !           155:        adjthresh = ADJ_THRESH;
        !           156: }
        !           157: 
        !           158: /*
        !           159:  * adjtime - hp-ux copout of the standard Unix adjtime() system call
        !           160:  */
        !           161: int
        !           162: adjtime(
        !           163:        register struct timeval *delta,
        !           164:        register struct timeval *olddelta
        !           165:        )
        !           166: {
        !           167:        struct timeval newdelta;
        !           168: 
        !           169:        /*
        !           170:         * Corrections greater than one second are done immediately.
        !           171:         */
        !           172:        if (delta->tv_sec) {
        !           173:                adjthresh = ADJ_THRESH;
        !           174:                saveup = 0;
        !           175:                return(_adjtime(delta, olddelta));
        !           176:        }
        !           177: 
        !           178:        /*
        !           179:         * Corrections less than one second are accumulated until
        !           180:         * tripping a threshold, which is initially set at ADJ_THESH and
        !           181:         * reduced in ADJ_DELTA steps to zero. The idea here is to
        !           182:         * introduce large corrections quickly, while making sure that
        !           183:         * small corrections are introduced without excessive delay. The
        !           184:         * idea comes from the ARPAnet routing update algorithm.
        !           185:         */
        !           186:        saveup += delta->tv_usec;
        !           187:        if (abs(saveup) >= adjthresh) {
        !           188:                adjthresh = ADJ_THRESH;
        !           189:                newdelta.tv_sec = 0;
        !           190:                newdelta.tv_usec = saveup;
        !           191:                saveup = 0;
        !           192:                return(_adjtime(&newdelta, olddelta));
        !           193:        } else {
        !           194:                adjthresh -= ADJ_DELTA;
        !           195:        }
        !           196: 
        !           197:        /*
        !           198:         * While nobody uses it, return the residual before correction,
        !           199:         * as per Unix convention.
        !           200:         */
        !           201:        if (olddelta)
        !           202:            olddelta->tv_sec = olddelta->tv_usec = 0;
        !           203:        return(0);
        !           204: }
        !           205: 
        !           206: /*
        !           207:  * _adjtime - does the actual work
        !           208:  */
        !           209: int
        !           210: _adjtime(
        !           211:        register struct timeval *delta,
        !           212:        register struct timeval *olddelta
        !           213:        )
        !           214: {
        !           215:        register int mqid;
        !           216:        MsgBuf msg;
        !           217:        register MsgBuf *msgp = &msg;
        !           218: 
        !           219:        /*
        !           220:         * Get the key to the adjtime message queue (note that we must
        !           221:         * get it every time because the queue might have been removed
        !           222:         * and recreated)
        !           223:         */
        !           224:        if ((mqid = msgget(KEY, 0)) == -1)
        !           225:            return (-1);
        !           226:        msgp->msgb.mtype = CLIENT;
        !           227:        msgp->msgb.tv = *delta;
        !           228:        if (olddelta)
        !           229:            msgp->msgb.code = DELTA2;
        !           230:        else
        !           231:            msgp->msgb.code = DELTA1;
        !           232: 
        !           233:        /*
        !           234:         * Tickle adjtimed and snatch residual, if indicated. Lots of
        !           235:         * fanatic error checking here.
        !           236:         */
        !           237:        if (msgsnd(mqid, &msgp->msgp, MSGSIZE, 0) == -1)
        !           238:            return (-1);
        !           239:        if (olddelta) {
        !           240:                if (msgrcv(mqid, &msgp->msgp, MSGSIZE, SERVER, 0) == -1)
        !           241:                    return (-1);
        !           242:                *olddelta = msgp->msgb.tv;
        !           243:        }
        !           244:        return (0);
        !           245: }
        !           246: 
        !           247: #else
        !           248: # if NEED_QNX_ADJTIME
        !           249: /*
        !           250:  * Emulate adjtime() using QNX ClockAdjust().
        !           251:  * Chris Burghart <burghart@atd.ucar.edu>, 11/2001
        !           252:  * Miroslaw Pabich <miroslaw_pabich@o2.pl>, 09/2005
        !           253:  *
        !           254:  * This is an implementation of adjtime() for QNX.  
        !           255:  * ClockAdjust() is used to tweak the system clock for about
        !           256:  * 1 second period until the desired delta is achieved.
        !           257:  * Time correction slew is limited to reasonable value.
        !           258:  * Internal rounding and relative errors are reduced.
        !           259:  */
        !           260: # include <sys/neutrino.h>
        !           261: # include <sys/time.h>
        !           262: 
        !           263: # include <ntp_stdlib.h>
        !           264: 
        !           265: /*
        !           266:  * Time correction slew limit. QNX is a hard real-time system,
        !           267:  * so don't adjust system clock too fast.
        !           268:  */
        !           269: #define CORR_SLEW_LIMIT     0.02  /* [s/s] */
        !           270: 
        !           271: /*
        !           272:  * Period of system clock adjustment. It should be equal to adjtime
        !           273:  * execution period (1s). If slightly less than 1s (0.95-0.99), then olddelta
        !           274:  * residual error (introduced by execution period jitter) will be reduced.
        !           275:  */
        !           276: #define ADJUST_PERIOD       0.97  /* [s] */
        !           277: 
        !           278: int 
        !           279: adjtime (struct timeval *delta, struct timeval *olddelta)
        !           280: {
        !           281:     double delta_nsec;
        !           282:     double delta_nsec_old;
        !           283:     struct _clockadjust adj;
        !           284:     struct _clockadjust oldadj;
        !           285: 
        !           286:     /*
        !           287:      * How many nanoseconds are we adjusting?
        !           288:      */
        !           289:     if (delta != NULL)
        !           290:        delta_nsec = 1e9 * (long)delta->tv_sec + 1e3 * delta->tv_usec;
        !           291:     else
        !           292:        delta_nsec = 0;
        !           293: 
        !           294:     /*
        !           295:      * Build the adjust structure and call ClockAdjust()
        !           296:      */
        !           297:     if (delta_nsec != 0)
        !           298:     {
        !           299:        struct _clockperiod period;
        !           300:        long count;
        !           301:        long increment;
        !           302:        long increment_limit;
        !           303:        int isneg = 0;
        !           304: 
        !           305:        /*
        !           306:         * Convert to absolute value for future processing
        !           307:         */
        !           308:        if (delta_nsec < 0)
        !           309:        {
        !           310:            isneg = 1;
        !           311:            delta_nsec = -delta_nsec;
        !           312:        }
        !           313: 
        !           314:        /*
        !           315:         * Get the current clock period (nanoseconds)
        !           316:         */
        !           317:        if (ClockPeriod (CLOCK_REALTIME, 0, &period, 0) < 0)
        !           318:            return -1;
        !           319: 
        !           320:        /*
        !           321:         * Compute count and nanoseconds increment
        !           322:         */
        !           323:        count = 1e9 * ADJUST_PERIOD / period.nsec;
        !           324:        increment = delta_nsec / count + .5;
        !           325:        /* Reduce relative error */
        !           326:        if (count > increment + 1)
        !           327:        {
        !           328:            increment = 1 + (long)((delta_nsec - 1) / count);
        !           329:            count = delta_nsec / increment + .5;
        !           330:        }
        !           331: 
        !           332:        /*
        !           333:         * Limit the adjust increment to appropriate value
        !           334:         */
        !           335:        increment_limit = CORR_SLEW_LIMIT * period.nsec;
        !           336:        if (increment > increment_limit)
        !           337:        {
        !           338:            increment = increment_limit;
        !           339:            count = delta_nsec / increment + .5;
        !           340:            /* Reduce relative error */
        !           341:            if (increment > count + 1)
        !           342:            {
        !           343:                count =  1 + (long)((delta_nsec - 1) / increment);
        !           344:                increment = delta_nsec / count + .5;
        !           345:            }
        !           346:        }
        !           347: 
        !           348:        adj.tick_nsec_inc = isneg ? -increment : increment;
        !           349:        adj.tick_count = count;
        !           350:     }
        !           351:     else
        !           352:     {
        !           353:        adj.tick_nsec_inc = 0;
        !           354:        adj.tick_count = 0;
        !           355:     }
        !           356: 
        !           357:     if (ClockAdjust (CLOCK_REALTIME, &adj, &oldadj) < 0)
        !           358:        return -1;
        !           359: 
        !           360:     /*
        !           361:      * Build olddelta
        !           362:      */
        !           363:     delta_nsec_old = (double)oldadj.tick_count * oldadj.tick_nsec_inc;
        !           364:     if (olddelta != NULL)
        !           365:     {
        !           366:        if (delta_nsec_old != 0)
        !           367:        {
        !           368:            /* Reduce rounding error */
        !           369:            delta_nsec_old += (delta_nsec_old < 0) ? -500 : 500;
        !           370:            olddelta->tv_sec = delta_nsec_old / 1e9;
        !           371:            olddelta->tv_usec = (long)(delta_nsec_old - 1e9
        !           372:                                 * (long)olddelta->tv_sec) / 1000;
        !           373:        }
        !           374:        else
        !           375:        {
        !           376:            olddelta->tv_sec = 0;
        !           377:            olddelta->tv_usec = 0;
        !           378:        }
        !           379:     }
        !           380: 
        !           381:     return 0;
        !           382: }
        !           383: # else /* no special adjtime() needed */
        !           384: int adjtime_bs;
        !           385: # endif
        !           386: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>