Implement the adjtime() system call and add a test for it to test69.
Additionally, install the adjtime.2 and clock_*.2 man pages.
./usr/man/man1/znew.1 minix-sys
./usr/man/man2/accept.2 minix-sys
./usr/man/man2/access.2 minix-sys
+./usr/man/man2/adjtime.2 minix-sys
./usr/man/man2/alarm.2 minix-sys
./usr/man/man2/bind.2 minix-sys
./usr/man/man2/brk.2 minix-sys
./usr/man/man2/chown.2 minix-sys
./usr/man/man2/chroot.2 minix-sys
./usr/man/man2/close.2 minix-sys
+./usr/man/man2/clock_getres.2 minix-sys
+./usr/man/man2/clock_gettime.2 minix-sys
+./usr/man/man2/clock_settime.2 minix-sys
./usr/man/man2/connect.2 minix-sys
./usr/man/man2/creat.2 minix-sys
./usr/man/man2/dup.2 minix-sys
* clock_stop: called just before MINIX shutdown
* get_realtime: get wall time since boot in clock ticks
* set_realtime: set wall time since boot in clock ticks
+ * set_adjtime_delta: set the number of ticks to adjust realtime
* get_monotonic: get monotonic time since boot in clock ticks
* set_timer: set a watchdog timer (+)
* reset_timer: reset a watchdog timer (+)
*/
static clock_t realtime = 0;
+/* Number of ticks to adjust realtime by. A negative value implies slowing
+ * down realtime, a positive value implies speeding it up.
+ */
+static clock_t adjtime_delta = 0;
+
/*
* The boot processor's timer interrupt handler. In addition to non-boot cpus
* it keeps real time and notifies the clock task if need be.
if (cpu_is_bsp(cpuid)) {
monotonic++;
- realtime++;
+
+ /* if adjtime_delta has ticks remaining, apply one to realtime.
+ * limit changes to every other interrupt.
+ */
+ if (adjtime_delta != 0 && monotonic & 0x1) {
+ /* go forward or stay behind */
+ realtime += (adjtime_delta > 0) ? 2 : 0;
+ adjtime_delta += (adjtime_delta > 0) ? -1 : +1;
+ } else {
+ realtime++;
+ }
}
/* Update user and system accounting times. Charge the current process
realtime = newrealtime;
}
+/*===========================================================================*
+ * set_adjtime_delta *
+ *===========================================================================*/
+void set_adjtime_delta(clock_t ticks)
+{
+ adjtime_delta = ticks;
+}
+
/*===========================================================================*
* get_monotonic *
*===========================================================================*/
/* clock.c */
clock_t get_realtime(void);
void set_realtime(clock_t);
+void set_adjtime_delta(clock_t);
clock_t get_monotonic(void);
void set_timer(struct timer *tp, clock_t t, tmr_func_t f);
void reset_timer(struct timer *tp);
*===========================================================================*/
int do_settime(struct proc * caller, message * m_ptr)
{
- clock_t newclock;
+ clock_t newclock, ticks;
time_t timediff;
if (m_ptr->T_CLOCK_ID != CLOCK_REALTIME) /* only realtime can change */
return EINVAL;
+ if (m_ptr->T_SETTIME_NOW == 0) { /* user just wants to adjtime() */
+ /* convert delta value from seconds and nseconds to ticks */
+ ticks = (m_ptr->T_TIME_SEC * system_hz) +
+ (m_ptr->T_TIME_NSEC/(1000000000/system_hz));
+ set_adjtime_delta(ticks);
+ return(OK);
+ } /* else user wants to set the time */
+
/* prevent a negative value for realtime */
if (m_ptr->T_TIME_SEC <= boottime) {
/* boottime was likely wrong, try to correct it. */
timediff = m_ptr->T_TIME_SEC - boottime;
newclock = (timediff*system_hz) + (m_ptr->T_TIME_NSEC/(1000000000/system_hz));
- if (m_ptr->T_SETTIME_NOW) {
- set_realtime(newclock);
- } /* else used adjtime() method (to be implemented) */
+ set_realtime(newclock);
return(OK);
}
_lwp_*
acct
-adjtime
lchmod
lchown
clone
.PATH: ${.CURDIR}/sys-minix
-SRCS+= accept.c access.c bind.c brk.c sbrk.c m_closefrom.c getsid.c \
+SRCS+= accept.c access.c adjtime.c bind.c brk.c sbrk.c m_closefrom.c getsid.c \
chdir.c chmod.c fchmod.c chown.c fchown.c chroot.c close.c \
clock_getres.c clock_gettime.c clock_settime.c \
connect.c dup.c dup2.c execve.c fcntl.c flock.c fpathconf.c fork.c \
--- /dev/null
+#include <sys/cdefs.h>
+#include <lib.h>
+#include "namespace.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+#ifdef __weak_alias
+__weak_alias(adjtime, __adjtime50);
+#endif
+
+int adjtime(const struct timeval *delta, struct timeval *olddelta)
+{
+ message m;
+
+ m.m2_i2 = 0; /* use adjtime() method to slowly adjust the clock. */
+ m.m2_i1 = (clockid_t) CLOCK_REALTIME;
+ m.m2_l1 = (time_t) delta->tv_sec;
+ m.m2_l2 = (long) delta->tv_usec * 1000; /* convert usec to nsec */
+
+ if (_syscall(PM_PROC_NR, CLOCK_SETTIME, &m) < 0)
+ return -1;
+
+ if (olddelta != NULL) {
+ /* the kernel returns immediately and the adjustment happens in the
+ * background. Also, any currently running adjustment is stopped by
+ * another call to adjtime(2), so the only values possible on Minix
+ * for olddelta are those of delta.
+ */
+ olddelta->tv_sec = delta->tv_sec;
+ olddelta->tv_usec = delta->tv_usec;
+ }
+
+ return 0;
+}
+
{
message m;
+ m.m2_i2 = 1; /* set time immediately. don't use adjtime() method. */
m.m2_i1 = (clockid_t) clock_id;
m.m2_l1 = (time_t) ts->tv_sec;
m.m2_l2 = (long) ts->tv_nsec;
.endif # !defined(__MINIX)
.if !defined(__MINIX)
-MAN+= accept.2 access.2 acct.2 adjtime.2 bind.2 brk.2 chdir.2 \
- chflags.2 chmod.2 chown.2 chroot.2 clock_settime.2 clone.2 close.2 \
+MAN+= accept.2 access.2 acct.2 bind.2 brk.2 chdir.2 \
+ chflags.2 chmod.2 chown.2 chroot.2 clone.2 close.2 \
connect.2 dup.2 execve.2 _exit.2 extattr_get_file.2 \
fcntl.2 fdatasync.2 fhopen.2 \
flock.2 fork.2 fsync.2 getcontext.2 getdents.2 \
mprotect.2 mremap.2 msgctl.2 msgget.2 msgrcv.2 msgsnd.2 msync.2 \
munmap.2 nanosleep.2 nfssvc.2 ntp_adjtime.2 open.2 pathconf.2 pipe.2
.else
-MAN+= pipe.2
+MAN+= adjtime.2 clock_settime.2 pipe.2
.endif # !defined(__MINIX)
.if !defined(__MINIX)
MAN+= pmc_control.2 poll.2 posix_fadvise.2 profil.2 ptrace.2 __quotactl.2 \
MLINKS+=chmod.2 fchmod.2 chmod.2 lchmod.2
MLINKS+=chown.2 fchown.2 chown.2 lchown.2
MLINKS+=chroot.2 fchroot.2
+.else
MLINKS+=clock_settime.2 clock_gettime.2
MLINKS+=clock_settime.2 clock_getres.2
+.endif # !defined(__MINIX)
+.if !defined(__MINIX)
MLINKS+=extattr_get_file.2 extattr_set_file.2 \
extattr_get_file.2 extattr_delete_file.2 \
extattr_get_file.2 extattr_list_file.2 \
#define svrctl_argp m2_p1
#define stime m2_l1
#define clk_id m2_i1
+#define settime_now m2_i2
#define time_sec m2_l1
#define time_nsec m2_l2
#define memsize m4_l1
switch (m_in.clk_id) {
case CLOCK_REALTIME:
- s= sys_settime(1, m_in.clk_id, m_in.time_sec, m_in.time_nsec);
+ s= sys_settime(m_in.settime_now, m_in.clk_id, m_in.time_sec,
+ m_in.time_nsec);
return(s);
case CLOCK_MONOTONIC: /* monotonic cannot be changed */
default:
#endif /* _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE || _NETBSD_SOURCE */
#if defined(_NETBSD_SOURCE) || defined(HAVE_NBTOOL_CONFIG_H)
-#ifndef __minix
int adjtime(const struct timeval *, struct timeval *) __RENAME(__adjtime50);
+#ifndef __minix
int futimes(int, const struct timeval [2]) __RENAME(__futimes50);
int lutimes(const char *, const struct timeval [2]) __RENAME(__lutimes50);
#endif /* !__minix */
-/* Test 69. clock_getres(), clock_gettime(). */
+/* Test 69. clock_getres(), clock_gettime(), clock_settime(), adjtime().
+ *
+ * Note, any type of ntpd or software that calls adjtime() or settimeofday()
+ * should be disabled while running this test. This test takes ~40s to run.
+ */
#include <time.h>
#include <sys/types.h>
#define TRIALS 100
#define MAX_ERROR 4
+#ifndef DEBUG
#define DEBUG 0
+#endif
int subtest = 1;
static void test_clock_getres();
static void test_clock_gettime();
static void test_clock_settime();
+static void test_adjtime();
static void show_timespec(char *msg, struct timespec *ts);
static void test_clock_getres()
if (ts2.tv_sec < 0 || ts2.tv_nsec < 0) e(29);
if (ts2.tv_sec <= ts.tv_sec) e(30);
-
if (clock_gettime(-1, &ts) == 0) e(31);
}
if (clock_settime(-1, &ts) == 0) e(60);
}
+static void test_adjtime(void)
+{
+ struct timeval delta, olddelta;
+ struct timespec rt, mt;
+
+ /* set the realtime clock to the same value as the monotonic clock */
+ if (clock_gettime(CLOCK_MONOTONIC, &mt) == -1) e(65);
+ if (clock_settime(CLOCK_REALTIME, &mt) == -1) e(66);
+
+ delta.tv_sec = 7;
+ delta.tv_usec = 0;
+
+ if (adjtime(&delta, &olddelta) != 0) e(70); /* adjust +7 seconds */
+ sleep(15); /* should take 14 seconds to adjust the clock */
+
+ /* check that the 7 second adjustment puts us between 5 and 10 seconds
+ * ahead of the monotonic clock.
+ */
+ if (clock_gettime(CLOCK_MONOTONIC, &mt) == -1) e(71);
+ if (clock_gettime(CLOCK_REALTIME, &rt) == -1) e(72);
+ show_timespec("Monotonic", &mt);
+ show_timespec("Realtime (+7)", &rt);
+ if (rt.tv_sec - 5 < mt.tv_sec || rt.tv_sec - 10 > mt.tv_sec) e(73);
+
+ delta.tv_sec = -7;
+ if (adjtime(&delta, &olddelta) != 0) e(73); /* adjust -7 seconds */
+ sleep(15); /* should take 14 seconds to adjust the clock */
+
+ /* check that the 7 second adjustment puts us close to even with
+ * the monotonic clock.
+ */
+ if (clock_gettime(CLOCK_MONOTONIC, &mt) == -1) e(74);
+ if (clock_gettime(CLOCK_REALTIME, &rt) == -1) e(75);
+ show_timespec("Monotonic", &mt);
+ show_timespec("Realtime (-7)", &rt);
+ if (abs(rt.tv_sec - mt.tv_sec) > 5) e(76);
+
+}
+
static void show_timespec(char *msg, struct timespec *ts)
{
#if DEBUG == 1
test_clock_getres();
test_clock_gettime();
test_clock_settime();
+ test_adjtime();
/* get test end time */
if (clock_gettime(CLOCK_MONOTONIC, &endtime) == -1) e(2);