From owner-svn-src-stable-11@freebsd.org Mon Sep 11 17:32:29 2017 Return-Path: Delivered-To: svn-src-stable-11@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 0F853E186C4; Mon, 11 Sep 2017 17:32:29 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id B97C66A1CA; Mon, 11 Sep 2017 17:32:28 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v8BHWRjh046526; Mon, 11 Sep 2017 17:32:27 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v8BHWRgq046517; Mon, 11 Sep 2017 17:32:27 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201709111732.v8BHWRgq046517@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Mon, 11 Sep 2017 17:32:27 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r323447 - in stable/11: share/man/man9 sys/amd64/amd64 sys/isa sys/kern sys/sys sys/x86/isa X-SVN-Group: stable-11 X-SVN-Commit-Author: ian X-SVN-Commit-Paths: in stable/11: share/man/man9 sys/amd64/amd64 sys/isa sys/kern sys/sys sys/x86/isa X-SVN-Commit-Revision: 323447 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-11@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for only the 11-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 11 Sep 2017 17:32:29 -0000 Author: ian Date: Mon Sep 11 17:32:26 2017 New Revision: 323447 URL: https://svnweb.freebsd.org/changeset/base/323447 Log: MFC r320901-r320902, r320996-r320997, r321002, r321048, r321400, r321743, r321745 r320901: Protect access to the AT realtime clock with its own mutex. The mutex protecting access to the registered realtime clock should not be overloaded to protect access to the atrtc hardware, which might not even be the registered rtc. More importantly, the resettodr mutex needs to be eliminated to remove locking/sleeping restrictions on clock drivers, and that can't happen if MD code for amd64 depends on it. This change moves the protection into what's really being protected: access to the atrtc date and time registers. This change also adds protection when the clock is accessed from xentimer_settime(), which bypasses the resettodr locking. Differential Revision: https://reviews.freebsd.org/D11483 r320902: Support multiple realtime clocks, and remove locking/sleeping restrictions on clock drivers. This tracks multiple concurrent realtime clock drivers in a list sorted by clock resolution. When system time changes (and periodically) the clock_settime() methods of all registered clocks are invoked. To initialize system time, each driver is tried in turn from best to worst resolution, until one succesfully returns a valid time. The code no longer holds a mutex while calling the clock_settime() and clock_gettime() methods of the registered clocks. This allows clock drivers to do whatever kind of locking or sleeping is necessary (this is especially important for i2c clock chips since i2c drivers often need to sleep). A new clock_register_flags() function allows the clock driver to pass flags. The flags currently defined help support drivers that use their own techniques to avoid roundoff errors (prevents the 4/5 rounding done by the subr_rtc code). A driver which may need to wait for resources (such as bus ownership) may pass a flag to indicate that it will obtain system time for itself after waiting for resources; this is merely an optimization to avoid the common code retrieving a timespec that will never get used. Relnotes: yes Differential Revision: https://reviews.freebsd.org/D11484 r320996: Allow setting debug.clocktime as a tunable. Print 64-bit time_t correctly on 32-bit systems. r320997: Minor optimization: instead of converting between days and years using loops that start in 1970, assume most conversions are going to be for recent dates and use a precomputed number of days through the end of 2016. r321002: Revert r320997. There are reports of it getting the wrong results, so clearly my testing was insuffficent, and it's best to just revert it until I get it straightened out. r321048: Minor optimization: instead of converting between days and years using loops that start in 1970, assume most conversions are going to be for recent dates and use a precomputed number of days through the end of 2016. This is a do-over of r320997, hopefully this time with 100% more workiness. The first attempt had an off-by-one error, but instead of just adding another mysterious +1 adjustment, this rearranges the relationship between recent_base_year and recent_base_days so that the latter is the number of days that occurred before the start of the associated year (instead of the count thru the end of that year). This makes the recent_base stuff work more like the original loop logic that didn't need any +1 adjustments. r321400: Add common code to support realtime clocks that store year without century. Most realtime clocks store the year as 2 BCD digits. Some add a century bit to extend the range another hundred years. Every clock driver has its own code to determine the century and pass a full year value to clock_ct_to_ts(). Now clock drivers can just convert BCD to bin and store the result in the clocktime struct and let the common code figure out the century. Clocks with a century bit can just add 100 to year if the century bit is on. r321743: Add taskqueue_enqueue_timeout_sbt(), because sometimes you want more control over the scheduling precision than 'ticks' can offer, and because sometimes you're already working with sbintime_t units and it's dumb to convert them to ticks just so they can get converted back to sbintime_t under the hood. r321745: Add clock_schedule(), a feature that allows realtime clock drivers to request that their clock_settime() methods be called at a given offset from top-of-second. This adds a timeout_task to the rtc_instance so that each clock can be separately added to taskqueue_thread with the scheduling it prefers, instead of looping through all the clocks at once with a single task on taskqueue_thread. If a driver doesn't call clock_schedule() the default is the old behavior: clock_settime() is queued immediately. Modified: stable/11/share/man/man9/taskqueue.9 stable/11/sys/amd64/amd64/efirt.c stable/11/sys/isa/rtc.h stable/11/sys/kern/subr_clock.c stable/11/sys/kern/subr_rtc.c stable/11/sys/kern/subr_taskqueue.c stable/11/sys/sys/clock.h stable/11/sys/sys/taskqueue.h stable/11/sys/x86/isa/atrtc.c Directory Properties: stable/11/ (props changed) Modified: stable/11/share/man/man9/taskqueue.9 ============================================================================== --- stable/11/share/man/man9/taskqueue.9 Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/share/man/man9/taskqueue.9 Mon Sep 11 17:32:26 2017 (r323447) @@ -28,7 +28,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 1, 2016 +.Dd July 30, 2017 .Dt TASKQUEUE 9 .Os .Sh NAME @@ -82,6 +82,8 @@ struct timeout_task; .Ft int .Fn taskqueue_enqueue_timeout "struct taskqueue *queue" "struct timeout_task *timeout_task" "int ticks" .Ft int +.Fn taskqueue_enqueue_timeout_sbt "struct taskqueue *queue" "struct timeout_task *timeout_task" "sbintime_t sbt" "sbintime_t pr" "int flags" +.Ft int .Fn taskqueue_cancel "struct taskqueue *queue" "struct task *task" "u_int *pendp" .Ft int .Fn taskqueue_cancel_timeout "struct taskqueue *queue" "struct timeout_task *timeout_task" "u_int *pendp" @@ -211,8 +213,17 @@ is called on the task pointer passed to .Pp The .Fn taskqueue_enqueue_timeout -is used to schedule the enqueue after the specified amount of +function is used to schedule the enqueue after the specified number of .Va ticks . +The +.Fn taskqueue_enqueue_timeout_sbt +function provides finer control over the scheduling based on +.Va sbt , +.Va pr , +and +.Va flags , +as detailed in +.Xr timeout 9 . Only non-fast task queues can be used for .Va timeout_task scheduling. @@ -483,6 +494,7 @@ be created with a dedicated processing thread. .Xr ithread 9 , .Xr kthread 9 , .Xr swi 9 +.Xr timeout 9 .Sh HISTORY This interface first appeared in .Fx 5.0 . Modified: stable/11/sys/amd64/amd64/efirt.c ============================================================================== --- stable/11/sys/amd64/amd64/efirt.c Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/amd64/amd64/efirt.c Mon Sep 11 17:32:26 2017 (r323447) @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -444,7 +445,7 @@ efi_get_time_locked(struct efi_tm *tm) efi_status status; int error; - mtx_assert(&resettodr_lock, MA_OWNED); + mtx_assert(&atrtc_time_lock, MA_OWNED); error = efi_enter(); if (error != 0) return (error); @@ -461,9 +462,9 @@ efi_get_time(struct efi_tm *tm) if (efi_runtime == NULL) return (ENXIO); - mtx_lock(&resettodr_lock); + mtx_lock(&atrtc_time_lock); error = efi_get_time_locked(tm); - mtx_unlock(&resettodr_lock); + mtx_unlock(&atrtc_time_lock); return (error); } @@ -486,7 +487,7 @@ efi_set_time_locked(struct efi_tm *tm) efi_status status; int error; - mtx_assert(&resettodr_lock, MA_OWNED); + mtx_assert(&atrtc_time_lock, MA_OWNED); error = efi_enter(); if (error != 0) return (error); @@ -503,9 +504,9 @@ efi_set_time(struct efi_tm *tm) if (efi_runtime == NULL) return (ENXIO); - mtx_lock(&resettodr_lock); + mtx_lock(&atrtc_time_lock); error = efi_set_time_locked(tm); - mtx_unlock(&resettodr_lock); + mtx_unlock(&atrtc_time_lock); return (error); } Modified: stable/11/sys/isa/rtc.h ============================================================================== --- stable/11/sys/isa/rtc.h Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/isa/rtc.h Mon Sep 11 17:32:26 2017 (r323447) @@ -113,6 +113,7 @@ #ifdef _KERNEL extern struct mtx clock_lock; +extern struct mtx atrtc_time_lock; extern int atrtcclock_disable; int rtcin(int reg); void atrtc_restore(void); Modified: stable/11/sys/kern/subr_clock.c ============================================================================== --- stable/11/sys/kern/subr_clock.c Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/kern/subr_clock.c Mon Sep 11 17:32:26 2017 (r323447) @@ -72,7 +72,7 @@ SYSCTL_PROC(_machdep, OID_AUTO, adjkerntz, CTLTYPE_INT "Local offset from UTC in seconds"); static int ct_debug; -SYSCTL_INT(_debug, OID_AUTO, clocktime, CTLFLAG_RW, +SYSCTL_INT(_debug, OID_AUTO, clocktime, CTLFLAG_RWTUN, &ct_debug, 0, "Enable printing of clocktime debugging"); static int wall_cmos_clock; @@ -97,6 +97,13 @@ static const int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +/* + * Optimization: using a precomputed count of days between POSIX_BASE_YEAR and + * some recent year avoids lots of unnecessary loop iterations in conversion. + * recent_base_days is the number of days before the start of recent_base_year. + */ +static const int recent_base_year = 2017; +static const int recent_base_days = 17167; /* * This inline avoids some unnecessary modulo operations @@ -135,18 +142,27 @@ clock_ct_to_ts(struct clocktime *ct, struct timespec * { int i, year, days; - year = ct->year; - if (ct_debug) { printf("ct_to_ts("); print_ct(ct); printf(")"); } + /* + * Many realtime clocks store the year as 2-digit BCD; pivot on 70 to + * determine century. Some clocks have a "century bit" and drivers do + * year += 100, so interpret values between 70-199 as relative to 1900. + */ + year = ct->year; + if (year < 70) + year += 2000; + else if (year < 200) + year += 1900; + /* Sanity checks. */ if (ct->mon < 1 || ct->mon > 12 || ct->day < 1 || ct->day > days_in_month(year, ct->mon) || - ct->hour > 23 || ct->min > 59 || ct->sec > 59 || + ct->hour > 23 || ct->min > 59 || ct->sec > 59 || year < 1970 || (sizeof(time_t) == 4 && year > 2037)) { /* time_t overflow */ if (ct_debug) printf(" = EINVAL\n"); @@ -157,8 +173,14 @@ clock_ct_to_ts(struct clocktime *ct, struct timespec * * Compute days since start of time * First from years, then from months. */ - days = 0; - for (i = POSIX_BASE_YEAR; i < year; i++) + if (year >= recent_base_year) { + i = recent_base_year; + days = recent_base_days; + } else { + i = POSIX_BASE_YEAR; + days = 0; + } + for (; i < year; i++) days += days_in_year(i); /* Months */ @@ -171,7 +193,7 @@ clock_ct_to_ts(struct clocktime *ct, struct timespec * ts->tv_nsec = ct->nsec; if (ct_debug) - printf(" = %ld.%09ld\n", (long)ts->tv_sec, (long)ts->tv_nsec); + printf(" = %jd.%09ld\n", (intmax_t)ts->tv_sec, ts->tv_nsec); return (0); } @@ -188,8 +210,14 @@ clock_ts_to_ct(struct timespec *ts, struct clocktime * ct->dow = day_of_week(days); - /* Subtract out whole years, counting them in i. */ - for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++) + /* Subtract out whole years. */ + if (days >= recent_base_days) { + year = recent_base_year; + days -= recent_base_days; + } else { + year = POSIX_BASE_YEAR; + } + for (; days >= days_in_year(year); year++) days -= days_in_year(year); ct->year = year; @@ -209,8 +237,8 @@ clock_ts_to_ct(struct timespec *ts, struct clocktime * ct->sec = rsec; ct->nsec = ts->tv_nsec; if (ct_debug) { - printf("ts_to_ct(%ld.%09ld) = ", - (long)ts->tv_sec, (long)ts->tv_nsec); + printf("ts_to_ct(%jd.%09ld) = ", + (intmax_t)ts->tv_sec, ts->tv_nsec); print_ct(ct); printf("\n"); } Modified: stable/11/sys/kern/subr_rtc.c ============================================================================== --- stable/11/sys/kern/subr_rtc.c Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/kern/subr_rtc.c Mon Sep 11 17:32:26 2017 (r323447) @@ -63,8 +63,10 @@ __FBSDID("$FreeBSD$"); #include #include #include -#include +#include +#include #include +#include #ifdef FFCLOCK #include #endif @@ -72,116 +74,249 @@ __FBSDID("$FreeBSD$"); #include "clock_if.h" -static device_t clock_dev = NULL; -static long clock_res; -static struct timespec clock_adj; -struct mtx resettodr_lock; -MTX_SYSINIT(resettodr_init, &resettodr_lock, "tod2rl", MTX_DEF); - /* 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, 0, "Disallow adjusting time-of-day clock"); +/* + * An instance of a realtime clock. A list of these tracks all the registered + * clocks in the system. + * + * The resadj member is used to apply a "resolution adjustment" equal to half + * the clock's resolution, which is useful mainly on clocks with a whole-second + * resolution. Because the clock truncates the fractional part, adding half the + * resolution performs 4/5 rounding. The same adjustment is applied to the + * times returned from clock_gettime(), because the fraction returned will + * always be zero, but on average the actual fraction at the time of the call + * should be about .5. + */ +struct rtc_instance { + device_t clockdev; + int resolution; + int flags; + u_int schedns; + struct timespec resadj; + struct timeout_task + stask; + LIST_ENTRY(rtc_instance) + rtc_entries; +}; + +/* + * Clocks are updated using a task running on taskqueue_thread. + */ +static void settime_task_func(void *arg, int pending); + +/* + * Registered clocks are kept in a list which is sorted by resolution; the more + * accurate clocks get the first shot at providing the time. + */ +LIST_HEAD(rtc_listhead, rtc_instance); +static struct rtc_listhead rtc_list = LIST_HEAD_INITIALIZER(rtc_list); +static struct sx rtc_list_lock; +SX_SYSINIT(rtc_list_lock_init, &rtc_list_lock, "rtc list"); + +/* + * On the task thread, invoke the clock_settime() method of the clock. Do so + * holding no locks, so that clock drivers are free to do whatever kind of + * locking or sleeping they need to. + */ +static void +settime_task_func(void *arg, int pending) +{ + struct timespec ts; + struct rtc_instance *rtc; + + rtc = arg; + if (!(rtc->flags & CLOCKF_SETTIME_NO_TS)) { + getnanotime(&ts); + if (!(rtc->flags & CLOCKF_SETTIME_NO_ADJ)) { + ts.tv_sec -= utc_offset(); + timespecadd(&ts, &rtc->resadj); + } + } else { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + CLOCK_SETTIME(rtc->clockdev, &ts); +} + void -clock_register(device_t dev, long res) /* res has units of microseconds */ +clock_register_flags(device_t clockdev, long resolution, int flags) { + struct rtc_instance *rtc, *newrtc; - if (clock_dev != NULL) { - if (clock_res <= res) { - if (bootverbose) - device_printf(dev, "not installed as " - "time-of-day clock: clock %s has higher " - "resolution\n", device_get_name(clock_dev)); - return; + newrtc = malloc(sizeof(*newrtc), M_DEVBUF, M_WAITOK); + newrtc->clockdev = clockdev; + newrtc->resolution = (int)resolution; + newrtc->flags = flags; + newrtc->schedns = 0; + newrtc->resadj.tv_sec = newrtc->resolution / 2 / 1000000; + newrtc->resadj.tv_nsec = newrtc->resolution / 2 % 1000000 * 1000; + TIMEOUT_TASK_INIT(taskqueue_thread, &newrtc->stask, 0, + settime_task_func, newrtc); + + sx_xlock(&rtc_list_lock); + if (LIST_EMPTY(&rtc_list)) { + LIST_INSERT_HEAD(&rtc_list, newrtc, rtc_entries); + } else { + LIST_FOREACH(rtc, &rtc_list, rtc_entries) { + if (rtc->resolution > newrtc->resolution) { + LIST_INSERT_BEFORE(rtc, newrtc, rtc_entries); + break; + } else if (LIST_NEXT(rtc, rtc_entries) == NULL) { + LIST_INSERT_AFTER(rtc, newrtc, rtc_entries); + break; + } } - if (bootverbose) - device_printf(clock_dev, "removed as " - "time-of-day clock: clock %s has higher " - "resolution\n", device_get_name(dev)); } - clock_dev = dev; - clock_res = res; - clock_adj.tv_sec = res / 2 / 1000000; - clock_adj.tv_nsec = res / 2 % 1000000 * 1000; - if (bootverbose) - device_printf(dev, "registered as a time-of-day clock " - "(resolution %ldus, adjustment %jd.%09jds)\n", res, - (intmax_t)clock_adj.tv_sec, (intmax_t)clock_adj.tv_nsec); + sx_xunlock(&rtc_list_lock); + + device_printf(clockdev, + "registered as a time-of-day clock, resolution %d.%6.6ds\n", + newrtc->resolution / 1000000, newrtc->resolution % 1000000); } -/* - * inittodr and settodr derived from the i386 versions written - * by Christoph Robitschko , reintroduced and - * updated by Chris Stenton 8/10/94 - */ +void +clock_register(device_t dev, long res) +{ + clock_register_flags(dev, res, 0); +} + +void +clock_unregister(device_t clockdev) +{ + struct rtc_instance *rtc, *tmp; + + sx_xlock(&rtc_list_lock); + LIST_FOREACH_SAFE(rtc, &rtc_list, rtc_entries, tmp) { + if (rtc->clockdev == clockdev) { + LIST_REMOVE(rtc, rtc_entries); + break; + } + } + sx_xunlock(&rtc_list_lock); + if (rtc != NULL) { + taskqueue_cancel_timeout(taskqueue_thread, &rtc->stask, NULL); + taskqueue_drain_timeout(taskqueue_thread, &rtc->stask); + free(rtc, M_DEVBUF); + } +} + +void +clock_schedule(device_t clockdev, u_int offsetns) +{ + struct rtc_instance *rtc; + + sx_xlock(&rtc_list_lock); + LIST_FOREACH(rtc, &rtc_list, rtc_entries) { + if (rtc->clockdev == clockdev) { + rtc->schedns = offsetns; + break; + } + } + sx_xunlock(&rtc_list_lock); +} + /* - * Initialize the time of day register, based on the time base which is, e.g. - * from a filesystem. + * 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. + * + * First attempt to get the time from a registered realtime clock. The clocks + * are queried in order of resolution until one provides the time. If no clock + * can provide the current time, use the 'base' time provided by the caller, if + * non-zero. The 'base' time is potentially highly inaccurate, such as the last + * known good value of the system clock, or even a filesystem last-updated + * timestamp. It is used to prevent system time from appearing to move + * backwards in logs. */ void inittodr(time_t base) { struct timespec ts; + struct rtc_instance *rtc; int error; - if (clock_dev == NULL) { - printf("warning: no time-of-day clock registered, system time " - "will not be set accurately\n"); - goto wrong_time; + 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; } - /* XXX: We should poll all registered RTCs in case of failure */ - mtx_lock(&resettodr_lock); - error = CLOCK_GETTIME(clock_dev, &ts); - mtx_unlock(&resettodr_lock); - if (error != 0 && error != EINVAL) { - printf("warning: clock_gettime failed (%d), the system time " - "will not be set accurately\n", error); - goto wrong_time; + sx_xunlock(&rtc_list_lock); + + /* + * Do not report errors from each clock; it is expected that some clocks + * cannot provide results in some situations. Only report problems when + * no clocks could provide the time. + */ + if (error != 0) { + switch (error) { + case ENXIO: + printf("Warning: no time-of-day clock registered, "); + break; + case EINVAL: + printf("Warning: bad time from time-of-day clock, "); + break; + default: + printf("Error reading time-of-day clock (%d), ", error); + break; + } + printf("system time will not be set accurately\n"); + ts.tv_sec = (base > 0) ? base : -1; + ts.tv_nsec = 0; } - if (error == EINVAL || ts.tv_sec < 0) { - printf("Invalid time in real time clock.\n" - "Check and reset the date immediately!\n"); - goto wrong_time; - } - ts.tv_sec += utc_offset(); - timespecadd(&ts, &clock_adj); - tc_setclock(&ts); + if (ts.tv_sec >= 0) { + tc_setclock(&ts); #ifdef FFCLOCK - ffclock_reset_clock(&ts); + ffclock_reset_clock(&ts); #endif - return; - -wrong_time: - if (base > 0) { - ts.tv_sec = base; - ts.tv_nsec = 0; - tc_setclock(&ts); } } /* - * Write system time back to RTC + * Write system time back to all registered clocks, unless disabled by admin. + * This can be called from a context that restricts locking and/or sleeping; the + * actual updating is done asynchronously on a task thread. */ void resettodr(void) { - struct timespec ts; - int error; + struct timespec now; + struct rtc_instance *rtc; + sbintime_t sbt; + long waitns; - if (disable_rtc_set || clock_dev == NULL) + if (disable_rtc_set) return; - getnanotime(&ts); - timespecadd(&ts, &clock_adj); - ts.tv_sec -= utc_offset(); - /* XXX: We should really set all registered RTCs */ - mtx_lock(&resettodr_lock); - error = CLOCK_SETTIME(clock_dev, &ts); - mtx_unlock(&resettodr_lock); - if (error != 0) - printf("warning: clock_settime failed (%d), time-of-day clock " - "not adjusted to system time\n", error); + sx_xlock(&rtc_list_lock); + LIST_FOREACH(rtc, &rtc_list, rtc_entries) { + if (rtc->schedns != 0) { + getnanotime(&now); + waitns = rtc->schedns - now.tv_nsec; + if (waitns < 0) + waitns += 1000000000; + sbt = nstosbt(waitns); + } else + sbt = 0; + taskqueue_enqueue_timeout_sbt(taskqueue_thread, + &rtc->stask, -sbt, 0, C_PREL(31)); + } + sx_xunlock(&rtc_list_lock); } Modified: stable/11/sys/kern/subr_taskqueue.c ============================================================================== --- stable/11/sys/kern/subr_taskqueue.c Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/kern/subr_taskqueue.c Mon Sep 11 17:32:26 2017 (r323447) @@ -289,8 +289,8 @@ taskqueue_timeout_func(void *arg) } int -taskqueue_enqueue_timeout(struct taskqueue *queue, - struct timeout_task *timeout_task, int ticks) +taskqueue_enqueue_timeout_sbt(struct taskqueue *queue, + struct timeout_task *timeout_task, sbintime_t sbt, sbintime_t pr, int flags) { int res; @@ -304,7 +304,7 @@ taskqueue_enqueue_timeout(struct taskqueue *queue, /* Do nothing */ TQ_UNLOCK(queue); res = -1; - } else if (ticks == 0) { + } else if (sbt == 0) { taskqueue_enqueue_locked(queue, &timeout_task->t); /* The lock is released inside. */ } else { @@ -313,16 +313,25 @@ taskqueue_enqueue_timeout(struct taskqueue *queue, } else { queue->tq_callouts++; timeout_task->f |= DT_CALLOUT_ARMED; - if (ticks < 0) - ticks = -ticks; /* Ignore overflow. */ + if (sbt < 0) + sbt = -sbt; /* Ignore overflow. */ } - if (ticks > 0) { - callout_reset(&timeout_task->c, ticks, - taskqueue_timeout_func, timeout_task); + if (sbt > 0) { + callout_reset_sbt(&timeout_task->c, sbt, pr, + taskqueue_timeout_func, timeout_task, flags); } TQ_UNLOCK(queue); } return (res); +} + +int +taskqueue_enqueue_timeout(struct taskqueue *queue, + struct timeout_task *ttask, int ticks) +{ + + return (taskqueue_enqueue_timeout_sbt(queue, ttask, ticks * tick_sbt, + 0, 0)); } static void Modified: stable/11/sys/sys/clock.h ============================================================================== --- stable/11/sys/sys/clock.h Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/sys/clock.h Mon Sep 11 17:32:26 2017 (r323447) @@ -54,7 +54,6 @@ */ extern int tz_minuteswest; extern int tz_dsttime; -extern struct mtx resettodr_lock; int utc_offset(void); @@ -76,7 +75,42 @@ struct clocktime { int clock_ct_to_ts(struct clocktime *, struct timespec *); void clock_ts_to_ct(struct timespec *, struct clocktime *); -void clock_register(device_t, long); + +/* + * Time-of-day clock functions and flags. These functions might sleep. + * + * clock_register and clock_unregister() do what they say. Upon return from + * unregister, the clock's methods are not running and will not be called again. + * + * clock_schedule() requests that a registered clock's clock_settime() calls + * happen at the given offset into the second. The default is 0, meaning no + * specific scheduling. To schedule the call as soon after top-of-second as + * possible, specify 1. Each clock has its own schedule, but taskqueue_thread + * is shared by many tasks; the timing of the call is not guaranteed. + * + * Flags: + * + * CLOCKF_SETTIME_NO_TS + * Do not pass a timespec to clock_settime(), the driver obtains its own time + * and applies its own adjustments (this flag implies CLOCKF_SETTIME_NO_ADJ). + * + * CLOCKF_SETTIME_NO_ADJ + * Do not apply utc offset and resolution/accuracy adjustments to the value + * passed to clock_settime(), the driver applies them itself. + * + * CLOCKF_GETTIME_NO_ADJ + * Do not apply utc offset and resolution/accuracy adjustments to the value + * returned from clock_gettime(), the driver has already applied them. + */ + +#define CLOCKF_SETTIME_NO_TS 0x00000001 +#define CLOCKF_SETTIME_NO_ADJ 0x00000002 +#define CLOCKF_GETTIME_NO_ADJ 0x00000004 + +void clock_register(device_t _clockdev, long _resolution_us); +void clock_register_flags(device_t _clockdev, long _resolution_us, int _flags); +void clock_schedule(device_t clockdev, u_int _offsetns); +void clock_unregister(device_t _clockdev); /* * BCD to decimal and decimal to BCD. Modified: stable/11/sys/sys/taskqueue.h ============================================================================== --- stable/11/sys/sys/taskqueue.h Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/sys/taskqueue.h Mon Sep 11 17:32:26 2017 (r323447) @@ -79,6 +79,9 @@ int taskqueue_start_threads_cpuset(struct taskqueue ** int taskqueue_enqueue(struct taskqueue *queue, struct task *task); int taskqueue_enqueue_timeout(struct taskqueue *queue, struct timeout_task *timeout_task, int ticks); +int taskqueue_enqueue_timeout_sbt(struct taskqueue *queue, + struct timeout_task *timeout_task, sbintime_t sbt, sbintime_t pr, + int flags); int taskqueue_poll_is_busy(struct taskqueue *queue, struct task *task); int taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp); Modified: stable/11/sys/x86/isa/atrtc.c ============================================================================== --- stable/11/sys/x86/isa/atrtc.c Mon Sep 11 17:01:26 2017 (r323446) +++ stable/11/sys/x86/isa/atrtc.c Mon Sep 11 17:32:26 2017 (r323447) @@ -53,9 +53,17 @@ __FBSDID("$FreeBSD$"); #include #include "clock_if.h" +/* + * clock_lock protects low-level access to individual hardware registers. + * atrtc_time_lock protects the entire sequence of accessing multiple registers + * to read or write the date and time. + */ #define RTC_LOCK do { if (!kdb_active) mtx_lock_spin(&clock_lock); } while (0) #define RTC_UNLOCK do { if (!kdb_active) mtx_unlock_spin(&clock_lock); } while (0) +struct mtx atrtc_time_lock; +MTX_SYSINIT(atrtc_lock_init, &atrtc_time_lock, "atrtc", MTX_DEF); + int atrtcclock_disable = 0; static int rtc_reg = -1; @@ -158,6 +166,8 @@ atrtc_set(struct timespec *ts) clock_ts_to_ct(ts, &ct); + mtx_lock(&atrtc_time_lock); + /* Disable RTC updates and interrupts. */ writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); @@ -176,6 +186,8 @@ atrtc_set(struct timespec *ts) /* Re-enable RTC updates and interrupts. */ writertc(RTC_STATUSB, rtc_statusb); rtcin(RTC_INTR); + + mtx_unlock(&atrtc_time_lock); } /********************************************************************** @@ -347,6 +359,7 @@ atrtc_gettime(device_t dev, struct timespec *ts) * to make sure that no more than 240us pass after we start reading, * and try again if so. */ + mtx_lock(&atrtc_time_lock); while (rtcin(RTC_STATUSA) & RTCSA_TUP) continue; critical_enter(); @@ -364,6 +377,7 @@ atrtc_gettime(device_t dev, struct timespec *ts) ct.year += (ct.year < 80 ? 2000 : 1900); #endif critical_exit(); + mtx_unlock(&atrtc_time_lock); /* Set dow = -1 because some clocks don't set it correctly. */ ct.dow = -1; return (clock_ct_to_ts(&ct, ts));