rtc: cmos: use spin_lock_irqsave in cmos_interrupt
commit 00a39d8652ff9088de07a6fe6e9e1893452fe0dd upstream. cmos_interrupt() can be called in a non-interrupt context, such as in an ACPI event handler (which runs in an interrupt thread). Therefore, usage of spin_lock(&rtc_lock) is insecure. Use spin_lock_irqsave() / spin_unlock_irqrestore() instead. Before a misguided commit6950d046eb
("rtc: cmos: Replace spin_lock_irqsave with spin_lock in hard IRQ") the cmos_interrupt() function used spin_lock_irqsave(). That commit changed it to spin_lock() and broke locking, which was partially fixed in commit13be2efc39
("rtc: cmos: Disable irq around direct invocation of cmos_interrupt()") That second commit did not take account of the ACPI fixed event handler pathway, however. It introduced local_irq_disable() workarounds in cmos_check_wkalrm(), which can cause problems on PREEMPT_RT kernels and are now unnecessary. Add an explicit comment so that this change will not be reverted by mistake. Cc: stable@vger.kernel.org Fixes:6950d046eb
("rtc: cmos: Replace spin_lock_irqsave with spin_lock in hard IRQ") Signed-off-by: Mateusz Jończyk <mat.jonczyk@o2.pl> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Tested-by: Chris Bainbridge <chris.bainbridge@gmail.com> Reported-by: Chris Bainbridge <chris.bainbridge@gmail.com> Closes: https://lore.kernel.org/all/aDtJ92foPUYmGheF@debian.local/ Link: https://lore.kernel.org/r/20250607210608.14835-1-mat.jonczyk@o2.pl Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
5cdd1f7340
commit
39617dc3fa
@@ -697,8 +697,12 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
|
|||||||
{
|
{
|
||||||
u8 irqstat;
|
u8 irqstat;
|
||||||
u8 rtc_control;
|
u8 rtc_control;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock(&rtc_lock);
|
/* We cannot use spin_lock() here, as cmos_interrupt() is also called
|
||||||
|
* in a non-irq context.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&rtc_lock, flags);
|
||||||
|
|
||||||
/* When the HPET interrupt handler calls us, the interrupt
|
/* When the HPET interrupt handler calls us, the interrupt
|
||||||
* status is passed as arg1 instead of the irq number. But
|
* status is passed as arg1 instead of the irq number. But
|
||||||
@@ -732,7 +736,7 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
|
|||||||
hpet_mask_rtc_irq_bit(RTC_AIE);
|
hpet_mask_rtc_irq_bit(RTC_AIE);
|
||||||
CMOS_READ(RTC_INTR_FLAGS);
|
CMOS_READ(RTC_INTR_FLAGS);
|
||||||
}
|
}
|
||||||
spin_unlock(&rtc_lock);
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||||
|
|
||||||
if (is_intr(irqstat)) {
|
if (is_intr(irqstat)) {
|
||||||
rtc_update_irq(p, 1, irqstat);
|
rtc_update_irq(p, 1, irqstat);
|
||||||
@@ -1300,9 +1304,7 @@ static void cmos_check_wkalrm(struct device *dev)
|
|||||||
* ACK the rtc irq here
|
* ACK the rtc irq here
|
||||||
*/
|
*/
|
||||||
if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) {
|
if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) {
|
||||||
local_irq_disable();
|
|
||||||
cmos_interrupt(0, (void *)cmos->rtc);
|
cmos_interrupt(0, (void *)cmos->rtc);
|
||||||
local_irq_enable();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user