Mengembangkan Kode Kernel untuk GKI

Generic Kernel Image (GKI) mengurangi fragmentasi kernel dengan menyelaraskannya dengan kernel Linux upstream. Namun, ada alasan yang 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) yang menjadi tempat pembuatan GKI.

Pengembang harus mengirimkan perubahan kode ke upstream menggunakan Milis Kernel Linux (LKML) sebagai pilihan pertama, dan mengirimkan perubahan kode ke cabang android-mainline ACK hanya jika ada alasan kuat mengapa upstream tidak dapat dijalankan. Contoh alasan yang sah dan cara penanganannya adalah sebagai berikut.

  • Tambalan telah diserahkan ke LKML, tetapi tidak diterima pada saat peluncuran produk. Untuk menangani tambalan ini:

    • Memberikan bukti bahwa patch telah dikirimkan ke LKML dan komentar yang diterima untuk patch tersebut, atau perkiraan waktu penyerahan patch di bagian upstream.
    • Putuskan tindakan yang akan diambil untuk menempatkan patch di ACK, mendapatkan persetujuan upstream, dan kemudian mengeluarkannya dari ACK ketika versi upstream final digabungkan ke dalam ACK.
  • Patch ini mendefinisikan EXPORT_SYMBOLS_GPL() untuk modul vendor, namun tidak dapat dikirimkan ke hulu karena tidak ada modul dalam pohon yang menggunakan simbol tersebut. Untuk menangani patch ini, berikan detail tentang alasan modul Anda tidak dapat dikirimkan ke upstream dan alternatif yang Anda pertimbangkan sebelum membuat permintaan ini.

  • Patch ini tidak cukup umum untuk upstream dan tidak ada waktu untuk memfaktorkannya ulang sebelum rilis produk. Untuk menangani patch ini, berikan perkiraan waktu kapan patch yang telah difaktorkan ulang akan dikirimkan di bagian hulu (patch tidak akan diterima di ACK tanpa rencana untuk mengirimkan patch yang telah difaktorkan ulang di bagian hulu untuk ditinjau).

  • Patch tidak dapat diterima oleh upstream karena... <masukkan alasan di sini> . Untuk menangani patch ini, hubungi tim kernel Android dan bekerja sama dengan kami mengenai opsi untuk memfaktorkan ulang patch tersebut sehingga dapat dikirimkan untuk ditinjau dan diterima di bagian upstream.

Masih banyak lagi pembenaran potensial. Saat Anda mengirimkan bug atau patch Anda, sertakan pembenaran yang valid dan harapkan beberapa iterasi dan diskusi. Kami menyadari bahwa ACK akan melakukan beberapa perbaikan, terutama pada fase awal GKI ketika semua orang sedang belajar cara bekerja di hulu namun tidak dapat melonggarkan jadwal produk untuk melakukannya. Diperkirakan persyaratan hulu akan menjadi lebih ketat seiring berjalannya waktu.

Persyaratan tambalan

Patch harus sesuai dengan standar pengkodean kernel Linux yang dijelaskan dalam pohon sumber Linux , baik dikirimkan secara upstream atau ke ACK. Skrip scripts/checkpatch.pl dijalankan sebagai bagian dari pengujian prapengiriman Gerrit, jadi jalankan terlebih dahulu untuk memastikan lolos. Untuk menjalankan skrip checkpatch dengan konfigurasi yang sama dengan pengujian presubmit, gunakan //build/kernel/static_analysis:checkpatch_presubmit . Untuk detailnya, lihat build/kernel/kleaf/docs/checkpatch.md .

tambalan ACK

Patch yang dikirimkan ke ACK harus sesuai dengan standar pengkodean kernel Linux dan pedoman kontribusi . Anda harus menyertakan tag Change-Id dalam pesan penerapan; 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.

Kirimkan patch ke LKML terlebih dahulu untuk review upstream. Jika tambalannya adalah:

  • Diterima di bagian upstream, secara otomatis digabungkan ke dalam android-mainline .
  • Tidak diterima upstream, kirimkan ke android-mainline dengan referensi pengajuan upstream atau penjelasan mengapa tidak dikirimkan ke LKML.

Setelah patch diterima baik 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 menjamin bahwa patch ada di ACK berbasis LTS berikutnya. Pengecualian mencakup kasus ketika patch upstream di-backport ke android12-5.4 (karena patch tersebut kemungkinan besar sudah ada di android-mainline ).

Tambalan hulu

