Heim  >  Artikel  >  System-Tutorial  >  Linux-Systemschlaf (System Suspend) und Geräte-Interrupt-Behandlung

Linux-Systemschlaf (System Suspend) und Geräte-Interrupt-Behandlung

WBOY
WBOYnach vorne
2024-02-11 19:00:301245Durchsuche

Dieser Abschnitt löst hauptsächlich ein Problem: Wie kann ich Geräteinterrupts (IRQ) unterbrechen, wenn das System in den Ruhezustand wechselt? Wie kann der Geräte-IRQ beim Aufwecken des Systems wiederhergestellt werden?

Normalerweise wird der IRQ (Interrupt Request Line) aller Geräte deaktiviert, nachdem das System in den Ruhezustand wechselt. Der spezifische Zeitpunkt liegt nach der späten Suspendierungsphase des Geräts. Unten ist der relevante Code (irrelevanter Code weggelassen).

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

error = dpm_suspend_late(PMSG_SUSPEND);----späte Suspendierungsphase

error = platform_suspend_prepare_late(state);

Der folgende Code deaktiviert den IRQ jedes Geräts

error = dpm_suspend_noirq(PMSG_SUSPEND);——Betritt die Noirq-Bühne

error = platform_suspend_prepare_noirq(state);

……

}

In der Funktion „dpm_suspend_noirq“ wird nacheinander „device_suspend_noirq“ für jedes Gerät im System aufgerufen, um die Suspend-Callback-Funktion auszuführen, falls das Gerät „noirq“ ist. Natürlich wird vorher die Funktion „suspend_device_irqs“ aufgerufen, um die IRQs aller Geräte zu deaktivieren Geräte.

Die Idee dahinter ist folgende: Nachdem jeder Gerätetreiber den späten Suspend-Vorgang abgeschlossen hat, liegt es auf der Hand, dass diese angehaltenen Geräte keine Interrupts mehr auslösen sollten. Wenn immer noch einige Geräte nicht ordnungsgemäß angehalten wurden, besteht unsere beste Strategie darin, den IRQ des Geräts zu maskieren, um zu verhindern, dass der Interrupt übermittelt wird. Darüber hinaus haben wir im bisherigen Code (bezogen auf den Interrupt-Handler) die Situation von Geräten, die IRQ teilen, nicht sehr gut gehandhabt. Es gibt ein Problem: Nachdem die Geräte, die IRQ teilen, vollständig angehalten wurden, wird das Gerät ausgelöst, wenn ein Interrupt ausgelöst wird Treiber wird Der Interrupt-Handler ist nicht bereit. In einigen Szenarien greift der Interrupt-Handler auf den E/A-Adressraum des angehaltenen Geräts zu, was zu unvorhersehbaren Problemen führt. Diese Probleme sind schwer zu debuggen, daher haben wir suspend_device_irqs() und die Callback-Funktion in der Device-Noirq-Phase eingeführt.

Während des Systemwiederaufnahmevorgangs wird vor dem frühen Wiederaufnahmevorgang jedes Geräts der IRQ jedes Geräts erneut geöffnet. Der spezifische Code lautet wie folgt (einige irrelevante Codes wurden gelöscht):

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

platform_resume_noirq(state);---Führen Sie zuerst den Lebenslauf in der Noirq-Phase aus

dpm_resume_noirq(PMSG_RESUME);-----Der IRQ wird hier wiederhergestellt und tritt dann in die frühe Lebenslaufphase ein

platform_resume_early(state);

dpm_resume_early(PMSG_RESUME);

……}

In der Funktion „dpm_resume_noirq“ wird der Noirq-Rückruf jedes Gerätetreibers aufgerufen. Anschließend wird die Funktion „resume_device_irqs“ aufgerufen, um die Aktivierung jedes Geräte-IRQ abzuschließen.

2. Über IRQF_NO_SUSPEND Flag

Natürlich müssen einige Interrupts während des Suspend-Resume-Prozesses des gesamten Systems auslösbar bleiben (einschließlich während der Noirq-Phase, einschließlich der Phase, in der die Nicht-Boot-CPU in den Offline-Zustand versetzt und das System danach wieder auf Online zurückgesetzt wird System wird fortgesetzt). Ein einfaches Beispiel ist der Timer-Interrupt, zusätzlich zu IPI und einigen speziellen Geräte-Interrupts.

Beim Anfordern eines Interrupts kann das IRQF_NO_SUSPEND-Flag verwendet werden, um das IRQ-Subsystem zu informieren. Bei diesem Interrupt handelt es sich um den im vorherigen Absatz beschriebenen Interrupttyp: Er muss während des Suspend-Resume-Prozesses des Systems aktiviert bleiben. Mit diesem Flag deaktiviert suspend_device_irqs den IRQ nicht, sodass der Interrupt während nachfolgender Suspend- und Resume-Prozesse aktiviert bleibt. Dies garantiert natürlich nicht, dass der Interrupt das System wecken kann. Wenn Sie den Zweck des Aufwachens erreichen möchten, rufen Sie bitte enable_irq_wake auf.

Es ist zu beachten, dass das IRQF_NO_SUSPEND-Flag alle Peripheriegeräte betrifft, die diesen IRQ verwenden (ein IRQ kann von mehreren Peripheriegeräten gemeinsam genutzt werden, dies wird jedoch in ARM nicht verwendet). Wenn ein IRQ von mehreren Peripheriegeräten gemeinsam genutzt wird und jedes Peripheriegerät einen entsprechenden Interrupt-Handler registriert hat und einer von ihnen bei der Beantragung eines Interrupts das Flag IRQF_NO_SUSPEND verwendet, bleibt das System stehen, wenn es angehalten wird (bezogen auf nach suspend_device_irqs). Grund dafür, dass jeder IRQ deaktiviert wurde), können die Interrupt-Handler aller Geräte am IRQ normal ausgelöst und ausgeführt werden, auch wenn einige Geräte beim Aufruf von request_irq (oder anderen Interrupt-Registrierungsfunktionen) nicht das Flag IRQF_NO_SUSPEND setzen. Aus diesem Grund sollten wir die gleichzeitige Verwendung der beiden Flags IRQF_NO_SUSPEND und IRQF_SHARED möglichst vermeiden.

3. System-Interrupt-Weckschnittstelle: enable_irq_wake() unddisable_irq_wake()

Einige Interrupts können das System aus dem Ruhezustand aufwecken, den wir „Interrupts, die das System aufwecken können“ nennen. Natürlich müssen „Interrupts, die das System aufwecken können“ konfiguriert werden, um die Funktion zum Aufwecken des Systems zu ermöglichen . Solche Interrupts erscheinen im Allgemeinen als gewöhnliche E/A-Interrupts während des Betriebs. Nur bei der Vorbereitung zur Aktivierung der Wecksystemfunktion werden einige spezielle Konfigurationen und Einstellungen initiiert.

Solche Konfigurationen und Einstellungen können mit der Signalverarbeitungslogik auf dem Hardwaresystem (z. B. SOC) zusammenhängen. Wir können das folgende HW-Blockdiagramm betrachten:

Linux系统休眠(System Suspend)和设备中断处理

Peripherie-Interrupt-Signale werden an das „allgemeine Interrupt-Signalverarbeitungsmodul“ und das „spezifische Interrupt-Signal-Empfangsmodul“ gesendet. Im normalen Betrieb schalten wir die Verarbeitungslogik des „allgemeinen Interrupt-Signalverarbeitungsmoduls“ ein und die Verarbeitungslogik des „spezifischen Interrupt-Signal-Empfangsmoduls“ aus. Wenn das System jedoch in den Ruhezustand wechselt, ist es möglich, dass das „allgemeine Interrupt-Signalverarbeitungsmodul“ ausgeschaltet wurde. Zu diesem Zeitpunkt müssen wir das „spezifische Interrupt-Signal-Empfangsmodul“ starten, um das Interrupt-Signal zu empfangen Das Suspend-Resume-Modul des Systems (es ist oft der einzige HW-Block, der im Suspend-Zustand arbeiten kann) und kann normalerweise durch das Interrupt-Signal aufgeweckt werden. Nach dem Aufwachen ist es am besten, das „spezifische Interrupt-Signal-Empfangsmodul“ auszuschalten, damit die periphere Interrupt-Verarbeitung in den normalen Arbeitsmodus zurückkehren kann. Gleichzeitig werden unnötige Störungen des Suspend-Resume-Moduls des Systems vermieden.

Das IRQ-Subsystem stellt zwei Schnittstellenfunktionen zur Vervollständigung dieser Funktion bereit: Die Funktion „enable_irq_wake()“ wird verwendet, um die periphere Interrupt-Leitung zu öffnen, die zum Energieverwaltungsmodul des Systems (d. h. dem oben genannten Suspend-Resume-Modul) führt, und eine weitere Schnittstelle disable_irq_wake(), das zum Schließen verschiedener HW-Blöcke auf dem Pfad von der peripheren Interrupt-Leitung zum System-Energieverwaltungsmodul verwendet wird.

Der Aufruf von enable_irq_wake wirkt sich auf die Verarbeitung von suspend_device_irqs im System-Suspend-Prozess aus. Der Code lautet wie folgt:

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);

true zurückgeben;

}

Lassen Sie den Interrupt-Code zum Deaktivieren weg

}

Mit anderen Worten: Sobald „enable_irq_wake“ aufgerufen wird, um den Interrupt des Geräts als Weckquelle für den System-Suspend festzulegen, wird der Interrupt im Peripheriegerät nicht deaktiviert, sondern mit einem IRQD_WAKEUP_ARMED-Flag markiert. Für Interrupts, die keine Weckquellen sind, wird IRQS_SUSPENDED in der Funktion suspend_device_irq markiert und der IRQ des Geräts wird deaktiviert. Während des Systemweckvorgangs (resume_device_irqs) werden die deaktivierten Interrupts wieder aktiviert.

Wenn während des Suspend-Vorgangs einige Ereignisse auftreten (z. B. die Weckquelle generiert ein gültiges Signal), die zu einem Abbruch des Suspend führen, wird dieses Abbruchereignis natürlich auch dem PM-Kernmodul mitgeteilt. Das Ereignis muss dem PM-Kernmodul nicht sofort mitgeteilt werden. Im Allgemeinen überprüft der Suspend-Thread das ausstehende Weckereignis.

Während des System-Suspend-Prozesses beendet jeder Interrupt von der Weckquelle den Suspend-Prozess oder weckt das System auf (wenn das System in den Suspend-Zustand eingetreten ist). Nach der Ausführung von suspend_device_irqs werden jedoch normale Interrupts blockiert. Selbst wenn die HW zu diesem Zeitpunkt das Interrupt-Signal auslöst, kann sie ihren Interrupt-Handler nicht ausführen. Was passiert mit dem IRQ als Weckquelle? Obwohl sein Interrupt nicht maskiert wurde, wird sein Interrupt-Handler nicht ausgeführt (das HW-Signal wird zu diesem Zeitpunkt nur zum Aufwecken des Systems verwendet). Die einzigen Interrupt-Handler, die eine Chance zur Ausführung haben, sind die IRQs, die mit dem Flag IRQF_NO_SUSPEND gekennzeichnet sind, da ihre Interrupts immer aktiviert sind. Natürlich sollten diese Interrupts nicht enable_irq_wake aufrufen, um die Weckquelle festzulegen.

4. Unterbrechungen und Suspend-to-Idle

Suspend-to-Idle (auch bekannt als „Freeze“-Zustand) ist ein relativ neuer Energieverwaltungszustand des Systems. Der relevante Code lautet wie folgt:

static int suspend_enter(suspend_state_t state, bool *wakeup)

{

……

Late Suspend-Phase jedes Geräts

noirq Suspend-Phase jedes Geräts

if (state == PM_SUSPEND_FREEZE) {

freeze_enter();

gehe zu Platform_wake;

}

……

}

Die vorherigen Vorgänge von Freeze und Suspend sind im Grunde die gleichen: Zuerst werden die Prozesse im System eingefroren und dann die verschiedenen Geräte im System angehalten. Der Unterschied besteht darin, dass nach Abschluss von Noirq Suspend das Einfrieren diese Nicht-BSP-Prozesse nicht deaktiviert . Prozessor- und Syscore-Suspendierungsphase, ruft jedoch die Funktion freeze_enter auf, um alle Prozessoren in den Ruhezustand zu versetzen. Zu diesem Zeitpunkt kann jeder aktivierte Interrupt das System aufwecken. Und das bedeutet, dass diese Flags IRQF_NO_SUSPEND (deren IRQ während des suspend_device_irqs-Prozesses nicht maskiert wird) in der Lage sind, den Prozessor aus dem Ruhezustand aufzuwecken (es ist jedoch zu beachten, dass dieses Signal kein System-Aufwecksignal auslöst) und Gewöhnliche Interrupts können den Prozessor im Ruhezustand nicht aufwecken, da ihr IRQ deaktiviert ist.

Was ist mit Weckunterbrechungen, die das System aufwecken können? Da sein Interrupt nicht maskiert ist, kann das System auch aus dem Suspend-to-Idle-Zustand aufgeweckt werden. Der gesamte Vorgang ist derselbe wie das Aufwecken des Systems aus dem angehaltenen Zustand. Der einzige Unterschied besteht darin, dass der Interrupt-Verarbeitungspfad das System aus dem eingefrorenen Zustand aufweckt und der Aufweck-Verarbeitungspfad das System aus dem angehaltenen Zustand aufweckt. erfordern einen speziellen Power-Management-HW-BLOCK, der an der Interrupt-Verarbeitungslogik beteiligt ist.

5. Das IRQF_NO_SUSPEND-Flag und die Funktion enable_irq_wake können nicht gleichzeitig verwendet werden

Für ein Gerät ist es unvernünftig, bei der Beantragung eines Interrupts das Flag IRQF_NO_SUSPEND zu verwenden und gleichzeitig enable_irq_wake aufzurufen, um die Weckquelle festzulegen. Die Hauptgründe sind folgende:

1. Wenn der IRQ nicht gemeinsam genutzt wird, verwenden Sie das Flag IRQF_NO_SUSPEND, um anzugeben, dass Sie den Interrupt während des Suspend-Resume-Prozesses des gesamten Systems (einschließlich der Phase nach suspend_device_irqs) offen halten möchten, damit sein Interrupt-Handler normal aufgerufen werden kann . Der Aufruf der Funktion „enable_irq_wake“ zeigt an, dass Sie das IRQ-Signal des Geräts als Interrupt-Quelle festlegen möchten, sodass Sie nicht damit rechnen, seinen Interrupt-Handler aufzurufen. Diese beiden Anforderungen schließen sich offensichtlich gegenseitig aus.

2. Weder das Flag IRQF_NO_SUSPEND noch die Funktion enable_irq_wake gelten für einen Interrupt-Handler, sondern für alle registrierten Handler im IRQ. Es ist lächerlich, die Weckquelle und die No-Suspend-Interrupt-Quelle auf demselben IRQ zu teilen.

Unter ganz besonderen Umständen kann jedoch ein IRQ als Weckquelle festgelegt und auch das Flag IRQF_NO_SUSPEND gesetzt werden. Damit die Codelogik korrekt ist, muss der Treibercode des Geräts einige besondere Anforderungen erfüllen.

Das obige ist der detaillierte Inhalt vonLinux-Systemschlaf (System Suspend) und Geräte-Interrupt-Behandlung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:lxlinux.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen