GKI용 커널 코드 개발

일반 커널 이미지(GKI)는 업스트림 Linux 커널에 잘 맞게 정렬하여 커널 조각화를 줄입니다. 그러나, 일부 패치에 업스트림이 허용되지 않는 타당한 이유가 있거나 제품 일정을 따라야 하는 경우가 있습니다. 이런 경우 GKI가 빌드된 Android 공통 커널(ACK) 소스에 일부 패치를 유지합니다.

개발자는 일단 Linux 커널 메일링 리스트(LKML)를 사용하여 코드 변경사항을 업스트림으로 제출하되, 업스트림을 실행할 수 없는 타당한 이유가 있는 경우에만 ACK android-mainline 브랜치로 코드 변경사항을 제출해야 합니다. 타당한 이유의 예와 처리 방법은 다음과 같습니다.

  • 패치가 LKML에 제출되었지만, 제품 출시 일정에 맞게 수락되지 않았습니다. 이 패치를 처리하는 방법은 다음과 같습니다.

    • 패치를 LKML에 제출했으며 패치에 관한 의견을 받았다는 증거를 제시하거나 패치를 업스트림에 제출할 예상 시간을 제공합니다.
    • ACK에 패치가 포함되도록 작업 과정을 결정하고 승인된 업스트림으로 가져온 다음 최종 업스트림 버전이 ACK에 병합되면 ACK에서 패치를 제거합니다.
  • 패치가 공급업체 모듈의 EXPORT_SYMBOLS_GPL()을 정의하지만, 이 기호를 사용하는 트리 내 모듈이 없으므로 업스트림으로 패치를 제출할 수 없습니다. 이 패치를 처리하려면 모듈을 업스트림에 제출할 수 없는 이유와 요청을 하기 전에 고려한 다른 방법에 관해 자세히 설명합니다.

  • 패치가 업스트림에 제출하기에 일반적이지 않으며 제품 출시 전에 리팩터링할 시간이 없습니다. 이 패치를 처리하려면 리팩터링된 패치를 업스트림으로 제출하게 될 예상 시간을 제시합니다(검토를 위해 리팩터링된 패치를 업스트림에 제출할 계획이 없으면 ACK에서 패치를 수락하지 않음).

  • 업스트림에서 패치를 수락할 수 없습니다. 그 이유는 다음과 같습니다. <여기에 이유 삽입>. 이 패치를 처리하려면 Android 커널팀에 문의하고 패치를 리팩터링하는 옵션을 논의하여, 검토를 위해 패치를 제출하고 업스트림에서 수락할 수 있도록 합니다.

이 외에도 정당한 이유는 많습니다. 버그 또는 패치를 제출할 때는 유효하고 정당한 이유를 포함하고 몇 차례의 반복 및 토론을 예상해야 합니다. Google은 ACK가 일부 패치를 보류할 것을 알고 있습니다. 특히, 모두가 업스트림의 작동 방식을 학습하는 GKI 초기 단계에 그렇습니다. 하지만, 이를 위해 제품 일정을 늦출 수는 없습니다. 업스트림하기 위한 요구사항은 시간이 지날수록 더 엄격해질 것으로 예상됩니다.

패치 요구사항

패치를 업스트림이나 ACK로 제출하려면 Linux 소스 트리에 설명된 Linux 커널 코딩 표준을 준수해야 합니다. scripts/checkpatch.pl 스크립트는 Gerrit 사전 제출 테스트의 일부로 실행되므로 테스트를 통과할 수 있도록 미리 실행해 보세요. 사전 제출 테스트와 동일한 구성으로 checkpatch 스크립트를 실행하려면 //build/kernel/static_analysis:checkpatch_presubmit를 사용합니다. 자세한 내용은 build/kernel/kleaf/docs/checkpatch.md 페이지를 참고하세요.

ACK 패치

ACK에 제출된 패치는 Linux 커널 코딩 표준 및 참여 가이드라인을 준수해야 합니다. 커밋 메시지에 Change-Id 태그를 포함해야 합니다. 패치를 여러 브랜치(예: android-mainlineandroid12-5.4)에 제출하려면 패치의 모든 인스턴스에 동일한 Change-Id를 사용해야 합니다.

