Skip to content

Commit c008ba5

Browse files
alarmtimers: Handle late rtc module loading
The alarmtimers code currently picks a rtc device to use at late init time. However, if your rtc driver is loaded as a module, it may be registered after the alarmtimers late init code, leaving the alarmtimers nonfunctional. This patch moves the the rtcdevice selection to when we actually try to use it, allowing us to make use of rtc modules that may have been loaded at any point since bootup. CC: Thomas Gleixner <[email protected]> CC: Meelis Roos <[email protected]> Reported-by: Meelis Roos <[email protected]> Signed-off-by: John Stultz <[email protected]>
1 parent e08f6d4 commit c008ba5

File tree

1 file changed

+67
-70
lines changed

1 file changed

+67
-70
lines changed

kernel/time/alarmtimer.c

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,72 @@ static struct alarm_base {
4242
clockid_t base_clockid;
4343
} alarm_bases[ALARM_NUMTYPE];
4444

45+
/* freezer delta & lock used to handle clock_nanosleep triggered wakeups */
46+
static ktime_t freezer_delta;
47+
static DEFINE_SPINLOCK(freezer_delta_lock);
48+
4549
#ifdef CONFIG_RTC_CLASS
4650
/* rtc timer and device for setting alarm wakeups at suspend */
4751
static struct rtc_timer rtctimer;
4852
static struct rtc_device *rtcdev;
49-
#endif
53+
static DEFINE_SPINLOCK(rtcdev_lock);
5054

51-
/* freezer delta & lock used to handle clock_nanosleep triggered wakeups */
52-
static ktime_t freezer_delta;
53-
static DEFINE_SPINLOCK(freezer_delta_lock);
55+
/**
56+
* has_wakealarm - check rtc device has wakealarm ability
57+
* @dev: current device
58+
* @name_ptr: name to be returned
59+
*
60+
* This helper function checks to see if the rtc device can wake
61+
* from suspend.
62+
*/
63+
static int has_wakealarm(struct device *dev, void *name_ptr)
64+
{
65+
struct rtc_device *candidate = to_rtc_device(dev);
66+
67+
if (!candidate->ops->set_alarm)
68+
return 0;
69+
if (!device_may_wakeup(candidate->dev.parent))
70+
return 0;
71+
72+
*(const char **)name_ptr = dev_name(dev);
73+
return 1;
74+
}
75+
76+
/**
77+
* alarmtimer_get_rtcdev - Return selected rtcdevice
78+
*
79+
* This function returns the rtc device to use for wakealarms.
80+
* If one has not already been chosen, it checks to see if a
81+
* functional rtc device is available.
82+
*/
83+
static struct rtc_device *alarmtimer_get_rtcdev(void)
84+
{
85+
struct device *dev;
86+
char *str;
87+
unsigned long flags;
88+
struct rtc_device *ret;
89+
90+
spin_lock_irqsave(&rtcdev_lock, flags);
91+
if (!rtcdev) {
92+
/* Find an rtc device and init the rtc_timer */
93+
dev = class_find_device(rtc_class, NULL, &str, has_wakealarm);
94+
/* If we have a device then str is valid. See has_wakealarm() */
95+
if (dev) {
96+
rtcdev = rtc_class_open(str);
97+
/*
98+
* Drop the reference we got in class_find_device,
99+
* rtc_open takes its own.
100+
*/
101+
put_device(dev);
102+
rtc_timer_init(&rtctimer, NULL, NULL);
103+
}
104+
}
105+
ret = rtcdev;
106+
spin_unlock_irqrestore(&rtcdev_lock, flags);
107+
108+
return ret;
109+
}
110+
#endif
54111

55112

56113
/**
@@ -166,15 +223,17 @@ static int alarmtimer_suspend(struct device *dev)
166223
struct rtc_time tm;
167224
ktime_t min, now;
168225
unsigned long flags;
226+
struct rtc_device *rtc;
169227
int i;
170228

171229
spin_lock_irqsave(&freezer_delta_lock, flags);
172230
min = freezer_delta;
173231
freezer_delta = ktime_set(0, 0);
174232
spin_unlock_irqrestore(&freezer_delta_lock, flags);
175233

234+
rtc = alarmtimer_get_rtcdev();
176235
/* If we have no rtcdev, just return */
177-
if (!rtcdev)
236+
if (!rtc)
178237
return 0;
179238

180239
/* Find the soonest timer to expire*/
@@ -199,12 +258,12 @@ static int alarmtimer_suspend(struct device *dev)
199258
WARN_ON(min.tv64 < NSEC_PER_SEC);
200259

201260
/* Setup an rtc timer to fire that far in the future */
202-
rtc_timer_cancel(rtcdev, &rtctimer);
203-
rtc_read_time(rtcdev, &tm);
261+
rtc_timer_cancel(rtc, &rtctimer);
262+
rtc_read_time(rtc, &tm);
204263
now = rtc_tm_to_ktime(tm);
205264
now = ktime_add(now, min);
206265

207-
rtc_timer_start(rtcdev, &rtctimer, now, ktime_set(0, 0));
266+
rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
208267

209268
return 0;
210269
}
@@ -638,65 +697,3 @@ static int __init alarmtimer_init(void)
638697
}
639698
device_initcall(alarmtimer_init);
640699

641-
#ifdef CONFIG_RTC_CLASS
642-
/**
643-
* has_wakealarm - check rtc device has wakealarm ability
644-
* @dev: current device
645-
* @name_ptr: name to be returned
646-
*
647-
* This helper function checks to see if the rtc device can wake
648-
* from suspend.
649-
*/
650-
static int __init has_wakealarm(struct device *dev, void *name_ptr)
651-
{
652-
struct rtc_device *candidate = to_rtc_device(dev);
653-
654-
if (!candidate->ops->set_alarm)
655-
return 0;
656-
if (!device_may_wakeup(candidate->dev.parent))
657-
return 0;
658-
659-
*(const char **)name_ptr = dev_name(dev);
660-
return 1;
661-
}
662-
663-
/**
664-
* alarmtimer_init_late - Late initializing of alarmtimer code
665-
*
666-
* This function locates a rtc device to use for wakealarms.
667-
* Run as late_initcall to make sure rtc devices have been
668-
* registered.
669-
*/
670-
static int __init alarmtimer_init_late(void)
671-
{
672-
struct device *dev;
673-
char *str;
674-
675-
/* Find an rtc device and init the rtc_timer */
676-
dev = class_find_device(rtc_class, NULL, &str, has_wakealarm);
677-
/* If we have a device then str is valid. See has_wakealarm() */
678-
if (dev) {
679-
rtcdev = rtc_class_open(str);
680-
/*
681-
* Drop the reference we got in class_find_device,
682-
* rtc_open takes its own.
683-
*/
684-
put_device(dev);
685-
}
686-
if (!rtcdev) {
687-
printk(KERN_WARNING "No RTC device found, ALARM timers will"
688-
" not wake from suspend");
689-
}
690-
rtc_timer_init(&rtctimer, NULL, NULL);
691-
692-
return 0;
693-
}
694-
#else
695-
static int __init alarmtimer_init_late(void)
696-
{
697-
printk(KERN_WARNING "Kernel not built with RTC support, ALARM timers"
698-
" will not wake from suspend");
699-
return 0;
700-
}
701-
#endif
702-
late_initcall(alarmtimer_init_late);

0 commit comments

Comments
 (0)