Przeglądaj źródła

Merge branch 'fortglx/3.9/time' of git://git.linaro.org/people/jstultz/linux into timers/core

Trivial conflict in arch/x86/Kconfig

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Thomas Gleixner 12 lat temu
rodzic
commit
90889a635a

+ 1 - 1
MAINTAINERS

@@ -6598,7 +6598,7 @@ F:	drivers/dma/dw_dmac_regs.h
 F:	drivers/dma/dw_dmac.c
 F:	drivers/dma/dw_dmac.c
 
 
 TIMEKEEPING, NTP
 TIMEKEEPING, NTP
-M:	John Stultz <johnstul@us.ibm.com>
+M:	John Stultz <john.stultz@linaro.org>
 M:	Thomas Gleixner <tglx@linutronix.de>
 M:	Thomas Gleixner <tglx@linutronix.de>
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
 S:	Supported
 S:	Supported

+ 1 - 1
arch/powerpc/kernel/time.c

@@ -668,7 +668,7 @@ int update_persistent_clock(struct timespec now)
 	struct rtc_time tm;
 	struct rtc_time tm;
 
 
 	if (!ppc_md.set_rtc_time)
 	if (!ppc_md.set_rtc_time)
-		return 0;
+		return -ENODEV;
 
 
 	to_tm(now.tv_sec + 1 + timezone_offset, &tm);
 	to_tm(now.tv_sec + 1 + timezone_offset, &tm);
 	tm.tm_year -= 1900;
 	tm.tm_year -= 1900;

+ 1 - 0
arch/x86/Kconfig

@@ -106,6 +106,7 @@ config X86
 	select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC)
 	select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC)
 	select GENERIC_TIME_VSYSCALL if X86_64
 	select GENERIC_TIME_VSYSCALL if X86_64
 	select KTIME_SCALAR if X86_32
 	select KTIME_SCALAR if X86_32
+	select ALWAYS_USE_PERSISTENT_CLOCK
 	select GENERIC_STRNCPY_FROM_USER
 	select GENERIC_STRNCPY_FROM_USER
 	select GENERIC_STRNLEN_USER
 	select GENERIC_STRNLEN_USER
 	select HAVE_CONTEXT_TRACKING if X86_64
 	select HAVE_CONTEXT_TRACKING if X86_64

+ 2 - 1
arch/x86/kernel/tsc.c

@@ -623,7 +623,8 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
 	ns_now = __cycles_2_ns(tsc_now);
 	ns_now = __cycles_2_ns(tsc_now);
 
 
 	if (cpu_khz) {
 	if (cpu_khz) {
-		*scale = (NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR)/cpu_khz;
+		*scale = ((NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR) +
+				cpu_khz / 2) / cpu_khz;
 		*offset = ns_now - mult_frac(tsc_now, *scale,
 		*offset = ns_now - mult_frac(tsc_now, *scale,
 					     (1UL << CYC2NS_SCALE_FACTOR));
 					     (1UL << CYC2NS_SCALE_FACTOR));
 	}
 	}

+ 11 - 1
drivers/rtc/Kconfig

@@ -20,14 +20,24 @@ if RTC_CLASS
 config RTC_HCTOSYS
 config RTC_HCTOSYS
 	bool "Set system time from RTC on startup and resume"
 	bool "Set system time from RTC on startup and resume"
 	default y
 	default y
+	depends on !ALWAYS_USE_PERSISTENT_CLOCK
 	help
 	help
 	  If you say yes here, the system time (wall clock) will be set using
 	  If you say yes here, the system time (wall clock) will be set using
 	  the value read from a specified RTC device. This is useful to avoid
 	  the value read from a specified RTC device. This is useful to avoid
 	  unnecessary fsck runs at boot time, and to network better.
 	  unnecessary fsck runs at boot time, and to network better.
 
 
+config RTC_SYSTOHC
+	bool "Set the RTC time based on NTP synchronization"
+	default y
+	depends on !ALWAYS_USE_PERSISTENT_CLOCK
+	help
+	  If you say yes here, the system time (wall clock) will be stored
+	  in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11
+	  minutes if userspace reports synchronized NTP status.
+
 config RTC_HCTOSYS_DEVICE
 config RTC_HCTOSYS_DEVICE
 	string "RTC used to set the system time"
 	string "RTC used to set the system time"
-	depends on RTC_HCTOSYS = y
+	depends on RTC_HCTOSYS = y || RTC_SYSTOHC = y
 	default "rtc0"
 	default "rtc0"
 	help
 	help
 	  The RTC device that will be used to (re)initialize the system
 	  The RTC device that will be used to (re)initialize the system

+ 1 - 0
drivers/rtc/Makefile

