인터럽트의 경우 인터럽트 sequence가 진행중에 또 인터럽트가 발생할 수 있잖아요?
그럼 현재 인터럽트 진행중이던 context를 pcb에 저장하고 새로운 인터럽트를 수행하고 다시 pcb에서 불러와서 이어서 진행하는 방식인가요?
x86은 인터럽트 스택이 있다고 들은 것 같고, arm은 아니라고 들은 것 같은데, 그럼 어떻게 인터럽트가 중첩되는 것을 수행하는지 헷깔리네요.
중첩되면 중첩되는대로 pcb가 새로 생성되는 것인가요? 그리고 스택처럼 돌아가는 건지 궁금합니다.
그러니까
user -> packet 수신 -> packet 수신 -> packet 수신
이런식으로 pakcet에 대한 처리가 끝나기 전에 인터럽트가 또 발생했을 때 처리가 어떻게 진행되는 지 궁금해서요.
인터럽트 라인을 막아놓고 처리하는 게 아니라고 이해한 상태여서 헷깔리네요.;
댓글 6
-
문c(문영일)
2020.07.08 11:30
-
코딩의노예
2020.07.08 13:58
답변 감사합니다.
그렇다면 4)번 구간에서 발생하게 되면, 이전에 동작하던 irq context는 어디에 저장했다가 다시 복귀하는 것인가요?
인터럽트가 발생할 떄마다 pcb가 존재하는 것인가요?
-
문c(문영일)
2020.07.09 16:43
nest된 irq context의 저장은 현재 동작 중인 커널 스택에 context를 push하여 저장합니다.
context는 커널 및 유저에 따라 struct pt_regs 또는 struct user_pt_regs를 사용합니다.
그리고 인터럽트가 발생 시 커널에서 pcb(OS 설계론에서 사용된 process control block) 영역에 저장된
해당 태스크(커널 스레드이든 유저 스레드)를 빠르게 찾을 수 있습니다.
arm의 경우 pcb는 현재 커널 스택의 마지막 부분(주소로는 앞부분)에 커널 컴파일 옵션에 따라
struct task 또는 struct thread_info를 저장하여 사용합니다. (최근 향상된 보안 옵션에 따라 숨기기도 합니다)
감사합니다.
-
이파란
2022.05.01 03:36
안녕하세요! 문영일님, 16차 이파란입니다.
이제 실낱같은 맥락이 보이는 것 같아서 여쭤봅니다!라즈베리파이 400 보드 기준입니다~
<질문 1> 드라이버 소스코드에서 실질적으로 irq-nesting 이 가능한 구간 예시
$ pinout
Raspberry Pi 400 Rev 1.0Revision : c03130
SoC : BCM2711
$ ethtool -i eth0
driver: bcmgenet
version: 5.15.32-v8+
말씀해주신 irq-nesting 이 가능한 부분 관해서,(3) irq-enable // ~ irq-nesting이 가능 구간 ~ // (4) hard-irq에서 동작하는 softirq 처리
라즈베리파이 400 보드의 이더넷 드라이버 bcmgenet 기준으로 예를 들면
drivers/net/ethernet/broadcom/genet/bcmgenet.c 소스 코드 안에irqreturn_t bcmgenet_isr0(int irq, void *dev_id) 함수의 해당 라인에서 irq-nesting 이 가능할까요?
rpi-5.15.y/drivers/net/ethernet/broadcom/genet/bcmgenet.c#L3190
(3) spin_unlock_irqrestore(); // 인터럽트 라인 활성화 // 여기서 irq nesting 이 가능할까요? (4) schedule_work(); // hard-irq 코드에서 워크큐로 큐잉 <질문 2> 네트워크 드라이버는 보통 워크큐로 Bottom Half,
하나는 hardirq 로 Top Half 로 처리하는지 궁금합니다!
irqreturn_t 리턴 타입의 핸들러는 스터디하면서
봤던 드라이버에서는 보통 1개만 있었습니다.
하지만 bcmgenet 드라이버는 request_irq 2개를 등록하고,
2개가 다른 방식의 irq 기법을 사용하는점을 확인했습니다.
1. bcmgenet_isr0: handle Rx and Tx default queues + other stuff
(1) request_irq 등록
rpi-5.15.y/drivers/net/ethernet/broadcom/genet/bcmgenet.c#L3365
(2) bcmgenet_isr0 함수 (Bottom Half - 워크큐)
rpi-5.15.y/drivers/net/ethernet/broadcom/genet/bcmgenet.c#L3141
2. bcmgenet_isr1: handle Rx and Tx priority queues
(1) request_irq 등록
rpi-5.15.y/drivers/net/ethernet/broadcom/genet/bcmgenet.c#L3372
(2) bcmgenet_isr1 함수 (Top Half)
rpi-5.15.y/drivers/net/ethernet/broadcom/genet/bcmgenet.c#L3093
bcmgenet 드라이버처럼 네트워크 드라이버에서는
하나는 워크큐로 Bottom Half,
하나는 hardirq 로 Top Half 로 처리하는 전형적인 예시인지 궁금합니다!
-
문c(문영일)
2022.05.02 01:22
질문이 매우 구체적이군요. ^^
<질문1의 답변>
사용자 드라이버에서 irq_request()로 등록한 irq action 핸들러들은 irq가 disble된채로만 동작하는 hardirq입니다. 이 irq 핸들러는 여러 레이어로 동작하는 irq 핸들러들 중 마지막이라고 할 수 있습니다. 결국 이 함수내에서 irq가 nest 되어 진입하는 경우는 없습니다. (물론 CONFIG_PREEMPT_RT 제외합니다)
레이어별로 동작하는 irq 핸들러 중 irq action 핸들러를 완료하고, irq-enable하고 irq context를 빠져나오기 직전의 틈이 극히 일부 있는데, 이 때 nest 될 수도 있습니다. 이러한 경우는 irq 스택에서 레지스터들의 push가 한 번 더 되겠죠. 음. 그 틈이 가끔 이용되긴 하겠지만, 운 좋으면 실질적으로 그 틈으로 1번 정도의 nest 만을 허용하게 되는 셈이라 별 큰 의미는 없습니다. 운 좋아야 nest 되는 것을 볼 수 있는 것이 어떤 큰 의미가 있겠습니까? 이걸 진정한 irq nest라고 부르지는 않습니다. ^^;
즉 request_irq()로 등록한 아래 함수 안에서 nest될 일이 없습니다.
irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
{
/* Acknowledge the interrupt */
return IRQ_HANDLED;
}
<질문2의 답변>
네트워크 드라이버들은 구현이 조금씩 다르지만, 기본적으로는 1개 이상의 irq action handler를 사용합니다.
보통 인터럽트가 발생한 원인에 대해 해당 네트워크 디바이스의 irq 상태 레지스터들이 있고 그 상태를 읽어서 irq가 발생한 원인에 따라서 각각의 case들을 동작시킵니다. 그런데 irq 들을 제각기 분리하여 동작시키는 방법도 있습니다. 최근에는 많은 메시지 방식의 irq 들을 사용하는 추세라 각각의 핸들러들을 전부 나눠서 동작시킬 수도 있습니다. 이렇게 처리하면 레지스터를 읽어야 하는 시간도 절약할 수 있습니다.어쩻든 bcmgenet.c에서 request_irq()로 등록한 핸들러가 2개가 있고,
이 2개는 처리하는 일이 조금씩 다르군요. (물론 devm_request_irq()에서 등록한 핸들러도 있지만 아무 일도 않하니 생략합니다.)bcmgenet_isr0() 핸들러에서는 4개의 case를 처리하고 있습니다.
1) dma 컨트롤러를 이용한 RX 요청이 완료되었음을 알리는 인터럽트
2) dma 컨트롤러를 이용한 TX 요청이 완료되었음을 알리는 인터럽트
3) 이더넷 파이(PHY) 관련한 설정이 완료(정상 완료 또는 에러)되었을 처리하기 위한 인터럽트
4) 이더넷 맥(MAC)의 링크 up/down 변화와 파이(PHY) 관련한 변화가 발생했을 때 처리하기 위한 인터럽트bcmgenet_isr1() 핸들러에서는 2개의 case를 처리하고 있습니다.
5) rx 큐들에 대한 인터럽트(내부 HW에서 16개 큐마다 제어합니다)
6) tx 큐들에 대한 인터럽트(내부 HW에서 16개 큐마다 제어합니다)위의 6가지 case를 1개의 인터럽트로 처리하도록 할 수도 있고, 그 이상의 인터럽트로 분리하여 처리할 수도 있는데, 이것은 이더넷 드라이버 HW 설계자의 의도에 좌우합니다.
아주 시간이 오래 걸리는 3) 번과, 4) 번의 경우는 각각의 별도의 워크 큐를 사용한 bottom-half 처리를 사용했습니다. (이것은 주요 패킷 전송이 아니고, 다른 이더넷 드라이버에서는 이러한 워크 큐를 사용하지 않을 수도 있습니다)
실제 이더넷 드라이버에서 받은 패킷을 처리하는 곳은 1), 2), 5), 6) 번입니다. 16+1가지 TX 큐에 대한 인터럽트와, 16+1가지 RX 큐에 대한 인터럽트를 담당하고 있습니다. 이들의 요청을 처리하기 위해 역시 시간이 소요되므로, softirq를 사용한 bottom-half 처리를 사용합니다. 이 네트워크용 softirq 처리 루틴은 net_tx_action() 및 net_tx_action() 두 함수에서 처리됩니다.
이 두 루틴은 각각의 사용자 이더넷 드라이버가 NAPI api 중 netif_napi_add()함수로 등록한 콜백 함수를 softirq가 호출하게 하여 데이터를 송/수신 처리합니다. 즉 여기까지가 사용자 이더넷 드라이버가 이러한 주요 루틴을 처리해줄 내용이고, 나머지는 softirq 이후로 패킷들이 skb를 구성한 후 TCP/IP 스택에 자동으로 연결되게 되어 있습니다.감사합니다.
-
이파란
2022.05.02 06:16
궁금한 점이 많이 해소되었고,
또 제가 어떤 부분을 공부해야할지 이정표가 되네요!
항상 감사드립니다!
16차 이파란 드림.
.
안녕하세요? 문c 블로그(http://jake.dothome.co.kr)의 문영일입니다.
arm에서 irq 처리는 일반적으로 nesting이 되지 않도록 합니다만,
엄밀히 말씀드리면 nesting이 가능한 구간이 있어 소개합니다.
다음은 인터럽트 복귀까지 hardirq 구간내에서의 인터럽트 처리 순서를 보여줍니다.
1) 자동으로 irq-disable 상태로 진입
2) irq-handler 처리
3) irq-enable
4) hard-irq에서 동작하는 softirq 처리
위와 같이 2)번 구간에서는 irq-handler 처리가 완료될 때까지 irq nesting을 하지 않습니다.
다만 4)번 구간에서는 irq를 enable하므로 irq-nesting이 가능합니다.
*참고:
softirq는 hardirq구간에서 동작하다가 일정 이상 처리 시간이 길어지면
hardirq 구간에서 처리하지 않고, ksoftirqd 스레드를 깨워 동작시킵니다.(bottom-half로 이관)
감사합니다.