Linux system hibernation and device interrupt handling

Source: Internet
Author: User

I. Suspend and resume of equipment IRQ

This section addresses the question of how to suspend a device interrupt (IRQ) during system hibernation. How do I resume a device IRQ during a wake-up from hibernation?

In general, the IRQ (interrupt request line) of each device is disable at the end of the system suspend process. The specific point in time is after the late suspend phase of each device. The code is as follows (some extraneous code is removed):

static int Suspend_enter (suspend_state_t State, BOOL *wakeup)

{......

Error = Dpm_suspend_late (pmsg_suspend);-----late suspend stage

Error = Platform_suspend_prepare_late (state);

The following code will disable the IRQ for each device

Error = DPM_SUSPEND_NOIRQ (pmsg_suspend);----Enter the NOIRQ stage

Error = PLATFORM_SUSPEND_PREPARE_NOIRQ (state);

......

}

In the DPM_SUSPEND_NOIRQ function, each device in the system is called DEVICE_SUSPEND_NOIRQ to execute the suspend callback function NOIRQ the device, of course, Before this, the Suspend_device_irqs function is called to disable the IRQ for all devices.

The idea is this: after each device driver has completed the late suspend, it is supposed that the devices that have been suspend should not be triggered again. If there are some devices that are not properly suspend, then our best strategy is to mask the IRQ of the device, thus preventing the interruption from being submitted. In addition, in the past code (referred to as interrupt handler), we did not handle the device sharing IRQ very well, there is a problem: after the device that shared the IRQ completes the suspend, if there is an interrupt trigger, then the device-driven interrupt Handler is not ready. In some scenarios, interrupt handler accesses the IO address space of the suspend device, leading to unpredictable issue. These issue are difficult to debug, so we introduce Suspend_device_irqs () and callback functions in the NOIRQ phase of the device.

During the system resume process, the IRQ for each device is reopened before the early resume process for each device, with the following code (some unrelated code removed):

static int Suspend_enter (suspend_state_t State, BOOL *wakeup)

{......

PLATFORM_RESUME_NOIRQ (state);---first perform NOIRQ stage resume

DPM_RESUME_NOIRQ (Pmsg_resume), where the IRQ is restored and then enters the early resume phase

Platform_resume_early (state);

Dpm_resume_early (Pmsg_resume);

......}

In the DPM_RESUME_NOIRQ function, each device-driven NOIRQ callback is called, after which the Resume_device_irqs function is called to complete the enable of each device IRQ.

Ii. about Irqf_no_suspend Flag

Of course, some interrupts need to remain in a state that can be triggered during the suspend-resume of the entire system, including during the NOIRQ phase, including when the Nonboot CPU is pushed to the offline state and the system resume is reset to the online stage. A simple example is a timer interrupt, which is also required for the IPI and some special purpose device interrupts.

In the interrupt request, Irqf_no_suspend flag can be used to inform the IRQ subsystem that the interrupt is the one described in the previous text: the need to remain enabled during the suspend-resume of the system. With this Flag,suspend_device_irqs, the IRQ is not disable, allowing the interrupt to remain open during subsequent suspend and resume processes. Of course, this does not guarantee that the interrupt can wake the system. If you want to achieve the purpose of wakeup, call Enable_irq_wake.

It is important to note that Irqf_no_suspend flag affects all peripherals that use the IRQ (an IRQ can be shared by multiple peripherals, but not in arm). If an IRQ is shared by multiple peripherals, and the respective peripherals are registered with the corresponding interrupt handler, if one uses Irqf_no_suspend flag when the request is interrupted, the system SUSPEND (referring to Suspend_ After Device_irqs, the IRQ has been disable), all the devices on the IRQ interrupt handler can be triggered by the normal execution, even if some devices are called REQUEST_IRQ (or other interrupt registration function) Irqf_no_suspend flag was not set at the time. Because of this, we should avoid using the two flags of Irqf_no_suspend and irqf_shared as much as possible.

Third, system interrupt wake-up interface: Enable_irq_wake () and Disable_irq_wake ()

Some interrupts can wake up the system from sleep, we call it "can wake up the system's interruption", of course, "can wake up the system interrupts" need to configure to start the wake-up system such functions. Such interrupts are typically present as normal I/O interrupt when working, so that special configurations and settings are initiated only when the system is ready to be awakened.

Such configurations and settings may be related to the signal processing logic on the hardware system (e.g. SOC), and we can consider the following HW block diagram:

The interrupt signal of the peripheral is sent to the "General interrupt signal Processing module" and "the specific interrupt signal receiving module". During normal operation, we will turn the processing logic on the "General Interrupt Signal Processing module", while turn off the processing logic of the "specific interrupt signal receiving module". However, when the system goes to sleep, it is possible that the "General interrupt Signal Processing module" is off, when we need to start the "specific interrupt signal receiving module" to receive the interrupt signal, This allows the system to suspend-resume the module (which is often the only HW block that can work when the suspend state) can be woken up normally by the interrupt signal. Once awakened, we'd better be turn off "specific interrupt signal receiving module", allowing the peripheral interrupt processing back to normal mode of operation, while also avoiding unnecessary interference from the system Suspend-resume module.