Sebagaimana ditentukan dalam pedoman kontribusi , patch upstream yang ditujukan untuk kernel ACK termasuk dalam kelompok berikut (diurutkan berdasarkan kemungkinan diterima).

  • UPSTREAM: - Patch yang dipilih dari 'android-mainline` kemungkinan besar akan diterima di ACK jika ada kasus penggunaan yang wajar.
  • BACKPORT: - Patch dari upstream yang tidak dipilih dengan baik dan memerlukan modifikasi juga kemungkinan besar akan diterima jika ada kasus penggunaan yang wajar.
  • FROMGIT: - Patch yang dipilih dari cabang pengelola sebagai persiapan untuk dikirimkan ke jalur utama Linux mungkin diterima jika ada tenggat waktu yang akan datang. Hal ini harus dibenarkan baik dari segi isi maupun jadwalnya.
  • FROMLIST: - Patch yang telah dikirimkan ke LKML namun belum diterima di cabang pengelola namun kemungkinan besar tidak akan diterima, kecuali jika pembenarannya cukup kuat sehingga patch tersebut akan diterima baik patch tersebut mendarat di Linux upstream atau tidak (kami berasumsi bahwa itu tidak akan terjadi). Pasti ada masalah yang terkait dengan patch FROMLIST untuk memfasilitasi diskusi dengan tim kernel Android.

Patch khusus Android

Jika Anda tidak dapat melakukan perubahan yang diperlukan di bagian hulu, Anda dapat mencoba mengirimkan patch di luar pohon ke ACK secara langsung. Mengirimkan patch di luar pohon mengharuskan Anda membuat masalah di TI yang menyebutkan patch tersebut dan alasan mengapa patch tidak dapat dikirimkan ke upstream (lihat daftar sebelumnya untuk contohnya). Namun, ada beberapa kasus di mana kode tidak dapat dikirimkan ke hulu. Kasus-kasus ini tercakup sebagai berikut dan harus mengikuti pedoman kontribusi untuk patch khusus Android dan ditandai dengan awalan ANDROID: pada subjek.

Perubahan pada gki_defconfig

Semua perubahan CONFIG pada gki_defconfig harus diterapkan pada versi arm64 dan x86 kecuali CONFIG khusus untuk arsitektur. Untuk meminta perubahan pada pengaturan CONFIG , buat masalah di TI untuk mendiskusikan perubahan tersebut. Perubahan CONFIG apa pun yang memengaruhi Antarmuka Modul Kernel (KMI) setelah dibekukan akan ditolak. Jika mitra meminta pengaturan yang bertentangan untuk satu konfigurasi, kami menyelesaikan konflik melalui diskusi tentang bug terkait.

Kode yang tidak ada di hulu

Modifikasi pada kode yang sudah spesifik untuk 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 khusus untuk Android. Bersikaplah eksplisit dalam bug Anda dan tambal mengapa kode tidak dapat dikirim ke upstream. Jika memungkinkan, pisahkan patch menjadi beberapa bagian yang dapat dikirimkan ke upstream dan bagian khusus Android yang tidak dapat dikirimkan ke upstream untuk meminimalkan jumlah kode out-of-tree yang disimpan di ACK.

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

Modul di luar pohon

Linux upstream secara aktif melarang dukungan untuk membangun modul out-of-tree. Ini adalah posisi yang masuk akal mengingat pengelola Linux tidak memberikan jaminan tentang sumber dalam kernel atau kompatibilitas biner dan tidak ingin mendukung kode yang tidak ada dalam pohon. Namun, GKI memberikan jaminan ABI untuk modul vendor, memastikan bahwa antarmuka KMI stabil selama masa pakai kernel yang didukung. Oleh karena itu, ada sejumlah 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() di mana modul yang menggunakan ekspor tidak ada di pohon sumber. Meskipun Anda harus mencoba meminta EXPORT_SYMBOL_GPL() upstream dan menyediakan modul yang menggunakan simbol yang baru diekspor, jika ada alasan yang valid mengapa modul tidak dikirimkan ke upstream, Anda dapat mengirimkan patch ke ACK sebagai gantinya. Anda perlu menyertakan alasan mengapa modul tidak dapat di-upstream dalam masalah ini. (Jangan meminta varian non-GPL, EXPORT_SYMBOL() .)

Konfigurasi tersembunyi

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

Untuk mengaktifkan konfigurasi tersembunyi, tambahkan pernyataan select di init/Kconfig.gki sehingga secara 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 tersebut harus ditentukan di gki_defconfig baik secara eksplisit atau sebagai ketergantungan.

Gubernur yang dapat dimuat

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

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

Kait penjual

Pada 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 bernilai tambah yang diandalkan mitra dengan dampak minimal pada kode kernel inti, GKI menerima vendor hooks yang memungkinkan modul dipanggil dari kode kernel inti. Selain itu, struktur data utama dapat dilengkapi dengan bidang data vendor yang tersedia untuk menyimpan data khusus vendor guna mengimplementasikan fitur-fitur ini.

Kait vendor hadir dalam dua varian (normal dan terbatas) yang didasarkan pada titik jejak (bukan peristiwa jejak) yang dapat dilampirkan oleh modul vendor. Misalnya, daripada menambahkan fungsi sched_exit() baru untuk melakukan akuntansi saat tugas keluar, vendor dapat menambahkan hook di do_exit() yang dapat dilampirkan oleh modul vendor untuk diproses. Contoh implementasi mencakup vendor hooks berikut.

  • Kait vendor normal menggunakan DECLARE_HOOK() untuk membuat fungsi titik jejak dengan nama trace_ name dengan name sebagai pengidentifikasi unik untuk jejak tersebut. Berdasarkan konvensi, nama hook vendor normal dimulai dengan android_vh , jadi nama hook sched_exit() adalah android_vh_sched_exit .
  • Kait vendor yang dibatasi diperlukan untuk kasus seperti kait penjadwal yang mana fungsi terlampir harus dipanggil meskipun CPU sedang offline atau memerlukan konteks nonatomik. Kait vendor yang dibatasi tidak dapat dilepas, sehingga modul yang terpasang pada kait yang dibatasi tidak akan pernah dapat dibongkar. Nama hook vendor yang dibatasi diawali dengan android_rvh .

Untuk menambahkan vendor hook, laporkan masalah ke TI dan kirimkan patch (seperti semua patch khusus Android, masalah harus ada dan Anda harus memberikan alasan). Dukungan untuk vendor hooks hanya ada di ACK, jadi jangan kirimkan patch ini ke Linux upstream.

Tambahkan bidang 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 bernilai tambah, tambahkan kolom ke struktur seperti yang ditunjukkan dalam contoh kode berikut.

Untuk menghindari potensi konflik antara kolom yang dibutuhkan oleh vendor dan kolom yang dibutuhkan oleh OEM, OEM tidak boleh menggunakan kolom yang dideklarasikan menggunakan makro ANDROID_VENDOR_DATA() . Sebaliknya, 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 */
}

Tentukan kaitan vendor

Tambahkan vendor hooks 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 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 sesuatu yang terpasang. Namun, jika modul vendor mendaftarkan handler menggunakan register_trace_android_vh_sched_exit() , fungsi terdaftar akan dipanggil. Pawang harus mengetahui konteks terkait kunci yang dipegang, status RCS, dan faktor lainnya. Hook harus didefinisikan dalam file header di 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 vendor hook, tambahkan file header dengan deklarasi hook ke drivers/android/vendor_hooks.c dan ekspor simbolnya. Misalnya, kode berikut melengkapi 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 didefinisikan sepenuhnya untuk menjamin stabilitas ABI. Kalau tidak, tidak aman untuk melakukan dereferensi pointer buram atau menggunakan struct dalam konteks berukuran. Penyertaan yang memberikan definisi lengkap struktur data tersebut harus dimasukkan ke dalam bagian #ifndef __GENKSYMS__ di drivers/android/vendor_hooks.c . File header di include/trace/hooks tidak boleh menyertakan file header kernel dengan definisi tipe untuk menghindari perubahan CRC yang merusak KMI. Alih-alih, nyatakan tipenya.

Lampirkan ke kait vendor

Untuk menggunakan vendor hooks, modul vendor perlu mendaftarkan handler untuk hook tersebut (biasanya dilakukan selama inisialisasi modul). Misalnya, kode berikut menunjukkan modul pengendali foo.ko 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);
    ...
}

Fitur inti kernel

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

Antarmuka pemrograman aplikasi pengguna (UAPI)

  • File tajuk UAPI. Perubahan pada file header UAPI harus terjadi di bagian upstream kecuali perubahan tersebut terjadi pada antarmuka khusus Android. Gunakan file header khusus vendor untuk menentukan antarmuka antara modul vendor dan kode ruang pengguna vendor.
  • node sysfs. Jangan menambahkan node sysfs baru ke kernel GKI (penambahan tersebut hanya valid di modul vendor). simpul sysfs yang digunakan oleh pustaka SoC dan perangkat-agnostik serta kode Java yang mencakup kerangka kerja Android hanya dapat diubah dengan cara yang kompatibel dan harus diubah di bagian hulu jika simpul tersebut bukan simpul sysfs khusus Android. Anda dapat membuat node sysfs khusus vendor untuk digunakan oleh ruang pengguna vendor. Secara default, akses ke node sysfs berdasarkan ruang pengguna ditolak menggunakan SELinux. Vendor berhak menambahkan label SELinux yang sesuai untuk memungkinkan akses oleh perangkat lunak vendor resmi.
  • Node DebugFS. Modul vendor dapat menentukan node dalam debugfs hanya untuk proses debug (karena debugfs tidak dipasang selama pengoperasian normal perangkat).