Mengembangkan kode kernel untuk GKI

Generic Kernel Image (GKI) mengurangi fragmentasi kernel dengan menyesuaikan dengan kernel Linux upstream. Namun, ada alasan valid mengapa beberapa patch tidak dapat diterima di upstream, dan ada jadwal produk yang harus dipenuhi, sehingga beberapa patch dipertahankan di sumber Android Common Kernel (ACK) tempat GKI dibuat.

Developer harus mengirimkan perubahan kode ke upstream menggunakan Linux Kernel Mailing List (LKML) sebagai pilihan pertama, dan mengirimkan perubahan kode ke cabang android-mainline ACK hanya jika ada alasan kuat mengapa upstream tidak layak. Contoh alasan yang valid dan cara menanganinya tercantum sebagai berikut.

  • Patch dikirim ke LKML, tetapi tidak diterima tepat waktu untuk rilis produk. Untuk menangani patch ini:

    • Berikan bukti bahwa patch dikirim ke LKML dan komentar yang diterima untuk patch, atau perkiraan waktu pengiriman patch ke upstream.
    • Tentukan tindakan untuk menerapkan patch di ACK, mendapatkannya disetujui upstream, lalu mengeluarkannya dari ACK saat versi upstream akhir digabungkan ke ACK.
  • Patch menentukan EXPORT_SYMBOLS_GPL() untuk modul vendor, tetapi tidak dapat dikirimkan ke upstream karena tidak ada modul dalam hierarki yang menggunakan simbol tersebut. Untuk menangani patch ini, berikan detail tentang alasan modul Anda tidak dapat dikirim ke upstream dan alternatif yang Anda pertimbangkan sebelum membuat permintaan ini.

  • Patch tidak cukup umum untuk upstream dan tidak ada waktu untuk memfaktorkan ulang sebelum rilis produk. Untuk menangani patch ini, berikan perkiraan waktu pengiriman patch yang difaktorkan ulang ke upstream (patch tidak akan diterima di ACK tanpa rencana untuk mengirimkan patch yang difaktorkan ulang ke upstream untuk ditinjau).

  • Patch tidak dapat diterima oleh upstream karena... <insert reason here>. Untuk menangani patch ini, hubungi tim kernel Android dan kerja samalah dengan kami terkait opsi untuk memfaktorkan ulang patch sehingga dapat dikirim untuk ditinjau dan diterima di upstream.

Masih ada banyak kemungkinan justifikasi. Saat Anda mengirimkan bug atau patch, sertakan justifikasi yang valid dan perkirakan beberapa iterasi dan diskusi. Kami menyadari bahwa ACK membawa beberapa patch, terutama pada fase awal GKI saat semua orang sedang mempelajari cara bekerja di upstream, tetapi tidak dapat memperlonggar jadwal produk untuk melakukannya. Perkiraan persyaratan upstream akan menjadi lebih ketat seiring waktu.

Persyaratan patch

Patch harus sesuai dengan standar coding kernel Linux yang dijelaskan dalam hierarki sumber Linux, baik yang dikirim ke upstream maupun ke ACK. Skrip scripts/checkpatch.pl dijalankan sebagai bagian dari pengujian pra-pengiriman Gerrit, jadi jalankan terlebih dahulu untuk memastikan skrip tersebut lulus. Untuk menjalankan skrip checkpatch dengan konfigurasi yang sama dengan pengujian pra-pengiriman, gunakan //build/kernel/static_analysis:checkpatch_presubmit. Untuk mengetahui detailnya, lihat build/kernel/kleaf/docs/checkpatch.md.

Patch ACK

Patch yang dikirimkan ke ACK harus sesuai dengan standar coding kernel Linux dan pedoman kontribusi. Anda harus menyertakan tag Change-Id dalam pesan commit; jika Anda mengirimkan patch ke beberapa cabang (misalnya, android-mainline dan android12-5.4), Anda harus menggunakan Change-Id yang sama untuk semua instance patch.