업스트림 검토를 위해 먼저 패치를 LKML에 제출합니다. 패치가 다음과 같은 경우 안내를 따라 처리합니다.

  • 업스트림에서 수락되면 패치는 android-mainline으로 자동 병합됩니다.
  • 업스트림에서 수락되지 않으면 업스트림 제출에 관한 참조 또는 LKML에 제출되지 못한 이유에 관한 설명과 함께 패치를 android-mainline으로 제출합니다.

업스트림 또는 android-mainline에서 패치가 수락되면 적절한 LTS 기반 ACK로 패치가 백포팅될 수 있습니다(예: Android 관련 코드를 수정하는 패치의 경우 android12-5.4android11-5.4). android-mainline에 제출하면 새 업스트림 출시 후보를 사용하여 테스트할 수 있으며, 패치가 다음 LTS 기반 ACK에 포함되는 것이 보장됩니다. 업스트림 패치가 android12-5.4로 백포팅되는 경우는 예외입니다(패치가 이미 android-mainline에 있을 가능성이 높기 때문).

업스트림 패치

참여 가이드라인에 명시된 대로 ACK 커널로 보낼 업스트림 패치는 다음 그룹에 속합니다(수락될 가능성 순으로 나열됨).

  • UPSTREAM: - 'android-mainline'에서 선택된 패치는 합당한 사용 사례가 있는 경우 ACK에서 수락될 가능성이 높습니다.
  • BACKPORT: - 명확하게 선택되지 않고 수정이 필요한 업스트림의 패치도 합당한 사용 사례가 있는 경우 수락될 가능성이 높습니다.
  • FROMGIT: - Linux 메인라인으로 제출하기 위한 준비로 유지관리자 브랜치에서 선택된 패치는 예정된 제출 마감일이 있는 경우 수락될 수도 있습니다. 이 경우 콘텐츠 및 일정 모두에서 타당한 이유가 있어야 합니다.
  • FROMLIST: - LKML에 제출했지만 유지관리자 브랜치에서 아직 수락하지 않은 패치는 업스트림 Linux에 포함되든 아니든 수락되어야 하는 충분한 이유가 없다면 수락될 가능성이 낮습니다. Android 커널팀과의 논의를 용이하게 하려면 FROMLIST 패치와 관련된 문제가 있어야 합니다.

Android 관련 패치

필수 변경사항을 업스트림에 포함할 수 없는 경우 트리 외부 패치를 ACK에 직접 제출해 볼 수 있습니다. 트리 외부 패치를 제출하려면 패치와 패치를 업스트림에 제출할 수 없는 이유(앞의 예시 목록 참고)를 포함하여 IT에서 문제를 생성해야 합니다. 그러나 코드를 업스트림으로 제출할 수 없는 경우도 있습니다. 이러한 사례는 다음과 같이 처리하며 Android 관련 패치를 위한 참여 가이드라인을 따르고 제목에 ANDROID: 접두어로 태그를 지정해야 합니다.

gki_defconfig의 변경사항

gki_defconfig에 관한 모든 CONFIG 변경사항은 CONFIG가 특정 아키텍처를 지정하지 않는 한 arm64 및 x86 버전에 모두 적용되어야 합니다. CONFIG 설정에 관한 변경을 요청하려면 IT에서 해당 변경사항을 논의하는 문제를 만들어야 합니다. 커널 모듈 인터페이스(KMI)가 고정된 후에는 KMI에 영향을 주는 CONFIG 변경사항은 모두 거부됩니다. 파트너가 단일 구성에 충돌하는 설정을 요청하는 경우 Google은 관련 버그에 관한 토론을 통해 충돌을 해결합니다.

업스트림에 존재하지 않는 코드

이미 Android와 관련된 코드의 수정사항은 업스트림으로 전송할 수 없습니다. 예를 들어, 바인더 드라이버가 업스트림에 유지되더라도 바인더 드라이버의 우선순위 상속 기능에 관한 수정사항은 Android와 관련되어 있으므로 업스트림으로 전송할 수 없습니다. 코드를 업스트림에 전송할 수 없는 이유를 버그와 패치에 명시합니다. 가능하면 패치를 업스트림으로 제출할 수 있는 부분과 업스트림으로 제출할 수 없는 Android 관련 부분으로 분할하여 ACK에서 유지관리하는 트리 외부 코드의 양을 최소화합니다.

이 카테고리의 다른 변경사항은 KMI 표현 파일, KMI 기호 목록, gki_defconfig, 빌드 스크립트 또는 구성, 업스트림에 없는 기타 스크립트의 업데이트입니다.

트리 외부 모듈

업스트림 Linux는 트리 외부 모듈의 빌드 지원을 권장하지 않습니다. 이는 Linux 유지관리자에서 커널 내 소스 또는 바이너리 호환성을 보장하지 않으며 트리에 없는 코드를 지원하지 않는 것을 고려할 때 합리적인 결정입니다. 하지만, GKI가 공급업체 모듈에 ABI를 보장하여 커널에서 지원하는 전체 기간 동안 KMI 인터페이스가 안정되도록 합니다. 따라서, ACK에는 허용되지만 업스트림에서는 허용되지 않는 공급업체 모듈을 지원하는 변경사항이 있습니다.

예를 들어, 내보내기를 사용하는 모듈이 소스 트리에 없는 경우 EXPORT_SYMBOL_GPL() 매크로를 추가하는 패치를 고려해보세요. EXPORT_SYMBOL_GPL() 업스트림을 요청하고 새로 내보낸 기호를 사용하는 모듈을 제공해야 하지만, 모듈이 업스트림으로 제출되지 않는 합당한 이유가 있다면 ACK에 패치를 제출할 수 있습니다. 이때 모듈을 업스트림할 수 없는 이유를 문제에 포함해야 합니다. (GPL이 아닌 변형인 EXPORT_SYMBOL()은 요청하지 마세요.)

숨겨진 구성

일부 트리 내 모듈은 gki_defconfig에 지정할 수 없는 숨겨진 구성을 자동으로 선택합니다. 예를 들어, CONFIG_SND_SOC_SOF=y가 구성되면 CONFIG_SND_SOC_TOPOLOGY가 자동으로 선택됩니다. 트리 외부 모듈 빌드를 수용하기 위해 GKI에는 숨겨진 구성을 사용 설정하는 메커니즘이 포함되어 있습니다.

숨겨진 구성을 사용 설정하려면 gki_defconfig에 사용 설정된 CONFIG_GKI_HACKS_TO_FIX 커널 구성을 기반으로 숨겨진 구성이 자동 선택되도록 init/Kconfig.gkiselect 문을 추가합니다. 이 메커니즘은 숨겨진 구성에만 사용합니다. 숨겨진 구성이 아닌 경우에는 구성을 gki_defconfig에 명시적으로 또는 종속 항목으로 지정해야 합니다.

로드 가능한 거버너

로드할 수 있는 거버너를 지원하는 커널 프레임워크(예: cpufreq)의 경우 기본 거버너(예: cpufreqschedutil 거버너)를 재정의할 수 있습니다. 로드 가능한 거버너 또는 드라이버를 지원하지 않지만 여전히 공급업체별 구현이 필요한 프레임워크(예: 열 프레임워크)의 경우 IT에서 문제를 만들고 Android 커널팀과 상담하세요.

Google은 개발자 및 업스트림 유지관리자와 협업하여 필요한 지원을 추가할 것입니다.

공급업체 후크

이전 출시에서는 개발자가 공급업체별 수정사항을 코어 커널에 직접 추가할 수 있었습니다. GKI 2.0에서는 제품별 코드가 코어 커널의 업스트림 또는 ACK에서 허용되지 않아 모듈에 구현해야 하므로 이러한 작업은 불가능합니다. 파트너가 코어 커널 코드에 미치는 영향을 최소화하여 사용하고 있는 부가 가치가 높은 기능을 사용 설정하기 위해 GKI는 핵심 커널 코드에서 모듈을 호출할 수 있는 공급업체 후크를 허용합니다. 또한, 키 데이터 구조는 공급업체별 데이터를 저장하여 이러한 기능을 구현할 수 있는 공급업체 데이터 필드를 사용하여 패딩될 수 있습니다.

공급업체 후크는 공급업체 모듈이 연결할 수 있는 tracepoint(트레이스 이벤트 아님)를 기반으로 하여 두 가지 변형(일반 및 제한)으로 제공됩니다. 예를 들어, 공급업체는 작업 종료 시 정산 작업을 위해 새 sched_exit() 함수를 추가하는 대신 처리를 위해 공급업체 모듈에서 연결할 수 있는 do_exit()에 후크를 추가하면 됩니다. 구현 예로는 다음과 같은 공급업체 후크가 있습니다.

  • 일반 공급업체 후크는 DECLARE_HOOK()를 사용하여 이름이 trace_name인 tracepoint 함수를 만듭니다. 여기서 name은 트레이스의 고유 식별자입니다. 규칙에 따라 일반 공급업체 후크 이름은 android_vh로 시작하므로 sched_exit() 후크의 이름은 android_vh_sched_exit입니다.
  • 제한된 공급업체 후크는 CPU가 오프라인이거나 원자가 아닌 컨텍스트가 필요한 경우에도 연결된 함수를 호출해야 하는 스케줄러 후크와 같은 경우에 필요합니다. 제한된 공급업체 후크는 분리할 수 없으므로 제한된 후크에 연결된 모듈은 로드를 취소할 수 없습니다. 제한된 공급업체 후크 이름은 android_rvh로 시작합니다.

공급업체 후크를 추가하려면 IT에 문제를 신고하고 패치를 제출합니다(모든 Android 관련 패치와 마찬가지로 문제가 있어야 하고 정당한 이유를 제공해야 함). 공급업체 후크는 ACK에서만 지원되므로 업스트림 Linux에 이러한 패치를 전송하면 안 됩니다.

구조에 공급업체 필드 추가

ANDROID_VENDOR_DATA() 매크로를 사용하여 android_vendor_data 필드를 추가하는 방식으로 공급업체 데이터를 키 데이터 구조와 연결할 수 있습니다. 예를 들어, 부가 가치가 높은 기능을 지원하려면 다음 코드 샘플과 같이 구조에 필드를 추가하면 됩니다.

공급업체에서 필요한 필드와 OEM에서 필요한 필드 간에 잠재된 충돌을 방지하려면 OEM은 ANDROID_VENDOR_DATA() 매크로를 사용하여 선언된 필드를 사용하면 안 됩니다. 대신 OEM은 ANDROID_OEM_DATA()를 사용하여 android_oem_data 필드를 선언해야 합니다.

#include <linux/android_vendor.h>
...
struct important_kernel_data {
  [all the standard fields];
  /* Create vendor data for use by hook implementations. The
   * size of vendor data is based on vendor input. Vendor data
   * can be defined as single u64 fields like the following that
   * declares a single u64 field named "android_vendor_data1" :
   */
  ANDROID_VENDOR_DATA(1);

  /*
   * ...or an array can be declared. The following is equivalent to
   * u64 android_vendor_data2[20]:
   */
  ANDROID_VENDOR_DATA_ARRAY(2, 20);

  /*
   * SoC vendors must not use fields declared for OEMs and
   * OEMs must not use fields declared for SoC vendors.
   */
  ANDROID_OEM_DATA(1);

  /* no further fields */
}

공급업체 후크 정의

DECLARE_HOOK() 또는 DECLARE_RESTRICTED_HOOK()를 사용하여 공급업체 후크를 선언한 다음 이를 코드에 tracepoint로 추가하여 공급업체 후크를 tracepoint로 커널 코드에 추가합니다. 예를 들어, 기존 do_exit() 커널 함수에 trace_android_vh_sched_exit()를 추가하려면 다음을 실행합니다.

#include <trace/hooks/exit.h>
void do_exit(long code)
{
    struct task_struct *tsk = current;
    ...
    trace_android_vh_sched_exit(tsk);
    ...
}

trace_android_vh_sched_exit() 함수는 처음에 어떤 항목이 연결되었는지 아닌지만 확인합니다. 하지만, 공급업체 모듈이 register_trace_android_vh_sched_exit()를 사용하여 핸들러를 등록하면 등록된 함수가 호출됩니다. 핸들러는 보류된 잠금, RCS 상태 및 기타 요소에 관해서는 컨텍스트를 인식해야 합니다. 후크는 include/trace/hooks 디렉터리의 헤더 파일에 정의해야 합니다.

예를 들어, 다음 코드는 include/trace/hooks/exit.h 파일의 trace_android_vh_sched_exit()에 가능한 선언을 제공합니다.

/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched
#define TRACE_INCLUDE_PATH trace/hooks

