Re: [tip:timers/core] time: Add timekeeping_inject_sleeptime

From: Arve Hjønnevåg
Date: Fri Apr 29 2011 - 19:28:25 EST


On Fri, Apr 29, 2011 at 10:31 AM, tip-bot for John Stultz
<john.stultz@xxxxxxxxxx> wrote:
...
>
> time: Add timekeeping_inject_sleeptime
>
> Some platforms cannot implement read_persistent_clock, as
> their RTC devices are only accessible when interrupts are enabled.
> This keeps them from being used by the timekeeping code on resume
> to measure the time in suspend.
>
> The RTC layer tries to work around this, by calling do_settimeofday
> on resume after irqs are reenabled to set the time properly. However,
> this only corrects CLOCK_REALTIME, and does not properly adjust
> the sleep time value. This causes btime in /proc/stat to be incorrect
> as well as making the new CLOCK_BOTTTIME inaccurate.
>
> This patch resolves the issue by introducing a new timekeeping hook
> to allow the RTC layer to inject the sleep time on resume.
>
> The code also checks to make sure that read_persistent_clock is
> nonfunctional before setting the sleep time, so that should the RTC's
> HCTOSYS option be configured in on a system that does support
> read_persistent_clock we will not increase the total_sleep_time twice.
>
> CC: Arve Hjønnevåg <arve@xxxxxxxxxxx>
> CC: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> Acked-by: Arnd Bergmann <arnd@xxxxxxxx>
> Signed-off-by: John Stultz <john.stultz@xxxxxxxxxx>
> ---
>  drivers/rtc/class.c       |   23 +++++++-----------
>  include/linux/time.h      |    1 +
>  kernel/time/timekeeping.c |   56 ++++++++++++++++++++++++++++++++++++++++++--
>  3 files changed, 63 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
> index 3901386..4194e59 100644
> --- a/drivers/rtc/class.c
> +++ b/drivers/rtc/class.c
> @@ -41,26 +41,21 @@ static void rtc_device_release(struct device *dev)
>  * system's wall clock; restore it on resume().
>  */
>
> -static struct timespec delta;
>  static time_t          oldtime;
> +static struct timespec oldts;
>
>  static int rtc_suspend(struct device *dev, pm_message_t mesg)
>  {
>        struct rtc_device       *rtc = to_rtc_device(dev);
>        struct rtc_time         tm;
> -       struct timespec         ts = current_kernel_time();
>
>        if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
>                return 0;
>
>        rtc_read_time(rtc, &tm);
> +       ktime_get_ts(&oldts);
>        rtc_tm_to_time(&tm, &oldtime);
>
> -       /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */
> -       set_normalized_timespec(&delta,
> -                               ts.tv_sec - oldtime,
> -                               ts.tv_nsec - (NSEC_PER_SEC >> 1));
> -
>        return 0;
>  }
>
> @@ -70,10 +65,12 @@ static int rtc_resume(struct device *dev)
>        struct rtc_time         tm;
>        time_t                  newtime;
>        struct timespec         time;
> +       struct timespec         newts;
>
>        if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
>                return 0;
>
> +       ktime_get_ts(&newts);
>        rtc_read_time(rtc, &tm);
>        if (rtc_valid_tm(&tm) != 0) {
>                pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
> @@ -85,15 +82,13 @@ static int rtc_resume(struct device *dev)
>                        pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
>                return 0;
>        }
> +       /* calculate the RTC time delta */
> +       set_normalized_timespec(&time, newtime - oldtime, 0);
>
> -       /* restore wall clock using delta against this RTC;
> -        * adjust again for avg 1/2 second RTC sampling error
> -        */
> -       set_normalized_timespec(&time,
> -                               newtime + delta.tv_sec,
> -                               (NSEC_PER_SEC >> 1) + delta.tv_nsec);
> -       do_settimeofday(&time);
> +       /* subtract kernel time between rtc_suspend to rtc_resume */
> +       time = timespec_sub(time, timespec_sub(newts, oldts));

The delta you got from the rtc can be almost a second to long or
short. Do you do anything to prevent these errors from accumulating?

>
> +       timekeeping_inject_sleeptime(&time);
>        return 0;
>  }
>
...

--
Arve Hjønnevåg
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/