Kirim patch ke LKML terlebih dahulu untuk peninjauan upstream. Jika patch:

  • Di bagian upstream yang diterima, file tersebut akan otomatis digabungkan ke android-mainline.
  • Tidak diterima di upstream, kirimkan ke android-mainline dengan referensi ke pengiriman upstream atau penjelasan alasan tidak dikirimkan ke LKML.

Setelah patch diterima di upstream atau di android-mainline, patch tersebut dapat di-backport ke ACK berbasis LTS yang sesuai (seperti android12-5.4 dan android11-5.4 untuk patch yang memperbaiki kode khusus Android). Mengirimkan ke android-mainline memungkinkan pengujian dengan kandidat rilis upstream baru dan memastikan bahwa patch ada di ACK berbasis LTS berikutnya. Pengecualian mencakup kasus saat patch upstream di-backport ke android12-5.4 (karena patch mungkin sudah berada di android-mainline).

Patch upstream

Seperti yang ditentukan dalam panduan kontribusi, patch upstream yang ditujukan untuk kernel ACK termasuk dalam grup berikut (tercantum dalam urutan kemungkinan diterima).

  • UPSTREAM: - Patch yang dipilih dari 'android-mainline` kemungkinan akan diterima ke ACK jika ada kasus penggunaan yang wajar.
  • BACKPORT: - Patch dari upstream yang tidak begitu rapi dan memerlukan modifikasi juga kemungkinan diterima jika ada kasus penggunaan yang wajar.
  • FROMGIT: - Patch yang dipilih dari cabang pemeliharaan sebagai persiapan untuk pengiriman ke mainline Linux mungkin diterima jika ada batas waktu mendatang. Hal ini harus dibenarkan untuk konten dan jadwal.
  • FROMLIST: - Patch yang telah dikirimkan ke LKML, tetapi belum diterima ke cabang pengelola, kemungkinan tidak akan diterima, kecuali jika justifikasinya cukup meyakinkan sehingga patch akan diterima baik di Linux upstream maupun tidak (kami berasumsi bahwa patch tidak akan diterima). Harus ada masalah yang terkait dengan patch FROMLIST untuk memfasilitasi diskusi dengan tim kernel Android.

Patch khusus Android

Jika tidak dapat menerapkan perubahan yang diperlukan di upstream, Anda dapat mencoba mengirimkan patch out-of-tree ke ACK secara langsung. Untuk mengirimkan patch di luar tree, Anda harus membuat masalah di IT yang mengutip patch dan alasan mengapa patch tidak dapat dikirim ke upstream (lihat daftar sebelumnya untuk mengetahui contohnya). Namun, ada beberapa kasus saat kode tidak dapat dikirim ke upstream. Kasus ini dibahas sebagai berikut dan harus mengikuti panduan kontribusi untuk patch khusus Android dan diberi tag dengan awalan ANDROID: di subjek.

Perubahan pada gki_defconfig

Semua perubahan CONFIG ke gki_defconfig harus diterapkan ke versi arm64 dan x86, kecuali jika CONFIG bersifat khusus arsitektur. Untuk meminta perubahan pada setelan CONFIG, buat masalah di IT untuk mendiskusikan perubahan tersebut. Setiap perubahan CONFIG yang memengaruhi Antarmuka Modul Kernel (KMI) setelah dibekukan akan ditolak. Jika partner meminta setelan yang bertentangan untuk satu konfigurasi, kami akan menyelesaikan konflik melalui diskusi tentang bug terkait.

Kode yang tidak ada upstream

Modifikasi pada kode yang sudah khusus Android tidak dapat dikirim ke upstream. Misalnya, meskipun driver binder dipertahankan di upstream, modifikasi pada fitur pewarisan prioritas driver binder tidak dapat dikirim ke upstream karena ditujukan khusus Android. Jelaskan dengan jelas bug dan patch Anda tentang alasan kode tidak dapat dikirim ke upstream. Jika memungkinkan, bagi patch menjadi beberapa bagian yang dapat dikirim upstream dan bagian khusus Android yang tidak dapat dikirimkan pada upstream untuk meminimalkan jumlah kode out-of-tree yang dipertahankan di ACK.

