2024-02-07 10:06:34

by Pranav Prasad

[permalink] [raw]
Subject: [PATCH 0/2] alarmtimer: Rework the suspend flow in alarmtimer

Hi!

During the driver suspend phase of kernel suspend, alarmtimer's suspend
callback is invoked and it identifies the earliest next wakeup alarm and
programs that into the HW real time clock (RTC). However, there is an
exception to this process. If the next alarm is within the next 2 seconds,
the alarmtimer driver fails to suspend. In this case, a non-trivial amount
of power is spent to freeze and unfreeze all userspace processes and to
suspend and resume a number of devices. In the vast majority of cases, the
imminent alarm that caused the failure was likely already scheduled before
suspend even started. This provides an opportunity to reduce power
consumption if the suspend failure decision is made earlier in the suspend
flow before the unnecessary extra work is done. This patch series aims to
achieve a kernel suspend flow in which the check for an imminent alarm is
performed early during the suspend prepare phase.

Pranav Prasad (2):
alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend
check configurable
alarmtimer: Modify alarmtimer suspend callback to check for imminent
alarm using PM notifier

kernel/time/alarmtimer.c | 178 +++++++++++++++++++++++++++++++--------
1 file changed, 144 insertions(+), 34 deletions(-)

--
2.43.0.594.gd9cf4e227d-goog



2024-02-07 10:06:47

by Pranav Prasad

[permalink] [raw]
Subject: [PATCH 1/2] alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend check configurable

Currently, the alarmtimer_suspend does not allow the kernel
to suspend if the next alarm is within 2 seconds.
Create alarmtimer sysfs to make the value of 2 seconds configurable.
This allows flexibility to provide a different value based on the
type of device running the Linux kernel. As a data point, about 40% of
kernel suspend failures in a subset of Android devices were due to
this check. A differently configured value can avoid these suspend
failures which performs a lot of additional work affecting the
power consumption of these Android devices.

Signed-off-by: Pranav Prasad <[email protected]>
Signed-off-by: Kelly Rossmoyer <[email protected]>
---
kernel/time/alarmtimer.c | 61 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 3 deletions(-)

diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 4657cb8e8b1f..e5d2e560b4c1 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -33,6 +33,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/alarmtimer.h>

+static const char alarmtimer_group_name[] = "alarmtimer";
+
/**
* struct alarm_base - Alarm timer bases
* @lock: Lock for syncrhonized access to the base
@@ -63,6 +65,56 @@ static struct rtc_timer rtctimer;
static struct rtc_device *rtcdev;
static DEFINE_SPINLOCK(rtcdev_lock);

+/* Duration to check for soonest alarm during kernel suspend */
+static u64 suspend_check_duration_ns = 2 * NSEC_PER_SEC;
+
+static ssize_t suspend_check_duration_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%llu\n", suspend_check_duration_ns);
+}
+
+static ssize_t suspend_check_duration_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t n)
+{
+ u64 val;
+
+ if (kstrtou64(buf, 10, &val))
+ return -EINVAL;
+
+ suspend_check_duration_ns = val;
+
+ return n;
+}
+
+static struct kobj_attribute suspend_check_duration =
+ __ATTR_RW(suspend_check_duration);
+
+static struct attribute *alarmtimer_attrs[] = {
+ &suspend_check_duration.attr,
+ NULL,
+};
+
+static const struct attribute_group alarmtimer_attr_group = {
+ .name = alarmtimer_group_name,
+ .attrs = alarmtimer_attrs,
+};
+
+/**
+ * alarmtimer_sysfs_add - Adds sysfs attributes for alarmtimer
+ *
+ * Returns 0 if successful, non-zero value for error.
+ */
+static int alarmtimer_sysfs_add(void)
+{
+ int ret = sysfs_create_group(kernel_kobj, &alarmtimer_attr_group);
+
+ if (ret)
+ pr_warn("[%s] failed to create a sysfs group\n", __func__);
+
+ return ret;
+}
+
/**
* alarmtimer_get_rtcdev - Return selected rtcdevice
*
@@ -98,8 +150,11 @@ static int alarmtimer_rtc_add_device(struct device *dev)

pdev = platform_device_register_data(dev, "alarmtimer",
PLATFORM_DEVID_AUTO, NULL, 0);
- if (!IS_ERR(pdev))
+ if (!IS_ERR(pdev)) {
device_init_wakeup(&pdev->dev, true);
+ if (alarmtimer_sysfs_add())
+ pr_warn("[%s] Failed to add alarmtimer sysfs attributes\n", __func__);
+ }

spin_lock_irqsave(&rtcdev_lock, flags);
if (!IS_ERR(pdev) && !rtcdev) {
@@ -279,8 +334,8 @@ static int alarmtimer_suspend(struct device *dev)
if (min == 0)
return 0;

- if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
- pm_wakeup_event(dev, 2 * MSEC_PER_SEC);
+ if (ktime_to_ns(min) < suspend_check_duration_ns) {
+ pm_wakeup_event(dev, suspend_check_duration_ns/NSEC_PER_MSEC);
return -EBUSY;
}

--
2.43.0.594.gd9cf4e227d-goog


2024-02-07 10:07:10

by Pranav Prasad

[permalink] [raw]
Subject: [PATCH 2/2] alarmtimer: Modify alarmtimer suspend callback to

The alarmtimer driver currently fails suspend attempts when there is an
alarm pending within the next suspend_check_duration_ns nanoseconds, since
the system is expected to wake up soon anyway. The entire suspend process
is initiated even though the system will immediately awaken. This process
includes substantial work before the suspend fails and additional work
afterwards to undo the failed suspend that was attempted. Therefore on
battery-powered devices that initiate suspend attempts from userspace, it
may be advantageous to be able to fail the suspend earlier in the suspend
flow to avoid power consumption instead of unnecessarily doing extra work.
As one data point, an analysis of a subset of Android devices showed that
imminent alarms account for roughly 40% of all suspend failures on average
leading to unnecessary power wastage.

To facilitate this, register a PM notifier in the alarmtimer subsystem
that checks if an alarm is imminent during the prepare stage of kernel
suspend denoted by the event PM_SUSPEND_PREPARE. If an alarm is imminent,
it returns the errno code ETIME instead of EBUSY to userspace in order to
make it easily diagnosable.

Signed-off-by: Pranav Prasad <[email protected]>
Signed-off-by: Kelly Rossmoyer <[email protected]>
---
kernel/time/alarmtimer.c | 121 ++++++++++++++++++++++++++++-----------
1 file changed, 88 insertions(+), 33 deletions(-)

diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index e5d2e560b4c1..229de937c266 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -27,6 +27,7 @@
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/time_namespace.h>
+#include <linux/suspend.h>

#include "posix-timers.h"

@@ -115,6 +116,87 @@ static int alarmtimer_sysfs_add(void)
return ret;
}

+/**
+ * alarmtimer_init_soonest - Initializes parameters to find soonest alarm.
+ * @min: ptr to relative time to the soonest alarm to expire
+ * @expires: ptr to absolute time of the soonest alarm to expire
+ * @type: ptr to alarm type
+ *
+ */
+static void alarmtimer_init_soonest(ktime_t *min, ktime_t *expires, int *type)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&freezer_delta_lock, flags);
+ *min = freezer_delta;
+ *expires = freezer_expires;
+ *type = freezer_alarmtype;
+ freezer_delta = 0;
+ spin_unlock_irqrestore(&freezer_delta_lock, flags);
+}
+
+/**
+ * alarmtimer_get_soonest - Finds the soonest alarm to expire among the alarm bases.
+ * @min: ptr to relative time to the soonest alarm to expire
+ * @expires: ptr to absolute time of the soonest alarm to expire
+ * @type: ptr to alarm type
+ *
+ */
+static void alarmtimer_get_soonest(ktime_t *min, ktime_t *expires, int *type)
+{
+ int i;
+ unsigned long flags;
+
+ /* Find the soonest timer to expire */
+ for (i = 0; i < ALARM_NUMTYPE; i++) {
+ struct alarm_base *base = &alarm_bases[i];
+ struct timerqueue_node *next;
+ ktime_t delta;
+
+ spin_lock_irqsave(&base->lock, flags);
+ next = timerqueue_getnext(&base->timerqueue);
+ spin_unlock_irqrestore(&base->lock, flags);
+ if (!next)
+ continue;
+ delta = ktime_sub(next->expires, base->get_ktime());
+ if (!(*min) || (delta < *min)) {
+ *expires = next->expires;
+ *min = delta;
+ *type = i;
+ }
+ }
+}
+
+static int alarmtimer_pm_callback(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
+ ktime_t min, expires;
+ int type;
+
+ switch (mode) {
+ case PM_SUSPEND_PREPARE:
+ /* Initialize parameters to find soonest timer */
+ alarmtimer_init_soonest(&min, &expires, &type);
+
+ /* Find the soonest timer to expire */
+ alarmtimer_get_soonest(&min, &expires, &type);
+
+ if (min == 0)
+ return NOTIFY_DONE;
+
+ if (ktime_to_ns(min) < suspend_check_duration_ns) {
+ pr_warn("[%s] Suspend abort due to imminent alarm\n", __func__);
+ return notifier_from_errno(-ETIME);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block alarmtimer_pm_notifier = {
+ .notifier_call = alarmtimer_pm_callback,
+};
+
/**
* alarmtimer_get_rtcdev - Return selected rtcdevice
*
@@ -181,6 +263,7 @@ static int alarmtimer_rtc_add_device(struct device *dev)
static inline void alarmtimer_rtc_timer_init(void)
{
rtc_timer_init(&rtctimer, NULL, NULL);
+ register_pm_notifier(&alarmtimer_pm_notifier);
}

static struct class_interface alarmtimer_rtc_interface = {
@@ -296,48 +379,20 @@ EXPORT_SYMBOL_GPL(alarm_expires_remaining);
static int alarmtimer_suspend(struct device *dev)
{
ktime_t min, now, expires;
- int i, ret, type;
+ int ret, type;
struct rtc_device *rtc;
- unsigned long flags;
struct rtc_time tm;

- spin_lock_irqsave(&freezer_delta_lock, flags);
- min = freezer_delta;
- expires = freezer_expires;
- type = freezer_alarmtype;
- freezer_delta = 0;
- spin_unlock_irqrestore(&freezer_delta_lock, flags);
+ /* Initialize parameters to find soonest timer */
+ alarmtimer_init_soonest(&min, &expires, &type);

rtc = alarmtimer_get_rtcdev();
/* If we have no rtcdev, just return */
if (!rtc)
return 0;

- /* Find the soonest timer to expire*/
- for (i = 0; i < ALARM_NUMTYPE; i++) {
- struct alarm_base *base = &alarm_bases[i];
- struct timerqueue_node *next;
- ktime_t delta;
-
- spin_lock_irqsave(&base->lock, flags);
- next = timerqueue_getnext(&base->timerqueue);
- spin_unlock_irqrestore(&base->lock, flags);
- if (!next)
- continue;
- delta = ktime_sub(next->expires, base->get_ktime());
- if (!min || (delta < min)) {
- expires = next->expires;
- min = delta;
- type = i;
- }
- }
- if (min == 0)
- return 0;
-
- if (ktime_to_ns(min) < suspend_check_duration_ns) {
- pm_wakeup_event(dev, suspend_check_duration_ns/NSEC_PER_MSEC);
- return -EBUSY;
- }
+ /* Find the soonest timer to expire */
+ alarmtimer_get_soonest(&min, &expires, &type);

trace_alarmtimer_suspend(expires, type);

--
2.43.0.594.gd9cf4e227d-goog


2024-02-07 16:47:28

by Pranav Prasad

[permalink] [raw]
Subject: Re: [PATCH 0/2] alarmtimer: Rework the suspend flow in alarmtimer

Please ignore this patch series, submitting v2 with some more suggested fixes.

Pranav

On Wed, Feb 7, 2024 at 2:06 AM Pranav Prasad <[email protected]> wrote:
>
> Hi!
>
> During the driver suspend phase of kernel suspend, alarmtimer's suspend
> callback is invoked and it identifies the earliest next wakeup alarm and
> programs that into the HW real time clock (RTC). However, there is an
> exception to this process. If the next alarm is within the next 2 seconds,
> the alarmtimer driver fails to suspend. In this case, a non-trivial amount
> of power is spent to freeze and unfreeze all userspace processes and to
> suspend and resume a number of devices. In the vast majority of cases, the
> imminent alarm that caused the failure was likely already scheduled before
> suspend even started. This provides an opportunity to reduce power
> consumption if the suspend failure decision is made earlier in the suspend
> flow before the unnecessary extra work is done. This patch series aims to
> achieve a kernel suspend flow in which the check for an imminent alarm is
> performed early during the suspend prepare phase.
>
> Pranav Prasad (2):
> alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend
> check configurable
> alarmtimer: Modify alarmtimer suspend callback to check for imminent
> alarm using PM notifier
>
> kernel/time/alarmtimer.c | 178 +++++++++++++++++++++++++++++++--------
> 1 file changed, 144 insertions(+), 34 deletions(-)
>
> --
> 2.43.0.594.gd9cf4e227d-goog
>

2024-02-07 16:50:43

by Pranav Prasad

[permalink] [raw]
Subject: Re: [PATCH 1/2] alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend check configurable

Please ignore this patch, submitting v2 with some more suggested fixes.

Pranav

On Wed, Feb 7, 2024 at 2:06 AM Pranav Prasad <[email protected]> wrote:
>
> Currently, the alarmtimer_suspend does not allow the kernel
> to suspend if the next alarm is within 2 seconds.
> Create alarmtimer sysfs to make the value of 2 seconds configurable.
> This allows flexibility to provide a different value based on the
> type of device running the Linux kernel. As a data point, about 40% of
> kernel suspend failures in a subset of Android devices were due to
> this check. A differently configured value can avoid these suspend
> failures which performs a lot of additional work affecting the
> power consumption of these Android devices.
>
> Signed-off-by: Pranav Prasad <[email protected]>
> Signed-off-by: Kelly Rossmoyer <[email protected]>
> ---
> kernel/time/alarmtimer.c | 61 ++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 58 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
> index 4657cb8e8b1f..e5d2e560b4c1 100644
> --- a/kernel/time/alarmtimer.c
> +++ b/kernel/time/alarmtimer.c
> @@ -33,6 +33,8 @@
> #define CREATE_TRACE_POINTS
> #include <trace/events/alarmtimer.h>
>
> +static const char alarmtimer_group_name[] = "alarmtimer";
> +
> /**
> * struct alarm_base - Alarm timer bases
> * @lock: Lock for syncrhonized access to the base
> @@ -63,6 +65,56 @@ static struct rtc_timer rtctimer;
> static struct rtc_device *rtcdev;
> static DEFINE_SPINLOCK(rtcdev_lock);
>
> +/* Duration to check for soonest alarm during kernel suspend */
> +static u64 suspend_check_duration_ns = 2 * NSEC_PER_SEC;
> +
> +static ssize_t suspend_check_duration_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "%llu\n", suspend_check_duration_ns);
> +}
> +
> +static ssize_t suspend_check_duration_store(struct kobject *kobj,
> + struct kobj_attribute *attr, const char *buf, size_t n)
> +{
> + u64 val;
> +
> + if (kstrtou64(buf, 10, &val))
> + return -EINVAL;
> +
> + suspend_check_duration_ns = val;
> +
> + return n;
> +}
> +
> +static struct kobj_attribute suspend_check_duration =
> + __ATTR_RW(suspend_check_duration);
> +
> +static struct attribute *alarmtimer_attrs[] = {
> + &suspend_check_duration.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group alarmtimer_attr_group = {
> + .name = alarmtimer_group_name,
> + .attrs = alarmtimer_attrs,
> +};
> +
> +/**
> + * alarmtimer_sysfs_add - Adds sysfs attributes for alarmtimer
> + *
> + * Returns 0 if successful, non-zero value for error.
> + */
> +static int alarmtimer_sysfs_add(void)
> +{
> + int ret = sysfs_create_group(kernel_kobj, &alarmtimer_attr_group);
> +
> + if (ret)
> + pr_warn("[%s] failed to create a sysfs group\n", __func__);
> +
> + return ret;
> +}
> +
> /**
> * alarmtimer_get_rtcdev - Return selected rtcdevice
> *
> @@ -98,8 +150,11 @@ static int alarmtimer_rtc_add_device(struct device *dev)
>
> pdev = platform_device_register_data(dev, "alarmtimer",
> PLATFORM_DEVID_AUTO, NULL, 0);
> - if (!IS_ERR(pdev))
> + if (!IS_ERR(pdev)) {
> device_init_wakeup(&pdev->dev, true);
> + if (alarmtimer_sysfs_add())
> + pr_warn("[%s] Failed to add alarmtimer sysfs attributes\n", __func__);
> + }
>
> spin_lock_irqsave(&rtcdev_lock, flags);
> if (!IS_ERR(pdev) && !rtcdev) {
> @@ -279,8 +334,8 @@ static int alarmtimer_suspend(struct device *dev)
> if (min == 0)
> return 0;
>
> - if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
> - pm_wakeup_event(dev, 2 * MSEC_PER_SEC);
> + if (ktime_to_ns(min) < suspend_check_duration_ns) {
> + pm_wakeup_event(dev, suspend_check_duration_ns/NSEC_PER_MSEC);
> return -EBUSY;
> }
>
> --
> 2.43.0.594.gd9cf4e227d-goog
>

2024-02-07 17:25:05

by Pranav Prasad

[permalink] [raw]
Subject: Re: [PATCH 0/2] alarmtimer: Rework the suspend flow in alarmtimer

On Wed, Feb 7, 2024 at 2:06 AM Pranav Prasad <[email protected]> wrote:
>
> Hi!
>
> During the driver suspend phase of kernel suspend, alarmtimer's suspend
> callback is invoked and it identifies the earliest next wakeup alarm and
> programs that into the HW real time clock (RTC). However, there is an
> exception to this process. If the next alarm is within the next 2 seconds,
> the alarmtimer driver fails to suspend. In this case, a non-trivial amount
> of power is spent to freeze and unfreeze all userspace processes and to
> suspend and resume a number of devices. In the vast majority of cases, the
> imminent alarm that caused the failure was likely already scheduled before
> suspend even started. This provides an opportunity to reduce power
> consumption if the suspend failure decision is made earlier in the suspend
> flow before the unnecessary extra work is done. This patch series aims to
> achieve a kernel suspend flow in which the check for an imminent alarm is
> performed early during the suspend prepare phase.
>
> Pranav Prasad (2):
> alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend
> check configurable
> alarmtimer: Modify alarmtimer suspend callback to check for imminent
> alarm using PM notifier
>
> kernel/time/alarmtimer.c | 178 +++++++++++++++++++++++++++++++--------
> 1 file changed, 144 insertions(+), 34 deletions(-)
>
> --
> 2.43.0.594.gd9cf4e227d-goog
>

Please ignore this patch series, submitting v2 with some more suggested fixes.

Pranav

On Wed, Feb 7, 2024 at 8:46 AM Pranav Prasad <[email protected]> wrote:
>
> Please ignore this patch series, submitting v2 with some more suggested fixes.
>
> Pranav
>
> On Wed, Feb 7, 2024 at 2:06 AM Pranav Prasad <[email protected]> wrote:
> >
> > Hi!
> >
> > During the driver suspend phase of kernel suspend, alarmtimer's suspend
> > callback is invoked and it identifies the earliest next wakeup alarm and
> > programs that into the HW real time clock (RTC). However, there is an
> > exception to this process. If the next alarm is within the next 2 seconds,
> > the alarmtimer driver fails to suspend. In this case, a non-trivial amount
> > of power is spent to freeze and unfreeze all userspace processes and to
> > suspend and resume a number of devices. In the vast majority of cases, the
> > imminent alarm that caused the failure was likely already scheduled before
> > suspend even started. This provides an opportunity to reduce power
> > consumption if the suspend failure decision is made earlier in the suspend
> > flow before the unnecessary extra work is done. This patch series aims to
> > achieve a kernel suspend flow in which the check for an imminent alarm is
> > performed early during the suspend prepare phase.
> >
> > Pranav Prasad (2):
> > alarmtimer: Create alarmtimer sysfs to make duration of kernel suspend
> > check configurable
> > alarmtimer: Modify alarmtimer suspend callback to check for imminent
> > alarm using PM notifier
> >
> > kernel/time/alarmtimer.c | 178 +++++++++++++++++++++++++++++++--------
> > 1 file changed, 144 insertions(+), 34 deletions(-)
> >
> > --
> > 2.43.0.594.gd9cf4e227d-goog
> >

2024-02-07 17:29:46

by Pranav Prasad

[permalink] [raw]
Subject: Re: [PATCH 2/2] alarmtimer: Modify alarmtimer suspend callback to

Please ignore this patch, submitting v2 with some more suggested fixes.

Pranav


On Wed, Feb 7, 2024 at 8:39 AM Pranav Prasad <[email protected]> wrote:
>
> Please ignore this patch, submitting v2 with some more suggested fixes.
>
> Pranav
>
> On Wed, Feb 7, 2024, 2:06 AM Pranav Prasad <[email protected]> wrote:
>>
>> The alarmtimer driver currently fails suspend attempts when there is an
>> alarm pending within the next suspend_check_duration_ns nanoseconds, since
>> the system is expected to wake up soon anyway. The entire suspend process
>> is initiated even though the system will immediately awaken. This process
>> includes substantial work before the suspend fails and additional work
>> afterwards to undo the failed suspend that was attempted. Therefore on
>> battery-powered devices that initiate suspend attempts from userspace, it
>> may be advantageous to be able to fail the suspend earlier in the suspend
>> flow to avoid power consumption instead of unnecessarily doing extra work.
>> As one data point, an analysis of a subset of Android devices showed that
>> imminent alarms account for roughly 40% of all suspend failures on average
>> leading to unnecessary power wastage.
>>
>> To facilitate this, register a PM notifier in the alarmtimer subsystem
>> that checks if an alarm is imminent during the prepare stage of kernel
>> suspend denoted by the event PM_SUSPEND_PREPARE. If an alarm is imminent,
>> it returns the errno code ETIME instead of EBUSY to userspace in order to
>> make it easily diagnosable.
>>
>> Signed-off-by: Pranav Prasad <[email protected]>
>> Signed-off-by: Kelly Rossmoyer <[email protected]>
>> ---
>> kernel/time/alarmtimer.c | 121 ++++++++++++++++++++++++++++-----------
>> 1 file changed, 88 insertions(+), 33 deletions(-)
>>
>> diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
>> index e5d2e560b4c1..229de937c266 100644
>> --- a/kernel/time/alarmtimer.c
>> +++ b/kernel/time/alarmtimer.c
>> @@ -27,6 +27,7 @@
>> #include <linux/compat.h>
>> #include <linux/module.h>
>> #include <linux/time_namespace.h>
>> +#include <linux/suspend.h>
>>
>> #include "posix-timers.h"
>>
>> @@ -115,6 +116,87 @@ static int alarmtimer_sysfs_add(void)
>> return ret;
>> }
>>
>> +/**
>> + * alarmtimer_init_soonest - Initializes parameters to find soonest alarm.
>> + * @min: ptr to relative time to the soonest alarm to expire
>> + * @expires: ptr to absolute time of the soonest alarm to expire
>> + * @type: ptr to alarm type
>> + *
>> + */
>> +static void alarmtimer_init_soonest(ktime_t *min, ktime_t *expires, int *type)
>> +{
>> + unsigned long flags;
>> +
>> + spin_lock_irqsave(&freezer_delta_lock, flags);
>> + *min = freezer_delta;
>> + *expires = freezer_expires;
>> + *type = freezer_alarmtype;
>> + freezer_delta = 0;
>> + spin_unlock_irqrestore(&freezer_delta_lock, flags);
>> +}
>> +
>> +/**
>> + * alarmtimer_get_soonest - Finds the soonest alarm to expire among the alarm bases.
>> + * @min: ptr to relative time to the soonest alarm to expire
>> + * @expires: ptr to absolute time of the soonest alarm to expire
>> + * @type: ptr to alarm type
>> + *
>> + */
>> +static void alarmtimer_get_soonest(ktime_t *min, ktime_t *expires, int *type)
>> +{
>> + int i;
>> + unsigned long flags;
>> +
>> + /* Find the soonest timer to expire */
>> + for (i = 0; i < ALARM_NUMTYPE; i++) {
>> + struct alarm_base *base = &alarm_bases[i];
>> + struct timerqueue_node *next;
>> + ktime_t delta;
>> +
>> + spin_lock_irqsave(&base->lock, flags);
>> + next = timerqueue_getnext(&base->timerqueue);
>> + spin_unlock_irqrestore(&base->lock, flags);
>> + if (!next)
>> + continue;
>> + delta = ktime_sub(next->expires, base->get_ktime());
>> + if (!(*min) || (delta < *min)) {
>> + *expires = next->expires;
>> + *min = delta;
>> + *type = i;
>> + }
>> + }
>> +}
>> +
>> +static int alarmtimer_pm_callback(struct notifier_block *nb,
>> + unsigned long mode, void *_unused)
>> +{
>> + ktime_t min, expires;
>> + int type;
>> +
>> + switch (mode) {
>> + case PM_SUSPEND_PREPARE:
>> + /* Initialize parameters to find soonest timer */
>> + alarmtimer_init_soonest(&min, &expires, &type);
>> +
>> + /* Find the soonest timer to expire */
>> + alarmtimer_get_soonest(&min, &expires, &type);
>> +
>> + if (min == 0)
>> + return NOTIFY_DONE;
>> +
>> + if (ktime_to_ns(min) < suspend_check_duration_ns) {
>> + pr_warn("[%s] Suspend abort due to imminent alarm\n", __func__);
>> + return notifier_from_errno(-ETIME);
>> + }
>> + }
>> +
>> + return NOTIFY_DONE;
>> +}
>> +
>> +static struct notifier_block alarmtimer_pm_notifier = {
>> + .notifier_call = alarmtimer_pm_callback,
>> +};
>> +
>> /**
>> * alarmtimer_get_rtcdev - Return selected rtcdevice
>> *
>> @@ -181,6 +263,7 @@ static int alarmtimer_rtc_add_device(struct device *dev)
>> static inline void alarmtimer_rtc_timer_init(void)
>> {
>> rtc_timer_init(&rtctimer, NULL, NULL);
>> + register_pm_notifier(&alarmtimer_pm_notifier);
>> }
>>
>> static struct class_interface alarmtimer_rtc_interface = {
>> @@ -296,48 +379,20 @@ EXPORT_SYMBOL_GPL(alarm_expires_remaining);
>> static int alarmtimer_suspend(struct device *dev)
>> {
>> ktime_t min, now, expires;
>> - int i, ret, type;
>> + int ret, type;
>> struct rtc_device *rtc;
>> - unsigned long flags;
>> struct rtc_time tm;
>>
>> - spin_lock_irqsave(&freezer_delta_lock, flags);
>> - min = freezer_delta;
>> - expires = freezer_expires;
>> - type = freezer_alarmtype;
>> - freezer_delta = 0;
>> - spin_unlock_irqrestore(&freezer_delta_lock, flags);
>> + /* Initialize parameters to find soonest timer */
>> + alarmtimer_init_soonest(&min, &expires, &type);
>>
>> rtc = alarmtimer_get_rtcdev();
>> /* If we have no rtcdev, just return */
>> if (!rtc)
>> return 0;
>>
>> - /* Find the soonest timer to expire*/
>> - for (i = 0; i < ALARM_NUMTYPE; i++) {
>> - struct alarm_base *base = &alarm_bases[i];
>> - struct timerqueue_node *next;
>> - ktime_t delta;
>> -
>> - spin_lock_irqsave(&base->lock, flags);
>> - next = timerqueue_getnext(&base->timerqueue);
>> - spin_unlock_irqrestore(&base->lock, flags);
>> - if (!next)
>> - continue;
>> - delta = ktime_sub(next->expires, base->get_ktime());
>> - if (!min || (delta < min)) {
>> - expires = next->expires;
>> - min = delta;
>> - type = i;
>> - }
>> - }
>> - if (min == 0)
>> - return 0;
>> -
>> - if (ktime_to_ns(min) < suspend_check_duration_ns) {
>> - pm_wakeup_event(dev, suspend_check_duration_ns/NSEC_PER_MSEC);
>> - return -EBUSY;
>> - }
>> + /* Find the soonest timer to expire */
>> + alarmtimer_get_soonest(&min, &expires, &type);
>>
>> trace_alarmtimer_suspend(expires, type);
>>
>> --
>> 2.43.0.594.gd9cf4e227d-goog
>>