@@ -6,6 +6,7 @@ ccflags-$(CONFIG_RTC_DEBUG)	:= -DDEBUG
 
 
 obj-$(CONFIG_RTC_LIB)		+= rtc-lib.o
 obj-$(CONFIG_RTC_LIB)		+= rtc-lib.o
 obj-$(CONFIG_RTC_HCTOSYS)	+= hctosys.o
 obj-$(CONFIG_RTC_HCTOSYS)	+= hctosys.o
+obj-$(CONFIG_RTC_SYSTOHC)	+= systohc.o
 obj-$(CONFIG_RTC_CLASS)		+= rtc-core.o
 obj-$(CONFIG_RTC_CLASS)		+= rtc-core.o
 rtc-core-y			:= class.o interface.o
 rtc-core-y			:= class.o interface.o
 
 

+ 7 - 0
drivers/rtc/class.c

@@ -50,6 +50,10 @@ static int rtc_suspend(struct device *dev, pm_message_t mesg)
 	struct rtc_device	*rtc = to_rtc_device(dev);
 	struct rtc_device	*rtc = to_rtc_device(dev);
 	struct rtc_time		tm;
 	struct rtc_time		tm;
 	struct timespec		delta, delta_delta;
 	struct timespec		delta, delta_delta;
+
+	if (has_persistent_clock())
+		return 0;
+
 	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
 	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
 		return 0;
 		return 0;
 
 
@@ -88,6 +92,9 @@ static int rtc_resume(struct device *dev)
 	struct timespec		new_system, new_rtc;
 	struct timespec		new_system, new_rtc;
 	struct timespec		sleep_time;
 	struct timespec		sleep_time;
 
 
+	if (has_persistent_clock())
+		return 0;
+
 	rtc_hctosys_ret = -ENODEV;
 	rtc_hctosys_ret = -ENODEV;
 	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
 	if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
 		return 0;
 		return 0;

+ 44 - 0
drivers/rtc/systohc.c

@@ -0,0 +1,44 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+/**
+ * rtc_set_ntp_time - Save NTP synchronized time to the RTC
+ * @now: Current time of day
+ *
+ * Replacement for the NTP platform function update_persistent_clock
+ * that stores time for later retrieval by rtc_hctosys.
+ *
+ * Returns 0 on successful RTC update, -ENODEV if a RTC update is not
+ * possible at all, and various other -errno for specific temporary failure
+ * cases.
+ *
+ * If temporary failure is indicated the caller should try again 'soon'
+ */
+int rtc_set_ntp_time(struct timespec now)
+{
+	struct rtc_device *rtc;
+	struct rtc_time tm;
+	int err = -ENODEV;
+
+	if (now.tv_nsec < (NSEC_PER_SEC >> 1))
+		rtc_time_to_tm(now.tv_sec, &tm);
+	else
+		rtc_time_to_tm(now.tv_sec + 1, &tm);
+
+	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+	if (rtc) {
+		/* rtc_hctosys exclusively uses UTC, so we call set_time here,
+		 * not set_mmss. */
+		if (rtc->ops && (rtc->ops->set_time || rtc->ops->set_mmss))
+			err = rtc_set_time(rtc, &tm);
+		rtc_class_close(rtc);
+	}
+
+	return err;
+}

+ 7 - 3
fs/pstore/ram.c

@@ -167,12 +167,16 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
 static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
 {
 {
 	char *hdr;
 	char *hdr;
-	struct timeval timestamp;
+	struct timespec timestamp;
 	size_t len;
 	size_t len;
 
 
-	do_gettimeofday(&timestamp);
+	/* Report zeroed timestamp if called before timekeeping has resumed. */
+	if (__getnstimeofday(&timestamp)) {
+		timestamp.tv_sec = 0;
+		timestamp.tv_nsec = 0;
+	}
 	hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
 	hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
-		(long)timestamp.tv_sec, (long)timestamp.tv_usec);
+		(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000));
 	WARN_ON_ONCE(!hdr);
 	WARN_ON_ONCE(!hdr);
 	len = hdr ? strlen(hdr) : 0;
 	len = hdr ? strlen(hdr) : 0;
 	persistent_ram_write(prz, hdr, len);
 	persistent_ram_write(prz, hdr, len);

+ 1 - 0
include/linux/rtc.h

@@ -138,6 +138,7 @@ extern void rtc_device_unregister(struct rtc_device *rtc);
 extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
 extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm);
 extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
 extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm);
 extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs);
 extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs);
+extern int rtc_set_ntp_time(struct timespec now);
 int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);
 int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm);
 extern int rtc_read_alarm(struct rtc_device *rtc,
 extern int rtc_read_alarm(struct rtc_device *rtc,
 			struct rtc_wkalrm *alrm);
 			struct rtc_wkalrm *alrm);

