mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	PM: s2idle: ACPI: Fix wakeup interrupts handling
After commite3728b50cd("ACPI: PM: s2idle: Avoid possible race related to the EC GPE") wakeup interrupts occurring immediately after the one discarded by acpi_s2idle_wake() may be missed. Moreover, if the SCI triggers again immediately after the rearming in acpi_s2idle_wake(), that wakeup may be missed too. The problem is that pm_system_irq_wakeup() only calls pm_system_wakeup() when pm_wakeup_irq is 0, but that's not the case any more after the interrupt causing acpi_s2idle_wake() to run until pm_wakeup_irq is cleared by the pm_wakeup_clear() call in s2idle_loop(). However, there may be wakeup interrupts occurring in that time frame and if that happens, they will be missed. To address that issue first move the clearing of pm_wakeup_irq to the point at which it is known that the interrupt causing acpi_s2idle_wake() to tun will be discarded, before rearming the SCI for wakeup. Moreover, because that only reduces the size of the time window in which the issue may manifest itself, allow pm_system_irq_wakeup() to register two second wakeup interrupts in a row and, when discarding the first one, replace it with the second one. [Of course, this assumes that only one wakeup interrupt can be discarded in one go, but currently that is the case and I am not aware of any plans to change that.] Fixes:e3728b50cd("ACPI: PM: s2idle: Avoid possible race related to the EC GPE") Cc: 5.4+ <stable@vger.kernel.org> # 5.4+ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
		
							parent
							
								
									dc0075ba7f
								
							
						
					
					
						commit
						cb1f65c1e1
					
				
					 6 changed files with 42 additions and 13 deletions
				
			
		| 
						 | 
					@ -758,6 +758,7 @@ bool acpi_s2idle_wake(void)
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pm_wakeup_clear(acpi_sci_irq);
 | 
				
			||||||
		rearm_wake_irq(acpi_sci_irq);
 | 
							rearm_wake_irq(acpi_sci_irq);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ suspend_state_t pm_suspend_target_state;
 | 
				
			||||||
bool events_check_enabled __read_mostly;
 | 
					bool events_check_enabled __read_mostly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* First wakeup IRQ seen by the kernel in the last cycle. */
 | 
					/* First wakeup IRQ seen by the kernel in the last cycle. */
 | 
				
			||||||
