GKI용 커널 코드 개발

GKI(Generic Kernel Image)는 업스트림 Linux 커널과 밀접하게 일치하여 커널 조각화를 줄입니다. 그러나 일부 패치는 업스트림에서 허용되지 않는 타당한 이유가 있고 충족해야 하는 제품 일정이 있으므로 일부 패치는 GKI가 빌드되는 Android ACK(Common Kernel) 소스에서 유지 관리됩니다.

개발자는 LKML(Linux Kernel Mailing List)을 첫 번째 선택으로 사용하여 업스트림에 코드 변경 사항을 제출해야 하며 업스트림이 실행 가능하지 않은 강력한 이유가 있는 경우에만 ACK android-mainline 분기에 코드 변경 사항을 제출해야 합니다. 정당한 이유의 예와 처리 방법은 다음과 같습니다.

  • 패치가 LKML에 제출되었지만 제품 릴리스에 맞춰 제때 수락되지 않았습니다. 이 패치를 처리하려면:

    • 패치가 LKML에 제출되었다는 증거와 패치에 대해 받은 의견 또는 패치가 업스트림에 제출될 예상 시간을 제공합니다.
    • ACK에 패치를 적용하고 업스트림에서 승인을 받은 다음 최종 업스트림 버전이 ACK에 병합될 때 ACK에서 제거할 조치를 결정합니다.
  • 패치는 공급업체 모듈에 대해 EXPORT_SYMBOLS_GPL() 을 정의하지만 해당 기호를 사용하는 트리 내 모듈이 없기 때문에 업스트림으로 제출할 수 없습니다. 이 패치를 처리하려면 모듈을 업스트림에 제출할 수 없는 이유와 이 요청을 하기 전에 고려한 대안에 대한 세부정보를 제공하세요.

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

  • <여기에 이유 삽입> 때문에 업스트림에서 패치를 수락할 수 없습니다. 이 패치를 처리하려면 Android 커널 팀에 연락하여 검토를 위해 제출하고 업스트림에서 수락할 수 있도록 패치를 리팩터링하는 옵션에 대해 협력하십시오.

더 많은 잠재적인 근거가 있습니다. 버그나 패치를 제출할 때 유효한 이유를 포함하고 약간의 반복과 토론을 기대하십시오. 우리는 모두가 업스트림에서 작업하는 방법을 배우고 있지만 그렇게 하기 위해 제품 일정을 느슨하게 할 수는 없는 동안 특히 GKI의 초기 단계에서 ACK에 일부 패치가 포함될 것임을 알고 있습니다. 업스트림 요구 사항은 시간이 지남에 따라 더욱 엄격해질 것으로 예상됩니다.

패치 요구 사항

패치는 업스트림 제출이든 ACK 제출이든 Linux 소스 트리 에 설명된 Linux 커널 코딩 표준을 준수해야 합니다. scripts/checkpatch.pl 스크립트는 Gerrit 사전 제출 테스트의 일부로 실행되므로 사전에 실행하여 통과하는지 확인하십시오. 사전 제출 테스트와 동일한 구성으로 checkpatch 스크립트를 실행하려면 repo 체크아웃에서 build/static_analysis/checkpatch_presubmit.sh 를 사용하십시오.

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에 대한 변경 사항

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

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

이미 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_TOPOLOGY CONFIG_SND_SOC_SOF=y 가 구성되면 CONFIG_SND_SOC_TOPOLOGY가 자동으로 선택됩니다. 트리 외부 모듈 빌드를 수용하기 위해 GKI에는 숨겨진 구성을 활성화하는 메커니즘이 포함되어 있습니다.

숨겨진 구성을 활성화하려면 init/Kconfig.gkiselect 문을 추가하여 gki_defconfig 에서 활성화된 CONFIG_GKI_HACKS_TO_FIX 커널 구성에 따라 자동으로 선택되도록 합니다. 이 메커니즘은 숨겨진 구성에만 사용하십시오. 구성이 숨겨지지 않은 경우 명시적으로 또는 종속성으로 gki_defconfig 에 지정해야 합니다.

로드 가능한 거버너

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

필요한 지원을 추가하기 위해 귀하 및 업스트림 유지 관리자와 협력할 것입니다.

공급업체 후크

이전 릴리스에서는 공급업체별 수정 사항을 코어 커널에 직접 추가할 수 있었습니다. GKI 2.0에서는 제품별 코드가 모듈에서 구현되어야 하고 코어 커널 업스트림 또는 ACK에서 허용되지 않기 때문에 이것은 불가능합니다. 핵심 커널 코드에 대한 영향을 최소화하면서 파트너가 의존하는 부가 가치 기능을 활성화하기 위해 GKI는 핵심 커널 코드에서 모듈을 호출할 수 있도록 하는 공급업체 후크를 허용합니다. 또한 주요 데이터 구조는 이러한 기능을 구현하기 위해 공급업체별 데이터를 저장하는 데 사용할 수 있는 공급업체 데이터 필드로 채워질 수 있습니다.

공급업체 후크는 공급업체 모듈이 연결할 수 있는 추적점(추적 이벤트 아님)을 기반으로 하는 두 가지 변형(일반 및 제한됨)으로 제공됩니다. 예를 들어, 작업 종료 시 계정을 수행하기 위해 새로운 sched_exit() 함수를 추가하는 대신 공급업체는 처리를 위해 공급업체 모듈이 연결할 수 있는 후크를 do_exit() 에 추가할 수 있습니다. 구현 예에는 다음 공급업체 후크가 포함됩니다.

  • 일반 공급업체 후크는 DECLARE_HOOK() 를 사용하여 이름이 trace_ name 인 추적점 함수를 생성합니다. 여기서 name 은 추적의 고유 식별자입니다. 관례에 따라 일반 공급업체 후크 이름은 android_vh 로 시작하므로 sched_exit() 후크의 이름은 android_vh_sched_exit 입니다.
  • 제한된 벤더 후크는 CPU가 오프라인이거나 비원자 컨텍스트가 필요한 경우에도 연결된 함수를 호출해야 하는 스케줄러 후크와 같은 경우에 필요합니다. 제한된 공급업체 후크는 분리할 수 없으므로 제한된 후크에 연결된 모듈은 언로드할 수 없습니다. 하나의 첨부만 허용되므로 다른 첨부 시도는 -EBUSY 로 실패합니다. 제한된 공급업체 후크 이름은 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() 를 사용하여 선언한 다음 추적점으로 코드에 추가하여 커널 코드에 추적점으로 공급업체 후크를 추가합니다. 예를 들어, 기존 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/sched.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 */
#include <linux/sched.h>

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>

참고 : 후크 선언 내에서 사용되는 데이터 구조는 ABI 안정성을 보장하기 위해 완전히 정의되어야 합니다. 그렇지 않으면 불투명 포인터를 역참조하거나 크기가 지정된 컨텍스트에서 구조체를 사용하는 것이 안전하지 않습니다. 위의 예에서 #include <linux/sched.h>struct task_struct 의 정의를 가능하게 하고 ABI 추적을 가능하게 합니다.

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

#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);

공급업체 후크에 연결

공급업체 후크를 사용하려면 공급업체 모듈이 후크에 대한 처리기를 등록해야 합니다(일반적으로 모듈 초기화 중에 수행됨). 예를 들어, 다음 코드는 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, NULL);
    ...
}

핵심 커널 기능

이전 기술 중 어느 것도 모듈의 기능을 구현할 수 없는 경우 핵심 커널에 Android 관련 수정으로 기능을 추가해야 합니다. 문제 추적기(IT)에서 문제를 만들어 대화를 시작하세요.

사용자 응용 프로그래밍 인터페이스(UAPI)

  • UAPI 헤더 파일. UAPI 헤더 파일 에 대한 변경은 Android 전용 인터페이스에 대한 변경이 아닌 한 업스트림에서 발생해야 합니다. 공급업체별 헤더 파일을 사용하여 공급업체 모듈과 공급업체 사용자 공간 코드 간의 인터페이스를 정의합니다.
  • sysfs 노드. GKI 커널에 새 sysfs 노드를 추가하지 마십시오(이러한 추가는 공급업체 모듈에서만 유효합니다). SoC 및 장치에 구애받지 않는 라이브러리에서 사용하는 sysfs 노드와 Android 프레임워크를 구성하는 Java 코드는 호환되는 방식으로만 변경할 수 있으며 Android 전용 sysfs 노드가 아닌 경우 업스트림으로 변경해야 합니다. 공급업체 사용자 공간에서 사용할 공급업체별 sysfs 노드를 생성 할 수 있습니다 . 기본적으로 사용자 공간에 의한 sysfs 노드 액세스는 SELinux를 사용하여 거부됩니다. 인증된 공급업체 소프트웨어가 액세스할 수 있도록 적절한 SELinux 레이블을 추가하는 것은 공급업체에 달려 있습니다.
  • DebugFS 노드. 공급업체 모듈은 디버그용으로만 debugfs 에서 노드를 정의할 수 있습니다(디버그가 장치의 정상 작동 중에 마운트되지 debugfs 때문에).