+ 12 - 0
include/linux/time.h

@@ -115,6 +115,17 @@ static inline bool timespec_valid_strict(const struct timespec *ts)
 	return true;
 	return true;
 }
 }
 
 
+extern bool persistent_clock_exist;
+
+#ifdef ALWAYS_USE_PERSISTENT_CLOCK
+#define has_persistent_clock()	true
+#else
+static inline bool has_persistent_clock(void)
+{
+	return persistent_clock_exist;
+}
+#endif
+
 extern void read_persistent_clock(struct timespec *ts);
 extern void read_persistent_clock(struct timespec *ts);
 extern void read_boot_clock(struct timespec *ts);
 extern void read_boot_clock(struct timespec *ts);
 extern int update_persistent_clock(struct timespec now);
 extern int update_persistent_clock(struct timespec now);
@@ -158,6 +169,7 @@ extern int do_setitimer(int which, struct itimerval *value,
 			struct itimerval *ovalue);
 			struct itimerval *ovalue);
 extern unsigned int alarm_setitimer(unsigned int seconds);
 extern unsigned int alarm_setitimer(unsigned int seconds);
 extern int do_getitimer(int which, struct itimerval *value);
 extern int do_getitimer(int which, struct itimerval *value);
+extern int __getnstimeofday(struct timespec *tv);
 extern void getnstimeofday(struct timespec *tv);
 extern void getnstimeofday(struct timespec *tv);
 extern void getrawmonotonic(struct timespec *ts);
 extern void getrawmonotonic(struct timespec *ts);
 extern void getnstime_raw_and_real(struct timespec *ts_raw,
 extern void getnstime_raw_and_real(struct timespec *ts_raw,

+ 1 - 1
kernel/posix-timers.c

@@ -997,7 +997,7 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock,
 
 
 	err = kc->clock_adj(which_clock, &ktx);
 	err = kc->clock_adj(which_clock, &ktx);
 
 
-	if (!err && copy_to_user(utx, &ktx, sizeof(ktx)))
+	if (err >= 0 && copy_to_user(utx, &ktx, sizeof(ktx)))
 		return -EFAULT;
 		return -EFAULT;
 
 
 	return err;
 	return err;

+ 5 - 0
kernel/time/Kconfig

@@ -12,6 +12,11 @@ config CLOCKSOURCE_WATCHDOG
 config ARCH_CLOCKSOURCE_DATA
 config ARCH_CLOCKSOURCE_DATA
 	bool
 	bool
 
 
+# Platforms has a persistent clock
+config ALWAYS_USE_PERSISTENT_CLOCK
+	bool
+	default n
+
 # Timekeeping vsyscall support
 # Timekeeping vsyscall support
 config GENERIC_TIME_VSYSCALL
 config GENERIC_TIME_VSYSCALL
 	bool
 	bool

+ 12 - 4
kernel/time/ntp.c

@@ -15,6 +15,7 @@
 #include <linux/time.h>
 #include <linux/time.h>
 #include <linux/mm.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/rtc.h>
 
 
 #include "tick-internal.h"
 #include "tick-internal.h"
 
 
@@ -483,8 +484,7 @@ out:
 	return leap;
 	return leap;
 }
 }
 
 
-#ifdef CONFIG_GENERIC_CMOS_UPDATE
-
+#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
 static void sync_cmos_clock(struct work_struct *work);
 static void sync_cmos_clock(struct work_struct *work);
 
 
 static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
 static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock);
@@ -510,14 +510,22 @@ static void sync_cmos_clock(struct work_struct *work)
 	}
 	}
 
 
 	getnstimeofday(&now);
 	getnstimeofday(&now);
-	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2)
+	if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) {
+		fail = -ENODEV;
+#ifdef CONFIG_GENERIC_CMOS_UPDATE
 		fail = update_persistent_clock(now);
 		fail = update_persistent_clock(now);
+#endif
+#ifdef CONFIG_RTC_SYSTOHC
+		if (fail == -ENODEV)
+			fail = rtc_set_ntp_time(now);
+#endif
+	}
 
 
 	next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
 	next.tv_nsec = (NSEC_PER_SEC / 2) - now.tv_nsec - (TICK_NSEC / 2);
 	if (next.tv_nsec <= 0)
 	if (next.tv_nsec <= 0)
 		next.tv_nsec += NSEC_PER_SEC;
 		next.tv_nsec += NSEC_PER_SEC;
 
 
-	if (!fail)
+	if (!fail || fail == -ENODEV)
 		next.tv_sec = 659;
 		next.tv_sec = 659;
 	else
 	else
 		next.tv_sec = 0;
 		next.tv_sec = 0;

+ 35 - 10
kernel/time/timekeeping.c