Perubahan lain dalam kategori ini adalah pembaruan pada file representasi KMI, daftar simbol KMI, gki_defconfig, skrip atau konfigurasi build, atau skrip lainnya yang tidak ada di upstream.

Modul di luar hierarki

Linux upstream secara aktif tidak menganjurkan dukungan untuk mem-build modul di luar hierarki. Ini adalah posisi yang wajar mengingat bahwa pengelola Linux tidak memberikan jaminan tentang sumber dalam kernel atau kompatibilitas biner dan tidak ingin mendukung kode yang tidak ada dalam hierarki. Namun, GKI memang memberikan jaminan ABI untuk modul vendor, memastikan bahwa antarmuka KMI stabil untuk lifetime kernel yang didukung. Oleh karena itu, ada kelas perubahan untuk mendukung modul vendor yang dapat diterima untuk ACK, tetapi tidak dapat diterima untuk upstream.

Misalnya, pertimbangkan patch yang menambahkan makro EXPORT_SYMBOL_GPL() dengan modul yang menggunakan ekspor tidak ada dalam hierarki sumber. Meskipun Anda harus mencoba meminta EXPORT_SYMBOL_GPL() upstream dan menyediakan modul yang menggunakan simbol yang baru diekspor, jika ada justifikasi yang valid mengapa modul tidak dikirim upstream, Anda dapat mengirimkan patch ke ACK. Anda harus menyertakan justifikasi mengapa modul tidak dapat di-upstream dalam masalah. (Jangan meminta varian non-GPL, EXPORT_SYMBOL().)

Konfigurasi tersembunyi

Beberapa modul dalam hierarki secara otomatis memilih konfigurasi tersembunyi yang tidak dapat ditentukan di gki_defconfig. Misalnya, CONFIG_SND_SOC_TOPOLOGY dipilih secara otomatis saat CONFIG_SND_SOC_SOF=y dikonfigurasi. Untuk mengakomodasi pembuatan modul di luar hierarki, GKI menyertakan mekanisme untuk mengaktifkan konfigurasi tersembunyi.

Untuk mengaktifkan konfigurasi tersembunyi, tambahkan pernyataan select di init/Kconfig.gki sehingga otomatis dipilih berdasarkan konfigurasi kernel CONFIG_GKI_HACKS_TO_FIX, yang diaktifkan di gki_defconfig. Gunakan mekanisme ini hanya untuk konfigurasi tersembunyi. Jika konfigurasi tidak disembunyikan, konfigurasi harus ditentukan dalam gki_defconfig secara eksplisit atau sebagai dependensi.

Gubernur yang dapat dimuat

Untuk framework kernel (seperti cpufreq) yang mendukung gubernur yang dapat dimuat, Anda dapat mengganti gubernur default (seperti gubernur schedutil cpufreq. Untuk framework (seperti framework termal) yang tidak mendukung gubernur atau driver yang dapat dimuat, tetapi masih memerlukan implementasi khusus vendor, buat masalah di bagian IT dan konsultasikan dengan tim kernel Android.

Kami akan bekerja sama dengan Anda dan pengelola upstream untuk menambahkan dukungan yang diperlukan.

Hook vendor

Dalam rilis sebelumnya, Anda dapat menambahkan modifikasi khusus vendor langsung ke kernel inti. Hal ini tidak dapat dilakukan dengan GKI 2.0 karena kode khusus produk harus diterapkan dalam modul dan tidak akan diterima di kernel inti upstream atau di ACK. Untuk mengaktifkan fitur nilai tambah yang diandalkan partner dengan dampak minimal pada kode kernel inti, GKI menerima hook vendor yang memungkinkan modul dipanggil dari kode kernel inti. Selain itu, struktur data utama dapat diisi dengan kolom data vendor yang tersedia untuk menyimpan data khusus vendor guna menerapkan fitur ini.

Hook vendor tersedia dalam dua varian (normal dan terbatas) yang didasarkan pada tracepoint (bukan peristiwa rekaman aktivitas) yang dapat dilampirkan ke modul vendor. Misalnya, bukan menambahkan fungsi sched_exit() baru untuk melakukan pencatatan saat tugas selesai, vendor dapat menambahkan hook di do_exit() yang dapat dilampirkan modul vendor untuk diproses. Contoh implementasi mencakup hook vendor berikut.

  • Hook vendor normal menggunakan DECLARE_HOOK() untuk membuat fungsi tracepoint dengan nama trace_name dengan name adalah ID unik untuk trace. Secara konvensional, nama hook vendor normal diawali dengan android_vh, sehingga nama untuk hook sched_exit() adalah android_vh_sched_exit.
  • Hook vendor yang dibatasi diperlukan untuk kasus seperti hook penjadwal saat fungsi yang dilampirkan harus dipanggil meskipun CPU sedang offline atau memerlukan konteks nonatomic. Hook vendor yang dibatasi tidak dapat dilepas, sehingga modul yang dilampirkan ke hook yang dibatasi tidak akan pernah di-unload. Nama hook vendor yang dibatasi dimulai dengan android_rvh.

Untuk menambahkan hook vendor, ajukan masalah di IT dan kirimkan patch (seperti semua patch khusus Android, masalah harus ada dan Anda harus memberikan justifikasi). Dukungan untuk hook vendor hanya ada di ACK, jadi jangan kirim patch ini ke Linux upstream.

Menambahkan kolom vendor ke struktur

Anda dapat mengaitkan data vendor dengan struktur data utama dengan menambahkan kolom android_vendor_data menggunakan makro ANDROID_VENDOR_DATA(). Misalnya, untuk mendukung fitur nilai tambah, tambahkan kolom ke struktur seperti yang ditunjukkan dalam contoh kode berikut.

Untuk menghindari potensi konflik antara kolom yang diperlukan oleh vendor dan kolom yang diperlukan oleh OEM, OEM tidak boleh menggunakan kolom yang dideklarasikan menggunakan makro ANDROID_VENDOR_DATA(). Sebagai gantinya, OEM harus menggunakan ANDROID_OEM_DATA() untuk mendeklarasikan kolom 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 */
}

Menentukan hook vendor

Tambahkan hook vendor ke kode kernel sebagai tracepoint dengan mendeklarasikannya menggunakan DECLARE_HOOK() atau DECLARE_RESTRICTED_HOOK(), lalu menambahkannya ke kode sebagai tracepoint. Misalnya, untuk menambahkan trace_android_vh_sched_exit() ke fungsi kernel do_exit() yang sudah ada:

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

Fungsi trace_android_vh_sched_exit() awalnya hanya memeriksa apakah ada yang terpasang. Namun, jika modul vendor mendaftarkan pengendali menggunakan register_trace_android_vh_sched_exit(), fungsi yang terdaftar akan dipanggil. Pengendali harus mengetahui konteks sehubungan dengan kunci yang ditahan, status RCS, dan faktor lainnya. Hook harus ditentukan dalam file header dalam direktori include/trace/hooks.

Misalnya, kode berikut memberikan kemungkinan deklarasi untuk trace_android_vh_sched_exit() dalam file include/trace/hooks/exit.h.

/* 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>

Untuk membuat instance antarmuka yang diperlukan untuk hook vendor, tambahkan file header dengan deklarasi hook ke drivers/android/vendor_hooks.c dan ekspor simbol. Misalnya, kode berikut menyelesaikan deklarasi hook 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);

CATATAN: Struktur data yang digunakan dalam deklarasi hook harus ditentukan sepenuhnya untuk menjamin stabilitas ABI. Jika tidak, Anda tidak aman untuk menguraikan pointer buram atau menggunakan struct dalam konteks berukuran besar. Include yang memberikan definisi lengkap struktur data tersebut harus berada di dalam bagian #ifndef __GENKSYMS__ dari drivers/android/vendor_hooks.c. File header di include/trace/hooks tidak boleh menyertakan file header kernel dengan definisi jenis untuk menghindari perubahan CRC yang merusak KMI. Sebagai gantinya, deklarasikan jenisnya.

Pasang ke hook vendor

Untuk menggunakan hook vendor, modul vendor harus mendaftarkan pengendali untuk hook (biasanya dilakukan selama inisialisasi modul). Misalnya, kode berikut menunjukkan pengendali foo.ko modul untuk trace_android_vh_sched_exit().

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

Menggunakan hook vendor dari file header

Untuk menggunakan hook vendor dari file header, Anda mungkin perlu memperbarui file header hook vendor untuk menghapus definisi TRACE_INCLUDE_PATH guna menghindari error build yang menunjukkan bahwa file header titik rekaman aktivitas tidak dapat ditemukan. Misalnya,

In file included from .../common/init/main.c:111:
In file included from .../common/include/trace/events/initcall.h:74:
.../common/include/trace/define_trace.h:95:10: fatal error: 'trace/hooks/initcall.h' file not found
   95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:90:32: note: expanded from macro 'TRACE_INCLUDE'
   90 | # define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
      |                                ^~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:87:34: note: expanded from macro '__TRACE_INCLUDE'
   87 | # define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:10:27: note: expanded from macro '__stringify'
   10 | #define __stringify(x...)       __stringify_1(x)
      |                                 ^~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:9:29: note: expanded from macro '__stringify_1'
    9 | #define __stringify_1(x...)     #x
      |                                 ^~
<scratch space>:14:1: note: expanded from here
   14 | "trace/hooks/initcall.h"
      | ^~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Untuk memperbaiki jenis error build ini, terapkan perbaikan yang setara ke file header hook vendor yang Anda sertakan. Untuk informasi tambahan, lihat https://r.android.com/3066703.

diff --git a/include/trace/hooks/mm.h b/include/trace/hooks/mm.h
index bc6de7e53d66..039926f7701d 100644
--- a/include/trace/hooks/mm.h
+++ b/include/trace/hooks/mm.h
@@ -2,7 +2,10 @@
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mm

+#ifdef CREATE_TRACE_POINTS
 #define TRACE_INCLUDE_PATH trace/hooks
+#define UNDEF_TRACE_INCLUDE_PATH
+#endif

Menentukan UNDEF_TRACE_INCLUDE_PATH akan memberi tahu include/trace/define_trace.h untuk menghapus definisi TRACE_INCLUDE_PATH setelah membuat titik rekaman aktivitas.

Fitur kernel inti

Jika tidak ada teknik sebelumnya yang memungkinkan Anda menerapkan fitur dari modul, Anda harus menambahkan fitur tersebut sebagai modifikasi khusus Android ke kernel inti. Buat masalah di pelacak masalah (IT) untuk memulai percakapan.

User application programming interface (UAPI)

  • File header UAPI. Perubahan pada file header UAPI harus terjadi di upstream, kecuali jika perubahan tersebut dilakukan pada antarmuka khusus Android. Gunakan file header khusus vendor untuk menentukan antarmuka antara modul vendor dan kode ruang pengguna vendor.
  • node sysfs. Jangan tambahkan node sysfs baru ke kernel GKI (penambahan seperti ini hanya valid dalam modul vendor). Node sysfs yang digunakan oleh library SoC- dan perangkat agnostik serta kode Java yang menyusun framework Android hanya dapat diubah dengan cara yang kompatibel dan harus diubah upstream jika bukan node sysfs khusus Android. Anda dapat membuat node sysfs khusus vendor untuk digunakan oleh ruang pengguna vendor. Secara default, akses ke node sysfs oleh ruang pengguna ditolak menggunakan SELinux. Vendor dapat menambahkan label SELinux yang sesuai untuk mengizinkan akses oleh software vendor resmi.
  • Node DebugFS. Modul vendor dapat menentukan node di debugfs hanya untuk proses debug (karena debugfs tidak dipasang selama operasi normal perangkat).