본문 바로가기
Kernel/Documentation

[Linux Kernel] 리눅스 커널 개발 가이드: Posting Patches

by hyeyoo 2021. 9. 14.
※ 이 블로그의 글은 글쓴이가 공부하면서 정리하여 쓴 글입니다.
※ 최대한 내용을 검토하면서 글을 쓰지만 틀린 내용이 있을 수 있습니다.
※ 만약 틀린 부분이 있다면 댓글로 알려주세요.

대충 격리 끝나서 신나는 짤

와 휴가 나가고 나서 격리때문에 블로그 글을 하나도 못썼다. 얼른 이 시리즈 마저 번역(이라기엔 야매지만)하고 다른 주제도 글 써야겠다.

 

5. Posting patches — The Linux Kernel documentation

Sooner or later, the time comes when your work is ready to be presented to the community for review and, eventually, inclusion into the mainline kernel. Unsurprisingly, the kernel development community has evolved a set of conventions and procedures which

www.kernel.org

빠르던 늦던 언제가 당신의 작업을 커뮤니에 보내고, 리뷰를 받고, mainline 커널에 포함되는 순간이 온다. 커널 개발 커뮤니티에 패치를 게시하는 컨벤션과 과정이 존재하는 것은 놀랍지 않다. 이런 컨벤션과 과정을 따라야 함께하는 모두가 편해진다. 이 문서는 패치를 게시할 때 무엇을 따라야 하는지를 합리적으로 자세하게 설명한다. 더 많은 정보는 Submitting Patches: the essential guide to getting your code into the kernel, Submitting Drivers For The Linux Kernel, Linux Kernel Patch submission checklist를 참고하자.

 

5.1 When to post

패치가 충분히 준비된 후에 게시하고 싶은 욕구가 들 수 있다. 간단한 패치는 문제가 되지 않지만, 복잡한 프로젝트의 경우에는 작업을 마치기 전에 피드백을 받아서 얻을 수 있는 것이 매우 많다. 따라서 현재 작업중인 프로젝트를 커뮤니티에 게시하는 것도 고려해볼만 하다. 아니면 당신이 작업중인 git 트리를 올려서 관심있는 개발자들이 접근하도록 하는 것도 좋다.

 

아직 머지될 준비가 되지 않는 코드를 게시할 때는, 게시할 때 아직 준비되지 않았다는 점을 설명하는 것이 좋다. 또한 앞으로 어떤 작업이 필요한지, 현재 이슈가 무엇인지도 설명하자. 덜 만들어진 패치를 보는 사람은 더 적겠지만, 

설명을 잘 적어놓으면 패치를 보는 사람으로 하여금 자신이 올바른 방향으로 작업을 진행하도록 도울 수 있다는 생각이 들 것이다.

 

5.2 Before creating patches

개발 커뮤니티에 패치를 보내기 전에 고려해야 할 점들이 있다:

 

- 가능한 한 코드를 테스트하자. 커널의 디버깅 툴도 활용하고, CONFIG도 다양하게 설정해서 잘 빌드가 되는지 확인하고, 크로스 컴파일러로 다른 아키텍처에서도 동작하는지 등등을 확인해보자.

 

- 당신의 코드가 커널 코드 스타일 가이드라인에 맞는지 확인해보자.

 

- 당신의 패치가 성능에 영향을 주는가? 만약 준다면, 벤치마크를 통해서 성능에얼마나 영향을 주는지 확인해야 한다. 또한 벤치마크의 결과도 패치에 포함되어야 한다.

 

- 코드를 게시할 권한이 당신에게 있는지 확인하자. 만약 누군가가 고용해서 작업한 것이라면, 권한은 고용주에게 있을 것이며 고용주가 GPL 라이선스로 배포하는 것에 동의해야 한다.

 

일반적으로 코드를 게시하기 전에 하는 고민은, 짧은 시간에 보답을 한다. (충분히 고민 후에 메일을 보내자.)

 

5.3 Patch preparation

패치를 게시하기 전 준비 작업은 생각보다 양이 많을 수 있다. 하지만, 양이 많다고 해서 이 단계에서 시간을 줄이는 건 좋지 않다.

 

패치는 반드시 특정 커널 버전을 기반으로 해야한다. 일반적으로는 현재의 mainline을 기반으로 한다. mainline을 기반으로 할 때는 임의의 지점을 커밋을 기반으로 하기보단 -stable이나 -rc와 같이 잘 알려진 버전을 기반으로 하는 것이 좋다.

 

