Skip to content

Commit 360e938

Browse files
michallencAlan Carvalho de Assis
authored andcommitted
sched: add support for adjtime() interface
This commit adds Linux like adjtime() interface that is used to correct the system time clock if it varies from real value. The adjustment is done by slight adjustment of clock period and therefore the adjustment is without time jumps (both forward and backwards) The implementation is enabled by CONFIG_CLOCK_ADJTIME and separated from CONFIG_CLOCK_TIMEKEEPING functions. Options CONFIG_CLOCK_ADJTIME_SLEWLIMIT and CONFIG_CLOCK_ADJTIME_PERIOD can be used to control the adjustment speed. Interfaces up_get_timer_period() and up_adj_timer_period() has to be defined by architecture level support. This is not a POSIX interface but derives from 4.3BSD, System V. It is also supported for Linux compatibility. Signed-off-by: Michal Lenc <[email protected]>
1 parent 28eca64 commit 360e938

File tree

8 files changed

+375
-2
lines changed

8 files changed

+375
-2
lines changed

include/nuttx/arch.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,36 @@ void up_secure_irq_all(bool secure);
14571457

14581458
#endif
14591459

1460+
/****************************************************************************
1461+
* Function: up_adj_timer_period
1462+
*
1463+
* Description:
1464+
* Adjusts timer period. This call is used when adjusting timer period as
1465+
* defined in adjtime() function.
1466+
*
1467+
* Input Parameters:
1468+
* period_inc_usec - period adjustment in usec (reset to default value
1469+
* if 0)
1470+
*
1471+
****************************************************************************/
1472+
1473+
#ifdef CONFIG_CLOCK_ADJTIME
1474+
void up_adj_timer_period(long long period_inc_usec);
1475+
1476+
/****************************************************************************
1477+
* Function: up_get_timer_period
1478+
*
1479+
* Description:
1480+
* This function returns the timer period in usec.
1481+
*
1482+
* Input Parameters:
1483+
* period_usec - returned timer period in usec
1484+
*
1485+
****************************************************************************/
1486+
1487+
void up_get_timer_period(long long *period_usec);
1488+
#endif
1489+
14601490
/****************************************************************************
14611491
* Function: up_timer_initialize
14621492
*

include/sys/time.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ int settimeofday(FAR const struct timeval *tv,
251251
*
252252
****************************************************************************/
253253

254-
#ifdef CONFIG_CLOCK_TIMEKEEPING
254+
#if defined(CONFIG_CLOCK_TIMEKEEPING) || defined(CONFIG_CLOCK_ADJTIME)
255255
int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta);
256256
#endif
257257

sched/Kconfig

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,57 @@ config SYSTEM_TIME64
184184
and/or if a very long "uptime" is required, then this option can be
185185
selected to support a 64-bit wide timer.
186186

187+
config ARCH_HAVE_ADJTIME
188+
bool
189+
default n
190+
191+
config CLOCK_ADJTIME
192+
bool "Support adjtime function"
193+
default n
194+
depends on ARCH_HAVE_ADJTIME
195+
depends on !SCHED_TICKLESS
196+
depends on !CLOCK_TIMEKEEPING
197+
---help---
198+
Enables usage of adjtime() interface used to correct the system time
199+
clock. This requires specific architecture support capable of adjusting
200+
system timer period.
201+
202+
Interfaces up_get_timer_period() and up_adj_timer_period() has to be
203+
defined by architecture level support.
204+
205+
This is not a POSIX interface but derives from 4.3BSD, System V.
206+
It is also supported for Linux compatibility.
207+
208+
Currently it is not possible to use adjtime() if SCHED_TICKLESS or
209+
CLOCK_TIMEKEEPING is defined.
210+
211+
if CLOCK_ADJTIME
212+
213+
config CLOCK_ADJTIME_SLEWLIMIT
214+
int "Adjtime slew limit"
215+
default 2
216+
range 0 100
217+
---help---
218+
In real time systems we do not want the time to adjust too quickly.
219+
CLOCK_ADJTIME_SLEWLIMIT defines how many seconds can time change
220+
during each clock.
221+
222+
Note that CLOCK_ADJTIME_SLEWLIMIT is divided by 100 in source code,
223+
therefore CLOCK_ADJTIME_SLEWLIMIT=2 will result in possible 0.02 second
224+
adjustment.
225+
226+
config CLOCK_ADJTIME_PERIOD
227+
int "Adjtime period"
228+
default 97
229+
range 0 100
230+
---help---
231+
Define system clock adjustment period. Should be between 0.95 and 0.99.
232+
233+
Note that CLOCK_ADJTIME_PERIOD is divided by 100 in source code,
234+
therefore CLOCK_ADJTIME_PERIOD=97 will result in 0.97.
235+
236+
endif
237+
187238
config ARCH_HAVE_TIMEKEEPING
188239
bool
189240
default n