The IRQ subsystem provides two interface functions to complete this function: the Enable_irq_wake () function is used to open the path to the System power management module (i.e. the Suspend-resume module above) in the peripheral, and the other interface is Disable_irq_ Wake (), which turns off the various HW blocks that are disconnected from the peripheral to the System power Management module path.

Call the Enable_irq_wake will affect the system suspend process Suspend_device_irqs processing, the code is as follows:

static bool Suspend_device_irq (struct IRQ_DESC *desc)

{

......

if (Irqd_is_wakeup_set (&desc->irq_data)) {

Irqd_set (&desc->irq_data, irqd_wakeup_armed);

return true;

}

Omit code for Disable interrupts

}

That is, once the call to Enable_irq_wake sets the interrupt of the device as the wake-up source of the system suspend, the interrupt in that peripheral is not disable, but is marked with a irqd_wakeup_armed token. For interrupts that are not wakeup source, the SUSPEND_DEVICE_IRQ function marks the irqs_suspended and disable the device's IRQ. During system wake-up (Resume_device_irqs), Diable interrupts are re-enable.

Of course, if certain events occur during the suspend process (for example, wakeup source generates a valid signal), resulting in this suspend abort, the Abort event is also notified to the PM core module. The event does not need to be immediately notified to the PM core module, in general, suspend thread will check pending's wakeup event at certain points.

During system suspend, each interrupt from wakeup source terminates the suspend process or wakes the system (if the system has entered the suspend state). However, after the Suspend_device_irqs is executed, the normal interrupt is masked, and the interrupt handler cannot be performed even if the HW triggers the interrupt signal. What about the IRQ as wakeup source? Although its interrupt has not been mask off, but its interrupt handler will not execute (this time the HW signal only to wake the system). The only interrupt handler that have the opportunity to execute are those that mark Irqf_no_suspend flag because their interrupts are always enable. Of course, these interrupts should not call Enable_irq_wake for the wake-up source setting.

Iv. Interrupts and Suspend-to-idle

Suspend-to-idle (also known as the "freeze" state) is a relatively new system power management state, with the following code:

static int Suspend_enter (suspend_state_t State, BOOL *wakeup)

{

......

Late suspend stage for each device

NOIRQ suspend stage for each device

if (state = = Pm_suspend_freeze) {

Freeze_enter ();

Goto Platform_wake;

}

......

}

The freeze and suspend operations are basically the same: first freeze the process in the system, then the various device in the suspend system, not the same place after the NOIRQ suspend is complete, Instead of disable those non-bsp processors and syscore suspend stages, freeze calls the Freeze_enter function and pushes all the processors to the idle state. At this point, any enable interrupts can wake the system. This also means that the tag irqf_no_suspend (whose IRQ is not being blocked during the Suspend_device_irqs process) is capable of waking the processor from the idle state (however, it is important to note that this signal does not trigger a system wake-up signal) , and the normal interrupt cannot wake the processor in the idle state because its IRQ is disable.

What about the wakeup interrupt that can wake up the system? The system can also be awakened from the Suspend-to-idle state because its interrupts are not mask-off. The whole process is the same as waking the system from the suspend state, except that the interrupt processing path that wakes the system from the freeze state, and the wake-up processing path that wakes the system from the suspend state, requires the participation of special interrupt handling logic in the power management HW block.

V. Irqf_no_suspend flag and Enable_irq_wake function cannot be used simultaneously

For a device, it is unreasonable to use Irqf_no_suspend flag at the time of application interruption and call Enable_irq_wake to set wake-up source, mainly for the following reasons:

1. If the IRQ is not shared, use Irqf_no_suspend flag to indicate that you want to be in the suspend-resume process of the entire system (including Suspend_device_ IRQs) remains interrupted to open in order to call its interrupt handler normally. The call to the Enable_irq_wake function indicates that you want to set the IRQ signal for the device as the interrupt source, so you do not expect to call its interrupt handler. And these two requirements are clearly mutually exclusive.

2, the Irqf_no_suspend flag and the Enable_irq_wake function are not for a interrupt handler, but for all registered handler on that IRQ. It is absurd to share wake sources on an IRQ and no suspend interrupt source.

However, in very special situations, an IRQ can be set to wakeup source and also set the Irqf_no_suspend flag. For the code logic to be correct, the device's driver code needs to meet some special requirements.

Reference documents

1. Kernel Document Power/suspend-and-interrupts.txt

Linux system hibernation and device interrupt handling

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.