Linux 시스템 절전(System Suspend) 및 장치 인터럽트 처리

WBOY
풀어 주다: 2024-02-11 19:00:30
앞으로
1260명이 탐색했습니다.

이 섹션에서는 주로 문제를 해결합니다. 시스템이 절전 상태에 들어갈 때 장치 인터럽트(IRQ)를 일시 중단하는 방법은 무엇입니까? 시스템을 깨울 때 장치 IRQ를 복원하는 방법은 무엇입니까?

일반적으로 시스템이 절전 상태에 들어간 후에는 모든 장치의 IRQ(인터럽트 요청 라인)가 비활성화됩니다. 특정 시점은 장치의 늦은 일시 중지 단계 이후입니다. 아래는 해당 코드입니다(해당 코드는 생략).

static int suspens_enter(suspens_state_t state, bool *wakeup)

{……

error = dpm_suspens_late(PMSG_SUSPEND);---------늦은 정지 단계

오류 = 플랫폼_일시중단_준비_늦음(상태);

다음 코드는 각 장치의 irq를 비활성화합니다

error = dpm_suspens_noirq(PMSG_SUSPEND);——느와르크 단계 진입

오류 = platform_suspens_prepare_noirq(상태);

}

dpm_suspens_noirq 함수에서는 시스템 내 각 장치에 대해 차례대로 호출되어 해당 장치의 noirq 경우 일시정지 콜백 함수를 실행하게 됩니다. 물론 이 전에 suspens_device_irqs 함수를 호출하여 모든 장치의 irq를 비활성화합니다. 장치.

이에 대한 아이디어는 다음과 같습니다. 각 장치 드라이버가 늦은 일시 중단을 완료한 후에는 일시 중단된 장치가 더 이상 인터럽트를 트리거해서는 안 된다는 것이 합리적입니다. 올바르게 일시 중단되지 않은 장치가 여전히 있는 경우 최선의 전략은 장치의 irq를 마스크하여 인터럽트가 제출되지 않도록 하는 것입니다. 또한 이전 코드(인터럽트 핸들러 참조)에서는 IRQ를 공유하는 장치의 상황을 잘 처리하지 못했습니다. IRQ를 공유하는 장치가 일시 중지된 후 인터럽트가 트리거되면 장치가 다음과 같은 문제가 있습니다. 드라이버가 인터럽트 처리기를 준비하지 않았습니다. 일부 시나리오에서는 인터럽트 핸들러가 일시 중단된 장치의 IO 주소 공간에 액세스하여 예측할 수 없는 문제를 일으킬 수 있습니다. 이러한 문제는 디버그하기 어렵기 때문에 장치 noirq 단계에 suspens_device_irqs() 및 콜백 함수를 도입했습니다.

시스템 재개 프로세스 중, 각 장치의 초기 재개 프로세스 전에 각 장치의 IRQ가 다시 열립니다. 구체적인 코드는 다음과 같습니다(일부 관련 없는 코드는 삭제되었습니다):

static int suspens_enter(suspens_state_t state, bool *wakeup)

{……

platform_resume_noirq(state);---먼저 noirq 단계에서 이력서를 실행합니다

dpm_resume_noirq(PMSG_RESUME);-----여기서 irq가 복원된 후 초기 재개 단계로 들어갑니다

platform_resume_early(상태);

dpm_resume_early(PMSG_RESUME);

……}

dpm_resume_noirq 함수에서는 각 장치 드라이버의 noirq 콜백이 호출된 후, restart_device_irqs 함수가 호출되어 각 장치 irq의 활성화를 완료합니다.

2. IRQF_NO_SUSPEND 플래그 소개

물론, 일부 인터럽트는 전체 시스템의 일시 중지-재개 프로세스 동안(부팅되지 않은 CPU가 오프라인 상태로 푸시되고 시스템이 온라인으로 재설정되는 단계를 포함하여 noirq 단계 포함) 트리거 가능한 상태로 유지되어야 합니다. 시스템이 다시 시작됩니다). 간단한 예로는 IPI 및 일부 특수 목적 장치 인터럽트 외에 타이머 인터럽트가 있습니다.

인터럽트를 요청할 때 IRQF_NO_SUSPEND 플래그를 사용하여 IRQ 하위 시스템에 알릴 수 있습니다. 이 인터럽트는 이전 단락에서 설명한 일종의 인터럽트입니다. 시스템의 일시 중지-재개 프로세스 중에 활성화된 상태를 유지해야 합니다. 이 플래그를 사용하면 suspens_device_irqs는 IRQ를 비활성화하지 않으므로 후속 일시 중지 및 재개 프로세스 중에 인터럽트가 활성화된 상태로 유지됩니다. 물론 이것이 인터럽트가 시스템을 깨울 수 있다는 것을 보장하지는 않습니다. 깨우기 목적을 달성하고 싶다면, 활성화_irq_wake를 호출해주세요.

IRQF_NO_SUSPEND 플래그는 이 IRQ를 사용하는 모든 주변 장치에 영향을 미친다는 점에 유의해야 합니다(IRQ는 여러 주변 장치에서 공유할 수 있지만 ARM에서는 사용되지 않음). IRQ가 여러 주변 장치에 의해 공유되고 각 주변 장치가 해당 인터럽트 핸들러를 등록했으며 그 중 하나가 인터럽트를 적용할 때 IRQF_NO_SUSPEND 플래그를 사용하는 경우 시스템이 일시 중지되면(suspens_device_irqs 이후 참조) 각 IRQ가 비활성화된 이유) IRQ에 있는 모든 장치의 인터럽트 핸들러는 일부 장치가 request_irq(또는 다른 인터럽트 등록 함수)를 호출할 때 IRQF_NO_SUSPEND 플래그를 설정하지 않은 경우에도 정상적으로 트리거되고 실행될 수 있습니다. 이 때문에 IRQF_NO_SUSPEND 및 IRQF_SHARED 두 플래그를 동시에 사용하는 것을 최대한 피해야 합니다.

3. 시스템 인터럽트 웨이크업 인터페이스: 활성화_irq_wake() 및 비활성화_irq_wake()

일부 인터럽트는 시스템을 절전 상태에서 깨울 수 있으며, 이를 "시스템을 깨울 수 있는 인터럽트"라고 합니다. 물론 "시스템을 깨울 수 있는 인터럽트"는 시스템을 깨우는 기능을 활성화하도록 구성해야 합니다. 이러한 인터럽트는 일반적으로 작업 조건 중 일반 I/O 인터럽트로 나타납니다. 깨우기 시스템 기능을 활성화할 준비가 될 때만 일부 특수 구성 및 설정이 시작됩니다.

이러한 구성 및 설정은 하드웨어 시스템(예: SOC)의 신호 처리 로직과 관련될 수 있습니다. 다음 HW 블록 다이어그램을 고려해 볼 수 있습니다.

Linux系统休眠(System Suspend)和设备中断处理주변기기 인터럽트 신호는 "일반 인터럽트 신호 처리 모듈"과 "특정 인터럽트 신호 수신 모듈"로 전송됩니다. 정상적으로 동작할 때, "일반 인터럽트 신호 처리 모듈"의 처리 로직을 ON하고, "특정 인터럽트 신호 수신 모듈"의 처리 로직을 OFF로 하겠습니다. 그러나 시스템이 슬립 상태에 들어가면 "일반 인터럽트 신호 처리 모듈"이 꺼졌을 가능성이 있습니다. 이때 인터럽트 신호를 수신하려면 "특정 인터럽트 신호 수신 모듈"을 시작해야 합니다. 시스템 정지-재개 모듈(종종 정지 상태에서 작동할 수 있는 유일한 HW 블록임)은 인터럽트 신호에 의해 정상적으로 깨어날 수 있습니다. 일단 깨어나면 주변 장치의 인터럽트 처리가 일반 작업 모드로 돌아갈 수 있도록 "특정 인터럽트 신호 수신 모듈"을 끄는 것이 좋습니다. 동시에 시스템의 일시 중지-재개 모듈에 대한 불필요한 간섭도 방지합니다.

IRQ 하위 시스템은 이 기능을 완료하기 위해 두 가지 인터페이스 기능을 제공합니다. 활성화_irq_wake() 기능은 시스템 전원 관리 모듈(즉, 위의 일시 중지-재개 모듈)로 이어지는 주변 장치 인터럽트 라인을 여는 데 사용되며, 또 다른 인터페이스입니다. disable_irq_wake()는 주변 장치 인터럽트 라인에서 시스템 전원 관리 모듈까지의 경로에 있는 다양한 HW 블록을 닫는 데 사용됩니다.

enable_irq_wake를 호출하면 시스템 일시 중지 프로세스의 suspens_device_irqs 처리에 영향을 줍니다.

static bool suspens_device_irq(struct irq_desc *desc)

{

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

irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);

참을 반환합니다;

}

인터럽트 비활성화 코드 생략

}

즉, 장치의 인터럽트를 시스템 정지의 깨우기 소스로 설정하기 위해 활성화_irq_wake가 호출되면 주변 장치의 인터럽트는 비활성화되지 않지만 IRQD_WAKEUP_ARMED 플래그로 표시됩니다. 웨이크업 소스가 아닌 인터럽트의 경우 IRQS_SUSPENDED가 suspens_device_irq 함수에 표시되고 장치의 irq가 비활성화됩니다. 시스템 깨우기 프로세스(resume_device_irqs) 중에 비활성화된 인터럽트가 다시 활성화됩니다.

물론 일시 중지 프로세스 중에 일부 이벤트가 발생하여(예: 웨이크업 소스가 유효한 신호를 생성함) 일시 중지가 중단되면 이 중단 이벤트도 PM 코어 모듈에 통보됩니다. 이벤트는 PM 코어 모듈에 즉시 통보될 필요가 없습니다. 일반적으로 일시 중지 스레드는 어떤 시점에서 보류 중인 깨우기 이벤트를 확인합니다.