더 폭넓은 테스트와 리뷰를 위해서는 -mm, linux-next, 서브시스템 트리에 적용할 수 있는 패치를 만드는 것이 필요하다. (패치가 한 트리에선 적용되는데 다른 트리에선 적용되지 않는 경우도 많다.)

 

매우 간단한 패치가 아니라면 패치를 논리적으로 나눠서 여러 개로 만들어야 한다. 패치를 나누는 것은 하나의 예술이다. 몇몇 개발자들은 개발 커뮤니티가 기대하는 방법으로 패치를 나누는 데 많은 시간을 쓴다. 하지만 여기에 도움이 많이 되는 규칙들이 있다:

 

- 패치는 그 기능을 개발하기까지의 과정을 패치로 작성하는 것이 아니라, 완성본을 먼저 만든 후 그걸 논리적으로 나눠서 패치로 만들어야 한다. 개발하기까지의 과정은 관심사가 아니다.

 

- 각각의 논리적으로 독립적인 변경 사항은 각각의 패치로 만들어져야 한다. 이러한 변경 사항은 크거나 작을 수 있지만, 개념적으로 작고 한 줄 요약이 가능해야 한다. 각각의 패치가 무슨 기능을 하는지 명확하게 설명해서 패치별로 리뷰가 가능하도록 해야 한다.

 

- 하나의 패치에 어려 개의 변경 사항을 넣지 말자. 하나의 패치가 자료 구조도 수정하고, 보안 버그도 고치고, 코드의 스타일도 고치면 정작 중요한 부분을 리뷰받지 못할 수 있다.

 

- 패치 시리즈에서 각각의 패치를 하나씩 적용하다가 멈춰도 커널은 정상적으로 빌드 및 실행이 되어야 한다. 만약에 패치를 하나씩 적용하다가 잠깐 멈추었을 때 중간에 빌드나 실행이 안 되는 상태가 된다면 문제가 된다. 패치 시리즈를를 일부만 적용하는 예시는 "git bisect"로 regression을 찾을 때이다. 만약 패치 중간에 커널이 제대로 빌드되지 않는다면, git bisect로 버그를 찾기가 더 힘들어진다.

 

- 너무 과하게 패치를 나누지는 말자. 어떤 개발자가 하나의 파일을 수정하는 데 패치를 500개로 나눠서 보냈다 ㅡ 이것은 그 개발자를 커널 메일링 리스트에서 유명한 사람으로 만들지 못했다. 하나의 패치는 하나의 "논리적인 변경사항"을 변경한다면 그 크기는 문제가 되지 않는다.

 

- 완전 새로운 인프라를 추가할 때도 패치 시리즈로 나눠서 보내고싶을 수 있다. 하지만 마지막 패치를 적용하기 전까지 새로운 인프라를 사용하지 못하게 하게 된다. 이런 상황은 가능하면 피해야 한다. 만약에 이 패치 시리즈가 regression을 만들어낸다면, 이분탐색(bisection)을 했을 때 패치 시리즈의 마지막 패치를 문제로 잡아내지만, 실제로는 마지막 패치가 아닌 다른 패치에 문제가 존재할 수 있다. 가능하다면 새로운 코드를 추가할 때는 한꺼번에 추가해야 한다.

 

완벽한 패치 시리즈를 만드는 것은 "실제 작업"이 끝난 후에 상당한 시간과 생각을 필요로 하는, 좌절을 겪는 과정이 될 수 있다. 하지만 제대로 끝내기만 하면, 시간을 잘 쓴 것이다.

 

5.4 Patch formatting and changelogs

자 이제 당신이 완벽한 패치를 시리즈를 만드는 것까지 했다고 해보자. 하지만 아직 할 일이 많이 있다. 각각의 패치는 빠르고 명확하게 패치의 목적을 설명해야 한다. 그렇게 하기 위해서, 각각의 패치는 다음과 같이 구성된다:

 

- 필요에 따라 패치의 작성자가 누구인지 나타내는 "From" 라인을 써서 보낼 수 있다. 자신이 아닌 다른 누군가가 작성한 패치를 보낼 때는 반드시 필요하다. 하지만 추가를 할지 말지 고민할 땐 그냥 추가하자. From 라인을 추가했다고 문제가 되는 경우는 없다.

 

- 이 패치가 무슨 일을 하는지에 대한 한줄 설명을 써야한다. 이 메시지는 패치의 맥락과 관계없이 이 패치를 보는 사람이 패치의 적용 범위를 파악하기가 쉬어야 한다. 이 라인은 짧은 형식의 changelog에 뜨게 된다. 일반적으로 이 메시지는 연관이 있는 서브시스템을 먼저 쓰고, 그 다음에 패치의 목적을 쓴다. 예를 들어

 