@@ -29,6 +29,9 @@ static struct timekeeper timekeeper;
 /* flag for if timekeeping is suspended */
 /* flag for if timekeeping is suspended */
 int __read_mostly timekeeping_suspended;
 int __read_mostly timekeeping_suspended;
 
 
+/* Flag for if there is a persistent clock on this platform */
+bool __read_mostly persistent_clock_exist = false;
+
 static inline void tk_normalize_xtime(struct timekeeper *tk)
 static inline void tk_normalize_xtime(struct timekeeper *tk)
 {
 {
 	while (tk->xtime_nsec >= ((u64)NSEC_PER_SEC << tk->shift)) {
 	while (tk->xtime_nsec >= ((u64)NSEC_PER_SEC << tk->shift)) {
@@ -264,19 +267,18 @@ static void timekeeping_forward_now(struct timekeeper *tk)
 }
 }
 
 
 /**
 /**
- * getnstimeofday - Returns the time of day in a timespec
+ * __getnstimeofday - Returns the time of day in a timespec.
  * @ts:		pointer to the timespec to be set
  * @ts:		pointer to the timespec to be set
  *
  *
- * Returns the time of day in a timespec.
+ * Updates the time of day in the timespec.
+ * Returns 0 on success, or -ve when suspended (timespec will be undefined).
  */
  */
-void getnstimeofday(struct timespec *ts)
+int __getnstimeofday(struct timespec *ts)
 {
 {
 	struct timekeeper *tk = &timekeeper;
 	struct timekeeper *tk = &timekeeper;
 	unsigned long seq;
 	unsigned long seq;
 	s64 nsecs = 0;
 	s64 nsecs = 0;
 
 
-	WARN_ON(timekeeping_suspended);
-
 	do {
 	do {
 		seq = read_seqbegin(&tk->lock);
 		seq = read_seqbegin(&tk->lock);
 
 
@@ -287,6 +289,26 @@ void getnstimeofday(struct timespec *ts)
 
 
 	ts->tv_nsec = 0;
 	ts->tv_nsec = 0;
 	timespec_add_ns(ts, nsecs);
 	timespec_add_ns(ts, nsecs);
+
+	/*
+	 * Do not bail out early, in case there were callers still using
+	 * the value, even in the face of the WARN_ON.
+	 */
+	if (unlikely(timekeeping_suspended))
+		return -EAGAIN;
+	return 0;
+}
+EXPORT_SYMBOL(__getnstimeofday);
+
+/**
+ * getnstimeofday - Returns the time of day in a timespec.
+ * @ts:		pointer to the timespec to be set
+ *
+ * Returns the time of day in a timespec (WARN if suspended).
+ */
+void getnstimeofday(struct timespec *ts)
+{
+	WARN_ON(__getnstimeofday(ts));
 }
 }
 EXPORT_SYMBOL(getnstimeofday);
 EXPORT_SYMBOL(getnstimeofday);
 
 
@@ -640,12 +662,14 @@ void __init timekeeping_init(void)
 	struct timespec now, boot, tmp;
 	struct timespec now, boot, tmp;
 
 
 	read_persistent_clock(&now);
 	read_persistent_clock(&now);
+
 	if (!timespec_valid_strict(&now)) {
 	if (!timespec_valid_strict(&now)) {
 		pr_warn("WARNING: Persistent clock returned invalid value!\n"
 		pr_warn("WARNING: Persistent clock returned invalid value!\n"
 			"         Check your CMOS/BIOS settings.\n");
 			"         Check your CMOS/BIOS settings.\n");
 		now.tv_sec = 0;
 		now.tv_sec = 0;
 		now.tv_nsec = 0;
 		now.tv_nsec = 0;
-	}
+	} else if (now.tv_sec || now.tv_nsec)
+		persistent_clock_exist = true;
 
 
 	read_boot_clock(&boot);
 	read_boot_clock(&boot);
 	if (!timespec_valid_strict(&boot)) {
 	if (!timespec_valid_strict(&boot)) {
@@ -718,11 +742,12 @@ void timekeeping_inject_sleeptime(struct timespec *delta)
 {
 {
 	struct timekeeper *tk = &timekeeper;
 	struct timekeeper *tk = &timekeeper;
 	unsigned long flags;
 	unsigned long flags;
-	struct timespec ts;
 
 
-	/* Make sure we don't set the clock twice */
-	read_persistent_clock(&ts);
-	if (!(ts.tv_sec == 0 && ts.tv_nsec == 0))
+	/*
+	 * Make sure we don't set the clock twice, as timekeeping_resume()
+	 * already did it
+	 */
+	if (has_persistent_clock())
 		return;
 		return;
 
 
 	write_seqlock_irqsave(&tk->lock, flags);
 	write_seqlock_irqsave(&tk->lock, flags);