시스템 일시 중지 프로세스 중에 웨이크업 소스의 각 인터럽트는 일시 중지 프로세스를 종료하거나 시스템을 깨웁니다(시스템이 일시 중지 상태에 들어간 경우). 하지만 이 때 HW는 인터럽트 신호를 발생시키더라도 인터럽트 핸들러를 실행할 수 없습니다. 웨이크업 소스인 IRQ는 어떻게 되나요? 인터럽트가 마스크되지 않았더라도 인터럽트 핸들러는 실행되지 않습니다. (이때 HW 신호는 시스템을 깨우기 위해서만 사용됩니다.) 실행될 수 있는 유일한 인터럽트 핸들러는 IRQF_NO_SUSPEND 플래그로 표시된 IRQ입니다. 왜냐하면 해당 인터럽트는 항상 활성화되어 있기 때문입니다. 물론 이러한 인터럽트는 활성화 소스를 설정하기 위해 활성화_irq_wake를 호출해서는 안 됩니다.

4. 중단 및 유휴 상태

일시 중지("정지" 상태라고도 함)는 비교적 새로운 시스템 전원 관리 상태입니다. 관련 코드는 다음과 같습니다.

static int suspens_enter(suspens_state_t state, bool *wakeup)

{

각 기기의 늦은 정지 단계

noirq 각 장치의 정지 단계

if (상태 == PM_SUSPEND_FREEZE) {

freeze_enter();

플랫폼_wake로 이동;

}

}

Freeze 및 일시 중지의 이전 작업은 기본적으로 동일합니다. 먼저 시스템의 프로세스를 동결한 다음 시스템의 다양한 장치를 일시 중지합니다. 차이점은 noirq 일시 중지가 완료된 후에는 BSP가 아닌 프로세스를 비활성화하지 않는다는 것입니다. . 프로세서 및 syscore 일시 중지 단계이지만 모든 프로세서를 유휴 상태로 푸시하기 위해 Freeze_enter 함수를 호출합니다. 이때 활성화된 인터럽트는 시스템을 깨울 수 있습니다. 이는 IRQF_NO_SUSPEND 플래그(일시중단_device_irqs 프로세스 중에 IRQ가 마스크되지 않음)가 프로세서를 유휴 상태에서 깨울 수 있음을 의미합니다(그러나 이 신호는 시스템 깨우기 신호를 트리거하지 않는다는 점에 유의해야 합니다). 일반 인터럽트는 IRQ가 비활성화되어 있기 때문에 유휴 상태의 프로세서를 깨울 수 없습니다.

시스템을 깨울 수 있는 깨우기 인터럽트는 어떻습니까? 인터럽트가 마스크되지 않기 때문에 시스템은 유휴 상태에서 깨어날 수도 있습니다. 전체 프로세스는 정지 상태에서 시스템을 깨우는 것과 동일합니다. 유일한 차이점은 정지 상태에서 시스템을 깨우는 인터럽트 처리 경로와 시스템을 정지 상태에서 깨우는 깨우기 처리 경로입니다. 인터럽트 처리 로직에 특별한 전원 관리 HW BLOCK이 필요합니다.

5. IRQF_NO_SUSPEND 플래그와 활성화_irq_wake 기능은 동시에 사용할 수 없습니다

기기의 경우 인터럽트를 적용할 때 IRQF_NO_SUSPEND 플래그를 사용하고 동시에 wake-up 소스를 설정하기 위해 활성화_irq_wake를 호출하는 것은 무리입니다.

1. IRQ가 공유되지 않는 경우 IRQF_NO_SUSPEND 플래그를 사용하여 인터럽트 핸들러가 정상적으로 호출될 수 있도록 전체 시스템의 일시 중지-재개 프로세스(suspens_device_irqs 이후 단계 포함) 중에 인터럽트를 열어두기를 원함을 나타냅니다. . 활성화_irq_wake 함수를 호출하는 것은 장치의 irq 신호를 인터럽트 소스로 설정하려는 것이므로 해당 인터럽트 핸들러를 호출할 것으로 예상하지 않음을 나타냅니다. 이 두 가지 요구 사항은 분명히 상호 배타적입니다.

2. IRQF_NO_SUSPEND 플래그나 활성화_irq_wake 함수는 하나의 인터럽트 핸들러에 대한 것이 아니라 IRQ에 등록된 모든 핸들러에 대한 것입니다. 동일한 IRQ에서 웨이크업 소스와 중단 없는 인터럽트 소스를 공유하는 것은 터무니없는 일입니다.

그러나 매우 특별한 상황에서는 IRQ를 웨이크업 소스로 설정하고 IRQF_NO_SUSPEND 플래그를 설정할 수도 있습니다. 코드 논리가 정확하려면 장치의 드라이버 코드가 몇 가지 특별한 요구 사항을 충족해야 합니다.

위 내용은 Linux 시스템 절전(System Suspend) 및 장치 인터럽트 처리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:lxlinux.net
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