Linux 커널은 공유 IRQ를 어떻게 처리합니까?


14

지금까지 읽은 내용에 따르면 "커널이 인터럽트를 수신하면 등록 된 모든 핸들러가 호출됩니다."

각 IRQ에 대해 등록 된 핸들러는을 통해 볼 수 있으며 /proc/interrupts등록 된 핸들러는 request_irq대략 콜백을 전달 하는 드라이버에서 나온 것임을 이해합니다 .

irqreturn_t (*handler)(int, void *)

내가 아는 것을 기반으로 특정 IRQ와 관련된 각 인터럽트 처리기 콜백을 호출해야하며, 인터럽트가 실제로 처리해야하는지 여부를 결정하는 것은 처리기의 책임입니다. 핸들러가 특정 인터럽트를 처리하지 않으면 커널 매크로를 반환해야합니다 IRQ_NONE.

내가 이해하는 데 어려움이있는 것은 각 드라이버가 인터럽트를 처리 해야하는지 여부를 결정하는 방법입니다. 인터럽트를 예상 해야하는 경우 내부적으로 추적 할 수 있다고 생각합니다. 그렇다면 같은 IRQ 뒤에있는 여러 드라이버가 인터럽트를 예상하는 상황을 어떻게 처리 할 수 ​​있을지 모르겠습니다.

내가이 세부 사항을 이해하려고하는 이유 kexec는 PCIe 브리지 및 다운 스트림 PCI에서 리셋 핀과 다양한 레지스터를 사용하면서 시스템 작동 중에 커널을 다시 실행 하는 메커니즘을 망쳐 놓기 때문 입니다. 장치. 그리고 그렇게하면 재부팅 후 커널 패닉이 발생하거나 다른 드라이버가 아무런 작업도 일어나지 않아도 인터럽트를 받고 있다고 불평합니다.

핸들러가 인터럽트를 처리해야한다고 결정한 방법은 미스터리입니다.

편집 : 관련이있는 경우 문제의 CPU 아키텍처는 x86입니다.


1
stackoverflow.com/questions/14371513/for-a-shared-interrupt-line-how-do-i-find-which-interrupt-handler-to-usec
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

답변:


14

이에 대해서는 Corbet 등 의 Linux Device Drivers 3 판 10 장 에서 다룹 니다 . 온라인 으로 무료로 이용할 수 있으며 , 죽은 나무 나 전자 책 양식 으로 오라일리의 방법던질 수도 있습니다 . 질문과 관련된 부분은 첫 번째 링크의 278 페이지에서 시작합니다.

가치있는 것을 위해, 여기에 세 페이지와 내가 구글 화 한 다른 비트를 바꾸어 놓으려는 시도가 있습니다.

  • 공유 IRQ 핸들러를 등록하면 커널은 다음 중 하나를 확인합니다.

    ㅏ. 해당 인터럽트에 대한 다른 처리기가 없거나

    비. 모든 이전에 등록의 요청 인터럽트 공유

    두 경우 모두 적용되면 dev_id매개 변수가 고유한지 확인 하여 커널이 처리기를 제거하는 등의 여러 처리기를 구분할 수 있습니다.

  • PCI¹ 하드웨어 장치가 IRQ 회선을 올리면 커널의 저수준 인터럽트 핸들러가 호출되고 모두가 되고 등록 된dev_id 통해 처리기를 등록하는 데 사용 된 각각을 다시 전달합니다 request_irq().

    dev_id값은 기계 고유해야합니다. 이를 수행하는 일반적인 방법은 장치 당 포인터를 전달하는 것입니다.struct 드라이버가 해당 장치를 관리하는 데 사용하는 입니다. 이 운전자에게 도움이 될 수 있도록이 포인터는 드라이버의 메모리 공간 내에 있어야하므로,은 사실상 IPSO 그 driver.²에 고유 한

    주어진 인터럽트에 등록 된 다수의 드라이버가있는 경우, 그들은 것 모두 때 호출 중 하나를 장치 해당 공유 인터럽트 라인을 발생 . 드라이버 장치가이 작업을 수행하지 않은 경우 드라이버의 인터럽트 처리기에 해당 dev_id값이 아닌 값이 전달 됩니다. 이러한 상황이 발생하면 드라이버의 인터럽트 처리기가 즉시 반환해야합니다.

    또 다른 경우는 드라이버가 여러 장치를 관리하는 것입니다. 드라이버의 인터럽트 핸들러는 dev_id드라이버에 알려진 값 중 하나를 가져옵니다 . 코드는 각 장치를 폴링하여 어떤 장치가 인터럽트를 발생 시켰는지 확인해야합니다.

    Corbet 의 예 . PC 병렬 포트의 것입니다. 인터럽트 라인을 선언 할 때 첫 번째 장치 레지스터의 최상위 비트도 설정합니다. (즉, inb(0x378) & 0x80 == true표준 I / O 포트 번호를 가정하면) 처리기가이를 감지하면 작업을 수행 한 다음 I / O 포트에서 읽은 값을 맨 위에있는 포트에 다시 써서 IRQ를 지우십시오. 조금 지워졌습니다.

    특정 메커니즘이 특별한 이유는 없습니다. 다른 하드웨어 장치는 다른 메커니즘을 선택할 수 있습니다. 유일하게 중요한 것은 장치가 공유 인터럽트를 허용하기 위해서는 드라이버가 장치의 인터럽트 상태 를 읽을 수 있는 방법과 인터럽트 를 지우는 방법이 있어야 한다는 것입니다. 특정 장치가 사용하는 메커니즘을 찾으려면 장치의 데이터 시트 또는 프로그래밍 설명서를 읽어야합니다.

  • 인터럽트 핸들러가 커널에게 인터럽트를 처리했다고 알리면 커널이 동일한 인터럽트에 대해 등록 된 다른 핸들러를 계속 호출하지 않습니다. 레벨 트리거 인터럽트를 사용할 때 인터럽트 라인을 공유해야하는 경우 피할 수 없습니다.

    두 장치가 동시에 동일한 인터럽트 라인을 가정한다고 상상해보십시오. (또는 적어도 시간이 너무 가깝기 때문에 커널은 인터럽트 핸들러를 호출하여 라인을 지우고 두 번째 어설 션을 개별적으로 볼 시간이 없습니다.) 커널은 해당 인터럽트 라인에 대한 모든 핸들러를 호출하여 각 인터럽트 라인을 제공해야합니다. 관련된 하드웨어를 쿼리하여주의가 필요한지 확인할 수 있습니다. 두 개의 서로 다른 드라이버가 주어진 인터럽트에 대한 처리기 목록을 통해 동일한 패스 내에서 인터럽트를 성공적으로 처리 할 수 ​​있습니다.

    이 때문에 드라이버가 인터럽트 처리기가 반환되기 전에 언젠가 인터럽트 어설 션을 지우도록 장치에 알리는 것이 필수적입니다. 그렇지 않으면 어떻게되는지 명확하지 않습니다. 지속적으로 선언 된 인터럽트 라인은 커널이 공유 인터럽트 핸들러를 지속적으로 호출하게하거나, 새로운 인터럽트를 볼 수있는 커널의 기능을 가리므로 핸들러가 호출되지 않습니다. 어느 쪽이든, 재앙.


각주 :

  1. 위의 모든 것이 원래 PCI 사양에서 사용되는 레벨 트리거 인터럽트를 가정하기 때문에 위에서 PCI를 지정했습니다 . ISA는 에지 트리거 인터럽트를 사용하여 공유를 가장 까다 로웠으며 하드웨어가 지원하는 경우에만 가능했습니다. PCIe는 메시지 신호 인터럽트를 사용 합니다 . 인터럽트 메시지는 PCI 인터럽트 공유에 필요한 라운드 로빈 추측 게임을 피하기 위해 커널이 사용할 수있는 고유 한 값을 포함합니다. PCIe는 인터럽트 공유의 필요성을 제거 할 수 있습니다. (실제로 가능한지 모르겠지만 잠재력이 있다고 생각합니다.)

  2. Linux 커널 드라이버는 모두 동일한 메모리 공간을 공유하지만 관련이없는 드라이버는 다른 메모리 공간에서 움직이지 않아야합니다. 포인터를 지나치지 않으면 다른 운전자가 실수로 같은 값을 얻지 못할 수 있습니다.


1
언급했듯이 인터럽트 핸들러는 dev_id소유하지 않은 것으로 전달 될 수 있습니다 . 나에게 그것은 dev_id구조를 소유하지 않은 운전자가 내용을 해석하는 방법에 따라 여전히 그것을 자신의 것으로 착각 할 가능성이 0이 아닌 것처럼 보입니다 . 그렇지 않은 경우 어떤 메커니즘으로이를 방지 할 수 있습니까?
bsirang

dev_id드라이버의 메모리 공간 내에 무언가를 가리키는 포인터를 만들어 방지 할 수 있습니다. 다른 드라이버는 업 할 dev_id일어난 드라이버가 소유 메모리에 대한 포인터와 혼동 할을하지만 모두가 규칙에 의해 연주되어 있기 때문에 일이 없을거야. 이것은 커널 공간입니다. 자제는 사용자 공간 코드와 달리 당연한 것으로 간주되며 금지되지 않은 것은 허용된다고 가정 할 수 있습니다.
워렌 영

LDD3의 장 십에 따르면 "두 개 이상의 드라이버가 인터럽트 라인과 하드웨어 인터럽트 그 라인의 프로세서를 공유 할 때마다, 커널은 각각 자신의 dev_id 통과, 그 인터럽트에 등록 된 모든 핸들러를 호출" 그것은 이전의 이해 것 같아를 인터럽트 핸들러가 dev_id소유하지 않은 것으로 전달 될 수 있는지에 대해서는 올바르지 않습니다.
bsirang

그것은 내 잘못 읽었습니다. 내가 썼을 때, 나는 두 가지 개념을 혼란스럽게 만들고있었습니다. 내 답변을 편집했습니다. 인터럽트 핸들러가 빠르게 리턴해야하는 조건은 관리하지 않는 디바이스에 의한 인터럽트 어설 션으로 인해 호출되는 것입니다. 값은 dev_id이것이 발생했는지 확인하는 데 도움이되지 않습니다. 하드웨어에 "당신은 울렸다?"
워렌 영

예, 이제 내가 땜질하는 것이 실제로 다른 드라이버가 커널을 다시 시작한 후 장치가 "범위"를 갖게 된 이유를 알아 내야합니다 kexec.
bsirang

4

드라이버가 공유 IRQ를 요청하면 드라이버의 메모리 공간 내의 장치 특정 구조에 대한 참조에 대한 포인터를 커널에 전달합니다.

LDD3에 따르면 :

두 개 이상의 드라이버가 인터럽트 라인을 공유하고 하드웨어가 해당 라인의 프로세서를 인터럽트 할 때마다 커널은 해당 인터럽트에 등록 된 모든 핸들러를 호출하여 각각의 자체 dev_id를 전달합니다.

여러 드라이버의 IRQ 핸들러를 확인하면 인터럽트 또는 리턴을 처리해야하는지 여부를 결정하기 위해 하드웨어 자체를 검사합니다. IRQ_NONE .

UHCI-HCD 드라이버
  status = inw(uhci->io_addr + USBSTS);
  if (!(status & ~USBSTS_HCH))  /* shared interrupt, not mine */
    return IRQ_NONE;

위 코드에서 드라이버는 USBSTS 서비스에 인터럽트가 있는지 확인하기 위해 레지스터를 있습니다.

SDHCI 드라이버
  intmask = sdhci_readl(host, SDHCI_INT_STATUS);

  if (!intmask || intmask == 0xffffffff) {
    result = IRQ_NONE;
    goto out;
  }

이전 예제에서와 같이 드라이버는 SDHCI_INT_STATUS인터럽트를 서비스해야하는지 여부를 결정하기 위해 상태 레지스터 를 확인합니다.

Ath5k 드라이버
  struct ath5k_softc *sc = dev_id;
  struct ath5k_hw *ah = sc->ah;
  enum ath5k_int status;
  unsigned int counter = 1000;

  if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
        !ath5k_hw_is_intr_pending(ah)))
    return IRQ_NONE;

하나만 더 예.


0

링크를 확인하십시오 방문하십시오 :

메모리 매핑 레지스터에서 IRQ 상태를 확인한 후에 만 ​​IRQ 처리기에서 하반부 또는 다른 논리를 트리거하는 것이 일반적입니다. 따라서 문제는 기본적으로 훌륭한 프로그래머가 해결합니다.


귀하의 링크 내용을 사용할 수 없습니다
user3405291
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.