unsigned int pm_wakeup_irq __read_mostly;
 | 
					static unsigned int wakeup_irq[2] __read_mostly;
 | 
				
			||||||
 | 
					static DEFINE_RAW_SPINLOCK(wakeup_irq_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* If greater than 0 and the system is suspending, terminate the suspend. */
 | 
					/* If greater than 0 and the system is suspending, terminate the suspend. */
 | 
				
			||||||
static atomic_t pm_abort_suspend __read_mostly;
 | 
					static atomic_t pm_abort_suspend __read_mostly;
 | 
				
			||||||
| 
						 | 
					@ -942,19 +943,45 @@ void pm_system_cancel_wakeup(void)
 | 
				
			||||||
	atomic_dec_if_positive(&pm_abort_suspend);
 | 
						atomic_dec_if_positive(&pm_abort_suspend);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pm_wakeup_clear(bool reset)
 | 
					void pm_wakeup_clear(unsigned int irq_number)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pm_wakeup_irq = 0;
 | 
						raw_spin_lock_irq(&wakeup_irq_lock);
 | 
				
			||||||
	if (reset)
 | 
					
 | 
				
			||||||
 | 
						if (irq_number && wakeup_irq[0] == irq_number)
 | 
				
			||||||
 | 
							wakeup_irq[0] = wakeup_irq[1];
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							wakeup_irq[0] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wakeup_irq[1] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						raw_spin_unlock_irq(&wakeup_irq_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!irq_number)
 | 
				
			||||||
		atomic_set(&pm_abort_suspend, 0);
 | 
							atomic_set(&pm_abort_suspend, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pm_system_irq_wakeup(unsigned int irq_number)
 | 
					void pm_system_irq_wakeup(unsigned int irq_number)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (pm_wakeup_irq == 0) {
 | 
						unsigned long flags;
 | 
				
			||||||
		pm_wakeup_irq = irq_number;
 | 
					
 | 
				
			||||||
 | 
						raw_spin_lock_irqsave(&wakeup_irq_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (wakeup_irq[0] == 0)
 | 
				
			||||||
 | 
							wakeup_irq[0] = irq_number;
 | 
				
			||||||
 | 
						else if (wakeup_irq[1] == 0)
 | 
				
			||||||
 | 
							wakeup_irq[1] = irq_number;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							irq_number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						raw_spin_unlock_irqrestore(&wakeup_irq_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (irq_number)
 | 
				
			||||||
		pm_system_wakeup();
 | 
							pm_system_wakeup();
 | 
				
			||||||
	}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned int pm_wakeup_irq(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return wakeup_irq[0];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -497,14 +497,14 @@ extern void ksys_sync_helper(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* drivers/base/power/wakeup.c */
 | 
					/* drivers/base/power/wakeup.c */
 | 
				
			||||||
extern bool events_check_enabled;
 | 
					extern bool events_check_enabled;
 | 
				
			||||||
extern unsigned int pm_wakeup_irq;
 | 
					 | 
				
			||||||
extern suspend_state_t pm_suspend_target_state;
 | 
					extern suspend_state_t pm_suspend_target_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern bool pm_wakeup_pending(void);
 | 
					extern bool pm_wakeup_pending(void);
 | 
				
			||||||
extern void pm_system_wakeup(void);
 | 
					extern void pm_system_wakeup(void);
 | 
				
			||||||
extern void pm_system_cancel_wakeup(void);
 | 
					extern void pm_system_cancel_wakeup(void);
 | 
				
			||||||
extern void pm_wakeup_clear(bool reset);
 | 
					extern void pm_wakeup_clear(unsigned int irq_number);
 | 
				
			||||||
extern void pm_system_irq_wakeup(unsigned int irq_number);
 | 
					extern void pm_system_irq_wakeup(unsigned int irq_number);
 | 
				
			||||||
 | 
					extern unsigned int pm_wakeup_irq(void);
 | 
				
			||||||
extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 | 
					extern bool pm_get_wakeup_count(unsigned int *count, bool block);
 | 
				
			||||||
extern bool pm_save_wakeup_count(unsigned int count);
 | 
					extern bool pm_save_wakeup_count(unsigned int count);
 | 
				
			||||||
extern void pm_wakep_autosleep_enabled(bool set);
 | 
					extern void pm_wakep_autosleep_enabled(bool set);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -504,7 +504,10 @@ static ssize_t pm_wakeup_irq_show(struct kobject *kobj,
 | 
				
			||||||
					struct kobj_attribute *attr,
 | 
										struct kobj_attribute *attr,
 | 
				
			||||||
					char *buf)
 | 
										char *buf)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return pm_wakeup_irq ? sprintf(buf, "%u\n", pm_wakeup_irq) : -ENODATA;
 | 
						if (!pm_wakeup_irq())
 | 
				
			||||||
 | 
							return -ENODATA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sprintf(buf, "%u\n", pm_wakeup_irq());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
power_attr_ro(pm_wakeup_irq);
 | 
					power_attr_ro(pm_wakeup_irq);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,7 +134,7 @@ int freeze_processes(void)
 | 
				
			||||||
	if (!pm_freezing)
 | 
						if (!pm_freezing)
 | 
				
			||||||
		atomic_inc(&system_freezing_cnt);
 | 
							atomic_inc(&system_freezing_cnt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pm_wakeup_clear(true);
 | 
						pm_wakeup_clear(0);
 | 
				
			||||||
	pr_info("Freezing user space processes ... ");
 | 
						pr_info("Freezing user space processes ... ");
 | 
				
			||||||
	pm_freezing = true;
 | 
						pm_freezing = true;
 | 
				
			||||||
	error = try_to_freeze_tasks(true);
 | 
						error = try_to_freeze_tasks(true);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,8 +136,6 @@ static void s2idle_loop(void)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		pm_wakeup_clear(false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		s2idle_enter();
 | 
							s2idle_enter();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue