Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

53189
2265
156764


[3장] 커널 디버깅과 코드 학습: 79 페이지 디버깅 패치 추가 설명 Question_Announcement

독자분께서 한 가지 질문을 주신 내용인데요. 이 내용을 공유드리고자 합니다.
먼저 질문은 다음과 같습니다.

    * 79 페이지에 있는 패치 코드가 커널 버전 4.19 기준에서 보면 다르다.

이 질문에 대답을 먼저 드리면 '예로 든 로그가 실행된 커널 버전이 예전 버전인 '2.6' 이라 예전 소스 기준으로
설명을 한 것입니다'라고 말씀드리고 싶네요.

패치 코드의 내용 설명

이제 주신 질문에 대해 조금 더 자세히 설명을 드리겠습니다.
먼저 79 페이지에 있는 패치 코드의 내용은 다음과 같습니다.

diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -388,6 +388,8 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)

1 void __disable_irq(struct irq_desc *desc, unsigned int irq)
2 {
3 +       if ( irq == 4 )
4 +               WARN(1, KERN_WARNING " irq 4 is disbled %d, desc->depth %d \n", irq, desc->depth);
5        if (!desc->depth++)
6                irq_disable(desc);
7 }
@@ -442,6 +444,9 @@ EXPORT_SYMBOL(disable_irq);
8
9 void __enable_irq(struct irq_desc *desc, unsigned int irq)
10 {
11 +       if ( irq == 4 )
12 +               WARN(1, KERN_WARNING " irq 4 is enabled %d, desc->depth\n", irq, desc->depth);
13 +
14        switch (desc->depth) {
15        case 0:
16  err_out:

이번에는 커널 4.19 버전 기준으로 __disable_irq()/__enable_irq() 함수를 보겠습니다.

https://elixir.bootlin.com/linux/v4.19.120/source/kernel/irq/manage.c
void __disable_irq(struct irq_desc *desc)
{
if (!desc->depth++)
irq_disable(desc);
}

void __enable_irq(struct irq_desc *desc)
{
switch (desc->depth) {
case 0:
 err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
     irq_desc_get_irq(desc));
break;
case 1: {
if (desc->istate & IRQS_SUSPENDED)
goto err_out;
...
irq_startup(desc, IRQ_RESEND, IRQ_START_FORCE);
break;
}
default:
desc->depth--;
}
}

위 함수를 보면 인자가 1개 밖에 없습니다. 모두 인터럽트 디스크립터인 struct irq_desc 구조체 타입입니다.

그런데 다시 되돌아가 패치 코드를 보겠습니다.

diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -388,6 +388,8 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)