sched/clock/Make.defs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ ifeq ($(CONFIG_CLOCK_TIMEKEEPING),y)
2727
CSRCS += clock_timekeeping.c
2828
endif
2929

30+
ifeq ($(CONFIG_CLOCK_ADJTIME),y)
31+
CSRCS += clock_adjtime.c
32+
endif
33+
3034
# Include clock build support
3135

3236
DEPPATH += --dep-path clock

sched/clock/clock.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ extern volatile clock_t g_system_ticks;
6666
extern struct timespec g_basetime;
6767
#endif
6868

69+
#ifdef CONFIG_CLOCK_ADJTIME
70+
extern long long g_clk_adj_usec;
71+
extern long long g_clk_adj_count;
72+
#endif
73+
6974
/****************************************************************************
7075
* Public Function Prototypes
7176
****************************************************************************/
@@ -79,6 +84,11 @@ void clock_timer(void);
7984
# define clock_timer()
8085
#endif
8186

87+
#ifdef CONFIG_CLOCK_ADJTIME
88+
void clock_set_adjust(long long adj_usec, long long adj_count,
89+
long long *adj_usec_old, long long *adj_count_old);
90+
#endif
91+
8292
int clock_abstime2ticks(clockid_t clockid,
8393
FAR const struct timespec *abstime,
8494
FAR sclock_t *ticks);

