Add a new sysctl, debug.clock_do_io, to allow manully triggering a one-shot

read or write of all registered realtime clocks.  In the read case, the
values read are simply discarded.  For writes, there's no alternative but
to actually write the current system time to the device.
This commit is contained in:
ian 2018-02-12 17:41:11 +00:00
parent e5909c170b
commit e1340a314d

View File

@ -80,6 +80,11 @@ static int show_io;
SYSCTL_INT(_debug, OID_AUTO, clock_show_io, CTLFLAG_RWTUN, &show_io, 0,
"Enable debug printing of RTC clock I/O; 1=reads, 2=writes, 3=both.");
static int sysctl_clock_do_io(SYSCTL_HANDLER_ARGS);
SYSCTL_PROC(_debug, OID_AUTO, clock_do_io, CTLTYPE_INT | CTLFLAG_RW,
0, 0, sysctl_clock_do_io, "I",
"Trigger one-time IO on RTC clocks; 1=read (and discard), 2=write");
/* XXX: should be kern. now, it's no longer machdep. */
static int disable_rtc_set;
SYSCTL_INT(_machdep, OID_AUTO, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set,
@ -280,6 +285,36 @@ clock_schedule(device_t clockdev, u_int offsetns)
sx_xunlock(&rtc_list_lock);
}
static int
read_clocks(struct timespec *ts, bool debug_read)
{
struct rtc_instance *rtc;
int error;
error = ENXIO;
sx_xlock(&rtc_list_lock);
LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
if ((error = CLOCK_GETTIME(rtc->clockdev, ts)) != 0)
continue;
if (ts->tv_sec < 0 || ts->tv_nsec < 0) {
error = EINVAL;
continue;
}
if (!(rtc->flags & CLOCKF_GETTIME_NO_ADJ)) {
timespecadd(ts, &rtc->resadj);
ts->tv_sec += utc_offset();
}
if (!debug_read) {
if (bootverbose)
device_printf(rtc->clockdev,
"providing initial system time\n");
break;
}
}
sx_xunlock(&rtc_list_lock);
return (error);
}
/*
* Initialize the system time. Must be called from a context which does not
* restrict any locking or sleeping that clock drivers may need to do.
@ -296,28 +331,9 @@ void
inittodr(time_t base)
{
struct timespec ts;
struct rtc_instance *rtc;
int error;
error = ENXIO;
sx_xlock(&rtc_list_lock);
LIST_FOREACH(rtc, &rtc_list, rtc_entries) {
if ((error = CLOCK_GETTIME(rtc->clockdev, &ts)) != 0)
continue;
if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
error = EINVAL;
continue;
}
if (!(rtc->flags & CLOCKF_GETTIME_NO_ADJ)) {
timespecadd(&ts, &rtc->resadj);
ts.tv_sec += utc_offset();
}
if (bootverbose)
device_printf(rtc->clockdev,
"providing initial system time\n");
break;
}
sx_xunlock(&rtc_list_lock);
error = read_clocks(&ts, false);
/*
* Do not report errors from each clock; it is expected that some clocks
@ -380,3 +396,29 @@ resettodr(void)
}
sx_xunlock(&rtc_list_lock);
}
static int
sysctl_clock_do_io(SYSCTL_HANDLER_ARGS)
{
struct timespec ts_discard;
int error, value;
value = 0;
error = sysctl_handle_int(oidp, &value, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
switch (value) {
case CLOCK_DBG_READ:
if (read_clocks(&ts_discard, true) == ENXIO)
printf("No registered RTC clocks\n");
break;
case CLOCK_DBG_WRITE:
resettodr();
break;
default:
return (EINVAL);
}
return (0);
}