1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      rpcclock.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  Routines for manipulating the time entities understood by the rpc_timer
90**  services.
91**
92**
93*/
94
95#include <commonp.h>
96
97
98/* ========================================================================= */
99
100GLOBAL rpc_clock_t rpc_g_clock_curr;
101GLOBAL rpc_clock_unix_t rpc_g_clock_unix_curr;
102
103EXTERNAL boolean32 rpc_g_long_sleep;
104
105INTERNAL struct timeval  start_time = { 0, 0 };
106INTERNAL rpc_clock_t     rpc_clock_skew = 0;
107
108/* ========================================================================= */
109
110/*
111 * R P C _ _ C L O C K _ S T A M P
112 *
113 * Timestamp a data structure with the current approximate tick count.
114 * The tick count returned is only updated by the rpc_timer routines
115 * once each time through the select listen loop.  This degree of accuracy
116 * should be adequate for the purpose of tracking the age of a data
117 * structure.
118 */
119
120PRIVATE rpc_clock_t rpc__clock_stamp(void)
121{
122    if (rpc_g_long_sleep)
123	rpc__clock_update();
124
125    return( rpc_g_clock_curr );
126}
127
128/*
129 * R P C _ _ C L O C K _ A G E D
130 *
131 * A routine to determine whether a specified timestamp has aged.
132 */
133
134PRIVATE boolean rpc__clock_aged
135(
136    rpc_clock_t      time,
137    rpc_clock_t      interval
138)
139{
140    return( rpc_g_clock_curr >= (time + interval) );
141}
142
143/*
144 * R P C _ _ C L O C K _ U N I X _ E X P I R E D
145 *
146 * Determine whether a specified UNIX timestamp is in the past.
147 */
148
149PRIVATE boolean rpc__clock_unix_expired
150(
151    rpc_clock_unix_t time
152)
153{
154    return( rpc_g_clock_unix_curr >= time );
155}
156
157/*
158 * R P C _ _ C L O C K _ U P D A T E
159 *
160 * Update the current tick count.  This routine is the only one that
161 * actually makes system calls to obtain the time.
162 */
163
164PRIVATE void rpc__clock_update(void)
165{
166
167    struct timeval tp;
168    time_t ticks;
169
170    /*
171     * On startup, just initialize start time.  Arrange for the initial
172     * time to be '1' tick (0 can be confusing in some cases).
173     */
174    if (start_time.tv_sec == 0)
175    {
176        gettimeofday( &start_time, (struct timezone *) 0 );
177        rpc_g_clock_unix_curr = start_time.tv_sec;
178        start_time.tv_usec -= (1000000L/RPC_C_CLOCK_HZ);
179        if (start_time.tv_usec < 0)
180        {
181            start_time.tv_usec += 1000000L;
182            start_time.tv_sec --;
183        }
184        rpc_g_clock_curr = (rpc_clock_t) 1;
185    }
186    else
187    {
188        /*
189         * Get the time of day from the system, and convert it to the
190         * tick count format we're using (RPC_C_CLOCK_HZ ticks per second).
191         * For now, just use 1 second accuracy.
192         */
193        gettimeofday(&tp, (struct timezone *) 0 );
194        rpc_g_clock_unix_curr = tp.tv_sec;
195
196        ticks = (tp.tv_sec - start_time.tv_sec) * RPC_C_CLOCK_HZ +
197                 rpc_clock_skew;
198
199        if (tp.tv_usec < start_time.tv_usec)
200        {
201            ticks -= RPC_C_CLOCK_HZ;
202            tp.tv_usec += 1000000L;
203        }
204        ticks += (tp.tv_usec - start_time.tv_usec) / (1000000L/RPC_C_CLOCK_HZ);
205
206        /*
207         * We need to watch out for changes to the system time after we
208         * have stored away our starting time value.  This situation is
209         * handled by maintaining a static variable containing the amount of
210         * RPC_C_CLOCK_HZ's we believe that the current time has been altered.
211         * This variable gets updated each time we detect that the system time
212         * has been modified.
213         *
214         * This scheme takes into account the fact that there are data
215         * structures that have been timestamped with tick counts;  it is
216         * not possible to simply start the tick count over, of to just
217         * update the trigger counts in the list of periodic funtions.
218         *
219         * We determine that the system time has been modified if 1) the
220         * tick count has gone backward, or 2) if we notice that we haven't
221         * been called in 60 seconds.  This last condition is based on the
222         * assumption that the listen loop will never intentionally wait
223         * that long before calling us. It would also be possible (tho more
224         * complicated) to look at the trigger count of the next periodic
225         * routine and assume that if we've gone a couple of seconds past
226         * that then something's wrong.
227         */
228        if ( (ticks < rpc_g_clock_curr) ||
229            (ticks > (rpc_g_clock_curr + RPC_CLOCK_SEC(60))))
230        {
231            rpc_clock_skew += rpc_g_clock_curr - ticks + RPC_C_CLOCK_HZ;
232            rpc_g_clock_curr += RPC_C_CLOCK_HZ;
233        }
234        else
235        {
236            rpc_g_clock_curr = (rpc_clock_t) ticks;
237        }
238    }
239}
240
241/*
242 * R P C _ _ C L O C K _ T I M E S P E C
243 *
244 * Return a "struct timespec" equivalent to a given rpc_clock_t.
245 */
246
247PRIVATE void rpc__clock_timespec
248(
249        rpc_clock_t clock,
250        struct timespec *ts
251)
252{
253    int whole_secs;
254    int remaining_ticks;
255
256    clock -= rpc_clock_skew;
257
258    whole_secs = (int)clock / RPC_C_CLOCK_HZ;
259    remaining_ticks = (int)clock % RPC_C_CLOCK_HZ;
260    if (remaining_ticks < 0)
261    {
262        whole_secs--;
263        remaining_ticks += RPC_C_CLOCK_HZ;
264    }
265
266    ts->tv_sec = start_time.tv_sec + whole_secs;
267    ts->tv_nsec = (1000 * start_time.tv_usec) +
268        remaining_ticks * (1000000000 / RPC_C_CLOCK_HZ);
269    if (ts->tv_nsec >= 1000000000)
270    {
271	ts->tv_nsec -= 1000000000;
272	ts->tv_sec += 1;
273    }
274}
275