sched/clock/clock_adjtime.c

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/****************************************************************************
2+
* sched/clock/clock_adjtime.c
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one or more
5+
* contributor license agreements. See the NOTICE file distributed with
6+
* this work for additional information regarding copyright ownership. The
7+
* ASF licenses this file to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance with the
9+
* License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16+
* License for the specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
****************************************************************************/
20+
21+
/****************************************************************************
22+
* Included Files
23+
****************************************************************************/
24+
25+
#include <nuttx/config.h>
26+
27+
#ifdef CONFIG_CLOCK_ADJTIME
28+
29+
#include <sys/time.h>
30+
#include <stdint.h>
31+
#include <time.h>
32+
#include <errno.h>
33+
#include <debug.h>
34+
35+
#include <nuttx/irq.h>
36+
#include <nuttx/arch.h>
37+
38+
#include "clock/clock.h"
39+
40+
/****************************************************************************
41+
* Pre-processor Definitions
42+
****************************************************************************/
43+
44+
/* Current adjtime implementation uses periodic clock tick to adjust clock
45+
* period. Therefore this implementation will not work when tickless mode
46+
* is enabled by CONFIG_SCHED_TICKLESS=y
47+
*/
48+
49+
#ifdef CONFIG_SCHED_TICKLESS
50+
# error CONFIG_CLOCK_ADJTIME is not supported when CONFIG_SCHED_TICKLESS \
51+
is enabled!
52+
#endif
53+
54+
/* Set slew limit. In real time systems we don't want the time to adjust
55+
* too quickly. ADJTIME_SLEWLIMIT defines how many seconds can time change
56+
* during each clock.
57+
*/
58+
59+
#define ADJTIME_SLEWLIMIT (CONFIG_CLOCK_ADJTIME_SLEWLIMIT * 0.01)
60+
61+
/* Define system clock adjustment period. */
62+
63+
#define ADJTIME_PERIOD (CONFIG_CLOCK_ADJTIME_PERIOD * 0.01)
64+
65+
/****************************************************************************
66+
* Private Data
67+
****************************************************************************/
68+
69+
/****************************************************************************
70+
* Private Functions
71+
****************************************************************************/
72+
73+
/****************************************************************************
74+
* Public Functions
75+
****************************************************************************/
76+
77+
/****************************************************************************
78+
* Name: adjtime
79+
*
80+
* Description:
81+
* The adjtime() function gradually adjusts the system clock (as returned
82+
* by gettimeofday(2)). The amount of time by which the clock is to be
83+
* adjusted is specified in the structure pointed to by delta.
84+
*
85+
* This structure has the following form:
86+
*
87+
* struct timeval
88+
* {
89+
* time_t tv_sec; (seconds)
90+
* long tv_usec; (microseconds)
91+
* };
92+
*
93+
* If the adjustment in delta is positive, then the system clock is
94+
* speeded up by some small percentage until the adjustment has been
95+
* completed. If the adjustment in delta is negative, then the clock is
96+
* slowed down in a similar fashion.
97+
*
98+
* If a clock adjustment from an earlier adjtime() call is already in
99+
* progress at the time of a later adjtime() call, and delta is not NULL
100+
* for the later call, then the earlier adjustment is stopped, but any
101+
* already completed part of that adjustment is not undone.
102+
*
103+
* If olddelta is not NULL, then the buffer that it points to is used to
104+
* return the amount of time remaining from any previous adjustment that
105+
* has not yet been completed.
106+
*
107+
* NOTE: This is not a POSIX interface but derives from 4.3BSD, System V.
108+
* It is also supported for Linux compatibility.
109+
*
110+
****************************************************************************/
111+
112+
int adjtime(FAR const struct timeval *delta, FAR struct timeval *olddelta)
113+
{
114+
irqstate_t flags;
115+
long long adjust_usec;
116+
long long period_usec;
117+
long long adjust_usec_old;
118+
long long count; /* Number of cycles over which
119+
* we adjust the period
120+
*/
121+
long long incr; /* Period increment applied on
122+
* every cycle.
123+
*/
124+
long long count_old; /* Previous number of cycles over which
125+
* we adjust the period
126+
*/
127+
long long incr_old; /* Previous period increment applied on
128+
* every cycle.
129+
*/
130+
long long incr_limit;
131+
int is_negative;
132+
133+
is_negative = 0;
134+
135+
if (!delta)
136+
{
137+
set_errno(EINVAL);
138+
return -1;
139+
}
140+
141+
flags = enter_critical_section();
142+
143+
adjust_usec = (long long)delta->tv_sec * USEC_PER_SEC + delta->tv_usec;
144+
145+
if (adjust_usec < 0)
146+
{
147+
adjust_usec = -adjust_usec;
148+
is_negative = 1;
149+
}
150+
151+
/* Get period in usec. Target hardware has to provide support for
152+
* this function call.
153+
*/
154+
155+
up_get_timer_period(&period_usec);
156+
157+
/* Determine how much we want to adjust timer period and the number
158+
* of cycles over which we want to do the adjustment.
159+
*/
160+
161+
count = (USEC_PER_SEC * ADJTIME_PERIOD) / period_usec;
162+
incr = adjust_usec / count;
163+
164+
/* Compute maximum possible period increase and check
165+
* whether previously computed increase exceeds the maximum
166+
* one.
167+
*/
168+
169+
incr_limit = ADJTIME_SLEWLIMIT * period_usec;
170+
if (incr > incr_limit)
171+
{
172+
/* It does... limit computed increase and increment count. */
173+
174+
incr = incr_limit;
175+
count = adjust_usec / incr;
176+
}
177+
178+
if (is_negative == 1)
179+
{
180+
/* Positive or negative? */
181+
182+
incr = -incr;
183+
}
184+
185+
/* Ignore small differences. */
186+
187+
if (incr == 0)
188+
{
189+
count = 0;
190+
}
191+
192+
leave_critical_section(flags);
193+
194+
/* Initialize clock adjustment and get old adjust values. */
195+
196+
clock_set_adjust(incr, count, &incr_old, &count_old);
197+
198+
adjust_usec_old = count_old * incr_old;
199+
if (olddelta)
200+
{
201+
olddelta->tv_sec = adjust_usec_old / USEC_PER_SEC;
202+
olddelta->tv_usec = adjust_usec_old;
203+
}
204+
205+
return OK;
206+
}
207+
208+
#endif /* CONFIG_CLOCK_ADJTIME */

0 commit comments

Comments
 (0)