gpio: fix build on CONFIG_GPIO_SYSFS=n

 

- 빈 줄 하나 다음에 패치의 자세한 설명이 와야 한다. 설명은 필요한 만큼 얼마든지 길어도 된다. 여기서는 패치가 무엇을 하는지와 이 패치가 왜 적용되어야 하는지를 설명한다.

 

- 하나 이상의 태그들이 와야한다. 태그는 한 줄에 하나씩 온다. 패치에는 적어도 하나의 Signed-off-by: 태그는 포함되어야 한다. 

 

패치 예시를 보려면 아래 더보기 버튼을 누르자.

더보기

태그를 설명하기에 앞서 잠깐 git format-patch로 생성한 패치의 예시를 보자.

From 60c64ed0eb77b5984a26c548e6a314572a12e13b Mon Sep 17 00:00:00 2001
From: Hyeonggon Yoo <42.hyeyoo@gmail.com>
Date: Wed, 19 May 2021 00:25:25 +0900

2개의 From와 Date로, 이 패치가 누가, 언제 작성했는지 명시한다.

Subject: [PATCH] mm, slub: Fix support for clang 10

그 다음에는 이 패치의 제목이 무엇인지 Subject:로 명시한다.

Previously in commit  ff3daafe3fd3 ("mm, slub: change run-time assertion
in kmalloc_index() to compile-time"), changed kmalloc_index's run-time
assertion to compile-time assertion.

But clang 10 has a bug misevaluating __builtin_constant_p() as true,
making it unable to compile. This bug was fixed in clang 11.

To support clang 10, introduce a macro to do run-time assertion if clang
version is less than 11, even if the size is constant. Might revert this
commit later if we choose not to support clang 10.

그 다음 빈 줄 하나와, 3문단에 걸쳐 패치가 무엇을 하는지 설명한다.

Fixes: ff3daafe3fd3 ("mm, slub: change run-time assertion in kmalloc_index() to compile-time")
Link: https://lore.kernel.org/r/CA+G9fYvYxqVhUTkertjZjcrUq8LWPnO7qC==Wum3gYCwWF9D6Q@mail.gmail.com/
Link: https://lkml.org/lkml/2021/5/11/872
Suggested-by: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Hyeonggon Yoo <42.hyeyoo@gmail.com>

그 다음에는 Fixes, Link, Suggested-by, Signed-off-by 등등의 태그가 온다.

---
 include/linux/slab.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/slab.h b/include/linux/slab.h
index 9d316aac0aba..8d8dd8571261 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -413,7 +413,7 @@ static __always_inline unsigned int __kmalloc_index(size_t size,
 	if (size <=  16 * 1024 * 1024) return 24;
 	if (size <=  32 * 1024 * 1024) return 25;
 
-	if (size_is_constant)
+	if ((IS_ENABLED(CONFIG_CC_IS_GCC) || CONFIG_CLANG_VERSION >= 110000) && size_is_constant)
 		BUILD_BUG_ON_MSG(1, "unexpected size in kmalloc_index()");
 	else
 		BUG();
-- 
2.25.1

그 다음에는 diff의 결과, git 버전 등이 나온다.

 

위에서 말한 요소들이 패치의 changelog를 구성한다. changelog를 잘 쓰는 건 중요하지만 기피되는 경우가 많다. 여기서 언급하고 갈만한 점이 있다. changelog를 작성할 때는 많은 사람들이 당신이 작성한걸 볼거라는 걸 명심해야 한다. 이 패치를 리뷰해줄 메인테이너, 리뷰어도 볼 것이고 배포판을 관리하는 사람, 패치를 이전 커널로 백포팅하려는 다른 서브시스템의 메인테이너, 버그를 찾으려는 버그 헌터, 커널이 어떻게 바뀌었는지 알고싶은 사용자 등 많은 사람들이 보게 된다. changelog를 잘 써야 위에서 말한 모두에게 필요한 정보를 제공할 수 있다.

 

changelog를 잘 쓰기 위해서, 첫 번째 줄에 쓰는 한 줄 설명에 최대한 패치의 효과와 이 패치를 왜 작성했는지를 설명해야 한다. 그 다음에 해당 주제에 대해 자세하게 설명하면 된다. 만약 패치가 버그를 고친다면, 이 버그가 생긴 커밋이 무엇인지 표시하자. 커밋을 인용할 때는 아래와 같이 12자 이상의 커밋 해시와 커밋의 제목을 써야한다.

ff3daafe3fd3 ("mm, slub: change run-time assertion in kmalloc_index() to compile-time")

패치가 어떤 로그나 컴파일러 출력과 연관이 있다면, changelog에 그 내용을 추가해야 한다. 만약 현재 패치가 또다른 패치를 위한 것이라면 그 점을 changelog에서 명확하게 이야기해야 한다. 그리고 내부 API를 수정한다면 다른 개발자들이 그 수정 사항과, 이에 대해 어떻게 대처할지 자세하게 설명해야 한다. 일반적으로 당신이 changelog를 읽는 다른 개발자의 입장에서 생각개발자의 입장에서 생각해볼 수록, changelog의 질이 더 높아진다.

 

말할 것도 없이 changelog는 당신이 사용하는 버전 관리 시스템에서 사용하는 그 텍스트어야 한다. 그 다음에는 패치 자체가 단일화된 포맷 ("-u" 옵션)으로 온다. 이때 "-p" 옵션을 사용하면 diff가 수정하는 함수의 이름도 보여줘서, 다른 사람이 이해하기 더 쉬워진다.

 

패치를 만들 때는 관련이 없는 파일을 포함시키면 안된다. (예를 들어 빌드를 하면서 생기는 파일들) Documentation 폴더의 "dontdiff" 파일이 관련 없는 파일을 포함시키지 않도록 도울 것이다. diff의 옵션에 "-X"로 dontdiff를 보내면 된다.

 

위에서 언급한 태그는, 패치에 다양한 사람이 함께 참여했다는 것을 보여준다.  Submitting patches: the essential guide to getting your code into the kernel에 자세하게 설명되어있다. 여기에선 간단하게 요약해서 설명한다.

 

tag: Full Name <email address>  optional-other-stuff

흔한 태그들:

- Signed-off-by: 개발자가 코드에 대한 권리를 갖고있다는 서명이다. 또한 "Developer’s Certificate of Origin"에 대한 동의의 의미이기도 하다. Submitting patches: the essential guide to getting your code into the kernel 에서 전문을 확인할 수 있다. 서명이 되지 않은 코드는 메인라인에 머지될 수 없다.

 

- Co-developed-by: 패치가 다른 개발자와 함께 개발되었다는 의미이다. (From 태그에 명시된 개발자 외의 사람을 명시하는 데에 사용된다.)  모든 Co-developed-by 태그 뒤에는 해당 공동 개발자의 Signed-off-by 태그가 함께 와야한다.

 

- Acked-by: 개발자(주로 서브시스템의 메인테이너)가 해당 패치가 커널에 머지되어야 함을 동의한다는 의미이다.

 

- Tested-by: 패치를 테스트한 사람을 나타낸다.

 

- Reviewed-by: 패치가 올바르다는 것을 리뷰했음을 나타낸다. 리뷰어에 대한 자세한 내용은  Submitting patches: the essential guide to getting your code into the kernel를 참고하자.

 

- Reported-by: 해당 패치가 해결한 문제를 누가 보고했는지를 명시한다. 이 태그는 코드를 테스트하고, 코드가 올바르게 작동하지 않는다는 것을 알려준 이에게 크레딧을 제공하기 위해 사용된다.

 

- Cc: 이 패치의 사본을 메일로 받고, 코멘트를 남길 수 있는 사람을 나타낸다.

 

태그를 사용할 때는 주의하자. Cc: 빼고는 당사자의 허락을 받아야 쓸 수 있다.

5.5. Sending the patch

패치를 메일로 보내기 전에 신경써야할 것들이 있다:

- 이메일 클라이언트가 패치를 깨뜨리지 않는가? 패치는 이메일 클라이언트가 패치에 간섭하는 경우 제대로 동작하지 않고, 리뷰를 받기도 힘들다. 만약 이메일 클라이언트에 대한 의심이 든다면 자기 자신에게 메일을 보내서 제대로 적용되는지 확인해보자. Email clients info for Linux는 이메일 클라이언트로 패치를 보내는 것에 도움이 된다.

 

- 패치에 바보같은 실수를 하지는 않았는가? 항상 scripts/checkpatch.pl를 실행해서 확인해봐야한다. 다만 checkpatch.pl은 그렇게 똑똑하지 않기 때문에, checkpatch.pl가 시키는대로 고치는게 더 나쁘다면, 고치지 말자.

 

패치는 항상 plain text로 보내야 한다. 패치를 첨부파일(attachment)로 보내지 말자. 첨부파일로 보내면 리뷰어가 패치의 일부를 인용하기가 힘들어진다. (답장할 때 코드의 특정 부분에 코멘트를 달기가 힘들다. 그리고 bot이 패치를 테스트하기도 힘들다.)

 

패치를 메일로 보낼 땐 패치에 관심이 있을 만한 사람한테 보내야 한다. 다른 프로젝트와 달리 커널은 너무 많은 사람엑 카피를 보내는 것(cc 로 참조하는 것)을 권장한다. 패치와 관련된 사람들이 메일링 리스트에서 직접 찾아볼 거라고생각하지 말고 직접 카피를 보내자.

 

특히, 다음 사람들한테 보내야 한다:

 

- 해당 패치에 영향을 받는 서브시스템의 메인테이너

 

- 같은 영역에서 개발을 하는 개발자들 ㅡ 특히 현재 활동 영역이 겹치는 사람들. git으로 현재 당신이 수정중인 파일에서 누가 작업하는지 찾아보자.

 

- 당신이 버그 리포트에나 기능 요청에 대해 답장을 한다면 처음 글을 올린 사람에게도 사본을 보내자.

 

- 연관 있는 메일링 리스트로 사본을 보내자. 없다면, linux-kernel으로 보내면 된다.

 

- 버그를 고치고 있다면, 해당 패치가 다음 안정 릴리즈에 포함이 되어야 하는지 생각해보자. 포함이 되어야한다면, stable@vger.kernel.org에게도 사본을 보내고 패치에 Cc: 태그도 포함하자. 이 태그를 쓰면 안정화 팀에게 당신의 패치가 메인라인에 머지되었을 때 알림이 간다.

 

패치를 보낼 때 누가 이 패치를 승낙하고 머지할지 생각해보자. Linus Torvalds에게 바로 보내서 머지가 될 수도 있지만 보통 그렇게 하지 않는다. Linus는 매우 바쁘고, 대신에 각 서브시스템을 관리하는 메인테이너가 존재한다. 일반적으로 당신은 그 메인테이너에게 패치를 보낸 후에 머지될 것이다. 적당한 메인테이너가 없다면 Andrew Morton에게 보내면 된다.

 

패치를 메일로 보낼땐 좋은 제목이 필요하다. 표준적으로 많이 쓰이는 형식의 예를 들면:

[PATCH nn/mm] subsys: one-line description of the patch

 

여기서 "nn"은 해당 패치가 패치 시리즈에서 몇 번째 패치인지를 나타내고, "mm"은 패치 시리즈에 포함된 전체 패치의 개수이다. "subsys"는 해당 패치에 영향을 받는 서브시스템이다. 단일 패치를 보낼 때 nn/mm은 생략된다.

 

중요한 패치 시리즈라면 해당 패치 시리즈에 대한 설명을 0번째 메일로 보내는 것이 관례이다. (하지만 보편적으로 지켜지지는 않는 관례이다.) 예를 들어 최근에 Jens Axboe가 보낸 패치 시리즈를 보자. 이 패치 시리즈는 다음과 같이 구성되어있다.

 

[PATCHSET v5 0/6] Enable bio recycling for polled IO
    ㄴ [PATCH 1/6] bio: optimize initialization of a bio
    ㄴ [PATCH 2/6] fs: add kiocb alloc cache flag
    ㄴ [PATCH 3/6] bio: add allocation cache abstraction
    ㄴ [PATCH 4/6] block: clear BIO_PERCPU_CACHE flag if polling isn't supported
    ㄴ [PATCH 5/6] io_uring: enable use of bio alloc cache
    ㄴ [PATCH 6/6] block: use the percpu bio cache in __blkdev_direct_IO

 

여기서 패치 1~6은 실제 코드와 changelog가 존재하지만, 0번째는 이 패치 시리즈에 대한 간략한 설명이다. 이 패치 시리즈 같은 경우에는 5번째 버전이라서, 4번째 버전과 어떻게 다른지 설명이 되어있다. 관례대로 0번째에 패치 시리즈의 설명을 보낼 때 주의할 점은 패치 시리즈 설명은 changelog로 기록되지 않는다는 점이다. 따라서 각각의 패치의 설명 자체도 명확해야 한다.

 

일반적으로, 위에 예시에서처럼 multi-part 패치를 보낼 때는 두 번째 패치부터는 첫 번째 패치에 대한 답장으로 보내야 한다. git이나 quilt같은 경우에는 메일을 적절하게 보내는 기능이 있다. 하지만 패치 시리즈가 길고, 당신이 git을 사용한다면 -chain-reply-to 옵션으로 너무 답장을 중첩하지 않도록 (답장의 답장의 답장의 답장의 ...) 주의하자.

댓글