#if !defined(_TRACE_HOOK_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HOOK_SCHED_H
#include <trace/hooks/vendor_hooks.h>
/*
 * Following tracepoints are not exported in tracefs and provide a
 * mechanism for vendor modules to hook and extend functionality
 */

struct task_struct;

DECLARE_HOOK(android_vh_sched_exit,
             TP_PROTO(struct task_struct *p),
             TP_ARGS(p));

#endif /* _TRACE_HOOK_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

공급업체 후크에 필요한 인터페이스를 인스턴스화하려면 후크 선언이 있는 헤더 파일을 drivers/android/vendor_hooks.c에 추가하고 기호를 내보냅니다. 예를 들어, 다음 코드는 android_vh_sched_exit() 후크 선언을 완료합니다.

#ifndef __GENKSYMS__
/* struct task_struct */
#include <linux/sched.h>
#endif

#define CREATE_TRACE_POINTS
#include <trace/hooks/vendor_hooks.h>
#include <trace/hooks/exit.h>
/*
 * Export tracepoints that act as a bare tracehook (i.e. have no trace
 * event associated with them) to allow external modules to probe
 * them.
 */
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_sched_exit);

참고: 후크 선언 내에서 사용되는 데이터 구조는 ABI 안정성을 보장하기 위해 완전히 정의해야 합니다. 그러지 않으면 불투명 포인터를 역참조하거나 크기가 지정된 컨텍스트에서 구조체를 사용하는 것이 안전하지 않습니다. 이러한 데이터 구조의 전체 정의를 제공하는 include는 drivers/android/vendor_hooks.c#ifndef __GENKSYMS__ 섹션 내에 들어가야 합니다. include/trace/hooks의 헤더 파일에는 KMI를 중단하는 CRC 변경을 방지하기 위해 유형 정의가 있는 커널 헤더 파일이 포함되어 있어서는 안 됩니다. 대신 유형을 전방 선언하세요.

공급업체 후크에 연결

공급업체 후크를 사용하려면 공급업체 모듈은 후크의 핸들러를 등록해야 합니다(일반적으로 모듈 초기화 중에 실행됨). 예를 들어 다음 코드는 trace_android_vh_sched_exit()foo.ko 모듈 핸들러를 보여 줍니다.

#include <trace/hooks/sched.h>
...
static void foo_sched_exit_handler(void *data, struct task_struct *p)
{
    foo_do_exit_accounting(p);
}
...
static int foo_probe(..)
{
    ...
    rc = register_trace_android_vh_sched_exit(foo_sched_exit_handler, NULL);
    ...
}

코어 커널 기능

이전에 제시된 기법으로 모듈의 기능을 구현할 수 없다면 기능을 Android 관련 수정사항으로 코어 커널에 추가해야 합니다. Issue Tracker(IT)에서 문제를 만들어 논의를 시작합니다.

사용자 애플리케이션 프로그래밍 인터페이스(UAPI)

  • UAPI 헤더 파일. UAPI 헤더 파일의 변경사항은 Android 전용 인터페이스를 변경하는 것이 아닌 한 업스트림에서 발생해야 합니다. 공급업체별 헤더 파일을 사용하여 공급업체 모듈과 공급업체 사용자 공간 코드 간의 인터페이스를 정의합니다.
  • sysfs 노드. 새 sysfs 노드를 GKI 커널에 추가하면 안 됩니다(이러한 추가는 공급업체 모듈에서만 유효함). SoC 및 기기 제약이 없는 라이브러리에서 사용하는 sysfs 노드와 Android 프레임워크를 구성하는 자바 코드는 호환되는 방식으로만 변경할 수 있으며 Android 관련 sysfs 노드가 아닌 경우 업스트림에서 변경해야 합니다. 공급업체 사용자 공간에서 사용할 공급업체별 sysfs 노드를 만들 수 있습니다. 기본적으로 사용자 공간에 의한 sysfs 노드 액세스는 SELinux를 사용하여 거부됩니다. 승인된 공급업체 소프트웨어에 의한 액세스를 허용하도록 적절한 SELinux 라벨을 추가하는 것은 공급업체의 책임입니다.
  • DebugFS 노드. 공급업체 모듈은 디버깅을 위해서만 debugfs에 노드를 정의할 수 있습니다(기기의 일반적인 작동 중에는 debugfs가 마운트되지 않기 때문).