Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 112ec469 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'timers-core-for-linus' of...

Merge branch 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'timers-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  time: Fix stupid KERN_WARN compile issue
  rtc: Avoid accumulating time drift in suspend/resume
  time: Avoid accumulating time drift in suspend/resume
  time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime
parents a99a7d14 cbaa5152
Loading
Loading
Loading
Loading
+48 −17
Original line number Original line Diff line number Diff line
@@ -41,20 +41,41 @@ static void rtc_device_release(struct device *dev)
 * system's wall clock; restore it on resume().
 * system's wall clock; restore it on resume().
 */
 */


static time_t		oldtime;
static struct timespec old_rtc, old_system, old_delta;
static struct timespec	oldts;



static int rtc_suspend(struct device *dev, pm_message_t mesg)
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;
	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;


	/* snapshot the current RTC and system time at suspend*/
	rtc_read_time(rtc, &tm);
	rtc_read_time(rtc, &tm);
	ktime_get_ts(&oldts);
	getnstimeofday(&old_system);
	rtc_tm_to_time(&tm, &oldtime);
	rtc_tm_to_time(&tm, &old_rtc.tv_sec);


	/*
	 * To avoid drift caused by repeated suspend/resumes,
	 * which each can add ~1 second drift error,
	 * try to compensate so the difference in system time
	 * and rtc time stays close to constant.
	 */
	delta = timespec_sub(old_system, old_rtc);
	delta_delta = timespec_sub(delta, old_delta);
	if (abs(delta_delta.tv_sec)  >= 2) {
		/*
		 * if delta_delta is too large, assume time correction
		 * has occured and set old_delta to the current delta.
		 */
		old_delta = delta;
	} else {
		/* Otherwise try to adjust old_system to compensate */
		old_system = timespec_sub(old_system, delta_delta);
	}


	return 0;
	return 0;
}
}
@@ -63,32 +84,42 @@ static int rtc_resume(struct device *dev)
{
{
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_device	*rtc = to_rtc_device(dev);
	struct rtc_time		tm;
	struct rtc_time		tm;
	time_t			newtime;
	struct timespec		new_system, new_rtc;
	struct timespec		time;
	struct timespec		sleep_time;
	struct timespec		newts;


	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;


	ktime_get_ts(&newts);
	/* snapshot the current rtc and system time at resume */
	getnstimeofday(&new_system);
	rtc_read_time(rtc, &tm);
	rtc_read_time(rtc, &tm);
	if (rtc_valid_tm(&tm) != 0) {
	if (rtc_valid_tm(&tm) != 0) {
		pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
		pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
		return 0;
		return 0;
	}
	}
	rtc_tm_to_time(&tm, &newtime);
	rtc_tm_to_time(&tm, &new_rtc.tv_sec);
	if (newtime <= oldtime) {
	new_rtc.tv_nsec = 0;
		if (newtime < oldtime)

	if (new_rtc.tv_sec <= old_rtc.tv_sec) {
		if (new_rtc.tv_sec < old_rtc.tv_sec)
			pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
			pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
		return 0;
		return 0;
	}
	}
	/* calculate the RTC time delta */
	set_normalized_timespec(&time, newtime - oldtime, 0);


	/* subtract kernel time between rtc_suspend to rtc_resume */
	/* calculate the RTC time delta (sleep time)*/
	time = timespec_sub(time, timespec_sub(newts, oldts));
	sleep_time = timespec_sub(new_rtc, old_rtc);

	/*
	 * Since these RTC suspend/resume handlers are not called
	 * at the very end of suspend or the start of resume,
	 * some run-time may pass on either sides of the sleep time
	 * so subtract kernel run-time between rtc_suspend to rtc_resume
	 * to keep things accurate.
	 */
	sleep_time = timespec_sub(sleep_time,
			timespec_sub(new_system, old_system));


	timekeeping_inject_sleeptime(&time);
	timekeeping_inject_sleeptime(&sleep_time);
	return 0;
	return 0;
}
}


+28 −0
Original line number Original line Diff line number Diff line
@@ -604,6 +604,12 @@ static struct timespec timekeeping_suspend_time;
 */
 */
static void __timekeeping_inject_sleeptime(struct timespec *delta)
static void __timekeeping_inject_sleeptime(struct timespec *delta)
{
{
	if (!timespec_valid(delta)) {
		printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid "
					"sleep delta value!\n");
		return;
	}

	xtime = timespec_add(xtime, *delta);
	xtime = timespec_add(xtime, *delta);
	wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta);
	wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta);
	total_sleep_time = timespec_add(total_sleep_time, *delta);
	total_sleep_time = timespec_add(total_sleep_time, *delta);
@@ -686,12 +692,34 @@ static void timekeeping_resume(void)
static int timekeeping_suspend(void)
static int timekeeping_suspend(void)
{
{
	unsigned long flags;
	unsigned long flags;
	struct timespec		delta, delta_delta;
	static struct timespec	old_delta;


	read_persistent_clock(&timekeeping_suspend_time);
	read_persistent_clock(&timekeeping_suspend_time);


	write_seqlock_irqsave(&xtime_lock, flags);
	write_seqlock_irqsave(&xtime_lock, flags);
	timekeeping_forward_now();
	timekeeping_forward_now();
	timekeeping_suspended = 1;
	timekeeping_suspended = 1;

	/*
	 * To avoid drift caused by repeated suspend/resumes,
	 * which each can add ~1 second drift error,
	 * try to compensate so the difference in system time
	 * and persistent_clock time stays close to constant.
	 */
	delta = timespec_sub(xtime, timekeeping_suspend_time);
	delta_delta = timespec_sub(delta, old_delta);
	if (abs(delta_delta.tv_sec)  >= 2) {
		/*
		 * if delta_delta is too large, assume time correction
		 * has occured and set old_delta to the current delta.
		 */
		old_delta = delta;
	} else {
		/* Otherwise try to adjust old_system to compensate */
		timekeeping_suspend_time =
			timespec_add(timekeeping_suspend_time, delta_delta);
	}
	write_sequnlock_irqrestore(&xtime_lock, flags);
	write_sequnlock_irqrestore(&xtime_lock, flags);


	clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
	clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);