Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

693
557
422265


[부록 C] 리눅스 커널 프로젝트에 기여하기: 패치 전달 - 패치 코드를 작성하기 전 커널 코드 분석 부록

리눅스 커널 소스 코드를 수정하려면 먼저 소스 코드를 분석해야 합니다. 소스 코드를 이해하는 것은 물론이고 코드에 어떤 오류가 있는지 살펴보면서 코드를 볼 필요가 있습니다.

필자는 리눅스 커널의 vmalloc 서브 시스템 내 __vmalloc_area_node() 함수 코드를 분석했습니다.

자, 그럼 소스 코드를 같이 볼까요? 특히 07~13번째 코드를 눈여겨봅시다. 
[https://elixir.bootlin.com/linux/v5.3/source/mm/vmalloc.c]
01 static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
02                 pgprot_t prot, int node)
{
03    struct page **pages;
04    unsigned int nr_pages, array_size, i;
...
05    area->nr_pages = nr_pages;
06    /* Please note that the recursion is strictly bounded. */
07    if (array_size > PAGE_SIZE) {
08        pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
09               PAGE_KERNEL, node, area->caller);
10    } else {
11        pages = kmalloc_node(array_size, nested_gfp, node);
12    }
13    area->pages = pages;
14    if (!area->pages) {
15        remove_vm_area(area->addr);
16        kfree(area);
17        return NULL;
18   }
19    for (i = 0; i < area->nr_pages; i++) {
20        struct page *page;
...
21    }

소스 코드를 보고 다음과 같은 반문을 할 수 있습니다.

    뭐가 문제지?

많은 분들이 커널 소스 코드를 분석할 때 이해하는데 급급할 때가 많습니다. 하지만 커널 소스 코드를 조금 '비판적'으로 보면 가끔 논리적인 오류가 발견될 때가 있습니다.

자, 그러면 위 소스 코드의 문제점에 대해서 이야기를 해보려 합니다.
07    if (array_size > PAGE_SIZE) {
08        pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
09               PAGE_KERNEL, node, area->caller);
10    } else {
11        pages = kmalloc_node(array_size, nested_gfp, node);
12    }

08번째와 11번째 줄 코드는 __vmalloc_node() 함수와 kmalloc_node() 함수를 호출해 페이지를 할당 받습니다. 그런데 메모리 부족으로 페이지가 부족할 때는 이 함수들은 NULL을 반환합니다. 

이번에 13~17번째 줄 코드를 보겠습니다.
13    area->pages = pages;
14    if (!area->pages) {
15        remove_vm_area(area->addr);
16        kfree(area);
17        return NULL;
18   }

먼저 13번째 줄 코드를 보겠습니다.
페이지를 할당 받은 pages를 'area->pages' 필드에 저장합니다. page가 NULL일지도 모르는 상황인데도 'area->pages' 필드에 page를 저장합니다.

이번에는 14번째 줄 코드를 눈으로 따라가 봅시다.  'area->pages' 가 NULL인지 체크하는 동작입니다. 'area->pages' 가 NULL이면 '!area->pages' 구문은 TRUE이니 15번째 줄 코드를 실행해 'area->addr'와  'area' 주소를 해제(Free) 합니다. 

그래서 다음과 같은 아이디어가 생겼습니다.
08번째와 11번째 줄 코드와 같이 __vmalloc_node() 함수와 kmalloc_node() 함수를 호출해 페이지를 할당 받은 다음 바로 page 포인터에 대한 NULL 체크를 하자.
이 14~18번째 줄 예외 처리 코드 다음에 'area->pages = pages' 코드를 실행하면 좋지 않을까?   

위에서 설명한 기준에 따라 소스 코드를 수정했습니다. 12~20번째 줄 코드가 수정한 코드입니다.
01 static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
02                 pgprot_t prot, int node)
{
03    struct page **pages;
04    unsigned int nr_pages, array_size, i;
...
05    /* Please note that the recursion is strictly bounded. */
06    if (array_size > PAGE_SIZE) {
07        pages = __vmalloc_node(array_size, 1, nested_gfp|highmem_mask,
08                PAGE_KERNEL, node, area->caller);
09    } else {
10        pages = kmalloc_node(array_size, nested_gfp, node);
11   }
12
13    if (!pages) {
14        remove_vm_area(area->addr);
15        kfree(area);
16        return NULL;
17    }
18
19    area->pages = pages;
20
21    for (i = 0; i < area->nr_pages; i++) {
22       struct page *page;
...
23   }    

이번에는 패치 형태로 소스 수정 내역을 확인해보겠습니다.   
root@raspberrypi:/home/pi/kernel_src/linux-next# git diff mm/vmalloc.c
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index e66e7ff..0471c78 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -2416,13 +2416,15 @@ static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
        } else {
                pages = kmalloc_node(array_size, nested_gfp, node);
        }
-       area->pages = pages;
-       if (!area->pages) {
+
+       if (!pages) {
                remove_vm_area(area->addr);
                kfree(area);
                return NULL;
        }

+       area->pages = pages;
+
        for (i = 0; i < area->nr_pages; i++) {
                struct page *page;



"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


[부록 A] GCC 지시어
   * inline    
   * noinline    
   * __noreturn   
   * unused   
[부록 B] 리눅스 커널 실력을 키우는 방법
[부록 C] 리눅스 커널 프로젝트에 기여하기  
C.1 리눅스 커널 오픈소스 프로젝트 소개 
   * 용어  
C.2 설정 방법 
C.3 패치 코드를 작성한 후 이메일로 보내기  
C.5 리눅스 커널 오픈소스 프로젝트로 얻는 지식 


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


 

핑백

덧글

댓글 입력 영역