1 void __disable_irq(struct irq_desc *desc, unsigned int irq)
2 {
3 +       if ( irq == 4 )
4 +               WARN(1, KERN_WARNING " irq 4 is disbled %d, desc->depth %d \n", irq, desc->depth);
5        if (!desc->depth++)
6                irq_disable(desc);
7 }
@@ -442,6 +444,9 @@ EXPORT_SYMBOL(disable_irq);
8
9 void __enable_irq(struct irq_desc *desc, unsigned int irq)
10 {
11 +       if ( irq == 4 )
12 +               WARN(1, KERN_WARNING " irq 4 is enabled %d, desc->depth\n", irq, desc->depth);
13 +
14        switch (desc->depth) {
15        case 0:
16  err_out:

__disable_irq()/__enable_irq() 함수는 인자가 2개이며 정수형 인터럽트 번호인 'int irq'를 입력으로 받습니다.

정리하면, 패치 코드의 함수를 보니 커널 4.19 버전과 다르다는 건데요.

그 이유는 예제로 든 로그가 커널 2.6.28 에서 실행된 리눅스 시스템이기 때문입니다. 

(76 페이지)
https://www.unix.com/programming/148285-what-unbalanced-irq.html
01 WARNING: at kernel/irq/manage.c:225 __enable_irq+0x3b/0x57()
02 Unbalanced enable for IRQ 4
03 Modules linked in: svsknfdrvr [last unloaded: osal_linux]
04 Pid: 634, comm: ash Tainted: G W 2.6.28 #1
05 Call Trace:

04번째 줄을 보면 '2.6.28'이 보이죠. 이는 커널 버전을 뜻합니다.

'2.6.28'은 너무 오래된 버전(10년 전인데 10년이면 강산도 변한다고 했습니다)이라 
예로 들기 좀 그래서 지금도 IT 업체에서 적용 중인 LTS: 3.18 버전의 소스를 기준으로 삼았습니다.

3.18 버전 기준으로 __disable_irq/__enable_irq() 함수의 구현부는 다음과 같습니다.

https://elixir.bootlin.com/linux/v3.18.120/source/kernel/irq/manage.c
void __disable_irq(struct irq_desc *desc, unsigned int irq)
{
if (!desc->depth++)
irq_disable(desc);
}
void __enable_irq(struct irq_desc *desc, unsigned int irq)
{
switch (desc->depth) {
case 0:
 err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq);
break;

패치 코드에서 보이는 함수의 파라미터는 2개이며,
위 기준으로 책에서 소스 패치를 작성한 예를 보여드린 것입니다.

4.19 버전 기준으로 패치 코드를 어떻게 작성하면 될까?

질문을 주신 독자분의 관심사는 '4.19 버전에서는 어떻게 패치를 적용하면 될 것인가'라고 예상할 수 있는데요.
다음과 같은 형태의 패치 코드를 적용하면 될 것입니다.

https://elixir.bootlin.com/linux/v4.19.120/source/kernel/irq/manage.c
void disable_irq(unsigned int irq)
{
+ if( irq == 4 )
+ {
+ WARN(1, KERN_WARNING " irq 4 is disabled %d \n", irq);
+ }
if (!__disable_irq_nosync(irq))
synchronize_irq(irq);
}

void enable_irq(unsigned int irq)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);

if (!desc)
return;
if (WARN(!desc->irq_data.chip,
KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
goto out;
+
+ if( irq == 4 )
+ {
+ WARN(1, KERN_WARNING " irq 4 is enabled %d \n", irq);
+ }
+
__enable_irq(desc);
out:
irq_put_desc_busunlock(desc, flags);
}
EXPORT_SYMBOL(enable_irq);

위 함수에서 + 기호로 표시된 부분이 추가되는 코드입니다.

정리

리눅스 커널은 오픈 소스 프로젝트라 소스 버전에 따라 함수의 구현부가 바뀌고 있습니다.
그래서 버전에 따라 함수의 인자들이 변경될 수 있습니다.

질문 주신 독자님께 감사드리며 2판에서는 패치 코드의 기준 버전을 명시하도록 하겠습니다.

감사합니다.

Thanks,
Austin Kim

덧글

  • 크로마토그래피 2020/05/24 03:44 # 삭제 답글

    이번에 사고 2장 .sh파일들 실행하던중 용량이 부족하다 뜨던데 혹시 필자님은 sd카드 용량 몇기가 사용하셨나요? 전 16기간데 벌써 100프로 사용으로 뜨던데...
    PS) 혹시 .sh파일들 설치가 오래걸려서 끄고 나중에 다시 설치한적 있는데 그거랑은 상관없겠죠??
  • AustinKim 2020/05/24 09:49 #

    책을 읽어주시고 댓글을 남겨주셔서 감사합니다.

    저는 16GB SD카드를 사용해 라즈비안 커널을 빌드했는데, 용량이 다 찬 것을 본 적은 없습니다.
    라즈비안의 배포판 마다 약간 이미지 사이즈가 다른 것 같은데요.

    32GB SD카드를 사용해 라즈비안 커널을 빌드하시면 문제가 없을 것 같습니다.

    빌드 스크립트를 실행하다가 멈춘 경우 두 가지 경우를 생각해 볼 수 있는데요.

    build_rpi_kernel.sh(54 페이지)를 실행하다 멈춘 경우;
    => build_rpi_kernel.sh(54 페이지)를 실행하고,
    install_rpi_kernel_img.sh(58 페이지)를 실행하면 됩니다.

    install_rpi_kernel_img.sh(58 페이지)를 실행하다 멈춘 경우;
    => 마찬가지로 다시 build_rpi_kernel.sh(54 페이지)를 실행하고,
    install_rpi_kernel_img.sh(58 페이지)를 실행하면 됩니다.

    한 가지 더 중요한 팁을 드리면;
    build_rpi_kernel.sh을 실행하다 빌드 에러나 다른 이유로 실행이 멈추거나, 시간이 오래 걸려 'Ctl+C' 키를 입력해 스크립트 실행을 강제 종료시킬 수 있습니다. 이 때 build_rpi_kernel.sh을 실행해 문제 현상을 반드시! 해결한 후 install_rpi_kernel_img.sh을 실행해 라즈비안 커널 이미지를 설치해야 합니다.

    만약 build_rpi_kernel.sh을 실행하다 빌드 에러을 만난 후 install_rpi_kernel_img.sh을 실행하고 라즈베리 파이를 리부팅을 하면
    라즈베리 파이가 아예 부팅을 못하거나 마우스나 키보드가 아예 동작을 못할 수 있습니다.

    Thanks,
    Austin Kim
  • AustinKim 2020/05/24 09:49 # 답글

    책을 읽어주시고 댓글을 남겨주셔서 감사합니다.

    저는 16GB SD카드를 사용해 라즈비안 커널을 빌드했는데, 용량이 다 찬 것을 본 적은 없습니다.
    라즈비안의 배포판 마다 약간 이미지 사이즈가 다른 것 같은데요.

    32GB SD카드를 사용해 라즈비안 커널을 빌드하시면 문제가 없을 것 같습니다.

    빌드 스크립트를 실행하다가 멈춘 경우 두 가지 경우를 생각해 볼 수 있는데요.

    build_rpi_kernel.sh(54 페이지)를 실행하다 멈춘 경우;
    => build_rpi_kernel.sh(54 페이지)를 실행하고,
    install_rpi_kernel_img.sh(58 페이지)를 실행하면 됩니다.

    install_rpi_kernel_img.sh(58 페이지)를 실행하다 멈춘 경우;
    => 마찬가지로 다시 build_rpi_kernel.sh(54 페이지)를 실행하고,
    install_rpi_kernel_img.sh(58 페이지)를 실행하면 됩니다.

    한 가지 더 중요한 팁을 드리면;
    build_rpi_kernel.sh을 실행하다 빌드 에러나 다른 이유로 실행이 멈추거나, 시간이 오래 걸려 'Ctl+C' 키를 입력해 스크립트 실행을 강제 종료시킬 수 있습니다. 이 때 build_rpi_kernel.sh을 실행해 문제 현상을 반드시! 해결한 후 install_rpi_kernel_img.sh을 실행해 라즈비안 커널 이미지를 설치해야 합니다.

    만약 build_rpi_kernel.sh을 실행하다 빌드 에러을 만난 후 install_rpi_kernel_img.sh을 실행하고 라즈베리 파이를 리부팅을 하면
    라즈베리 파이가 아예 부팅을 못하거나 마우스나 키보드가 아예 동작을 못할 수 있습니다.
  • nagne 2020/05/28 22:15 # 삭제 답글

    p293 페이지, thread_info.h 의 r4 코드에도 오타가 있습니다. r이 빠졌어요 ~
  • AustinKim 2020/05/29 13:12 #

    오타를 알려주셔서 정말 감사합니다.
    2판에 꼭 반영토록 하겠습니다.
  • nagne 2020/05/29 01:05 # 삭제 답글

    p320 페이지, entry-armv.S 내용이 다른데 확인좀 부탁드려요 ㅠ
    같은 버전 받았는데 다음과 같이 코드가 전혀 다릅니다. 혹시 빌드해서 objdump 내용으로 봐야 하나요?
    https://pastebin.com/tQt4kdYV

    커널 버전: Linux raspberrypi 4.19.122-v7+ #9 SMP Thu May 28 16:19:31 BST 2020 armv7l GNU/Linux
  • AustinKim 2020/05/29 13:15 #

    언급하신 코드는 objdump로 추출한 결과입니다.
    명령어는 다음과 같습니다.
    $ objdump -d vmlinux

    vmlinux를 커널을 빌드하면 생성되는 파일입니다.

    objdump의 사용법은 아래 포스팅을 참고하시면 됩니다.
    http://rousalome.egloos.com/10009180
댓글 입력 영역