Kembangkan Kode Kernel untuk GKI

Generic Kernel Image (GKI) mengurangi fragmentasi kernel dengan menyelaraskan erat dengan kernel Linux upstream. Namun, ada alasan yang sah 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 dibangun.

Pengembang harus mengirimkan perubahan kode 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 dapat dijalankan. Contoh alasan yang valid dan cara mengatasinya adalah sebagai berikut.

  • Patch dikirimkan ke LKML, tetapi tidak diterima pada waktunya untuk rilis produk. Untuk menangani tambalan ini:

    • Berikan bukti bahwa tambalan telah dikirimkan ke LKML dan komentar yang diterima untuk tambalan, atau perkiraan waktu tambalan akan dikirimkan ke hulu.
    • Putuskan tindakan untuk mendaratkan tambalan di ACK, dapatkan persetujuan di hulu, lalu keluarkan dari ACK saat versi hulu final digabungkan ke dalam ACK.
  • Patch mendefinisikan EXPORT_SYMBOLS_GPL() untuk modul vendor, tetapi tidak dapat dikirimkan ke hulu karena tidak ada modul in-tree yang menggunakan simbol itu. Untuk menangani patch ini, berikan detail tentang mengapa modul Anda tidak dapat dikirimkan ke upstream dan alternatif yang Anda pertimbangkan sebelum membuat permintaan ini.

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

  • Patch tidak dapat diterima oleh upstream karena... <insert reason here> . Untuk menangani tambalan ini, hubungi tim kernel Android dan bekerja sama dengan kami tentang opsi untuk memfaktorkan ulang tambalan sehingga dapat dikirim untuk ditinjau dan diterima di hulu.

Ada banyak lagi pembenaran potensial. Saat Anda mengirimkan bug atau tambalan Anda, sertakan pembenaran yang valid dan harapkan beberapa iterasi dan diskusi. Kami menyadari bahwa ACK akan membawa beberapa tambalan, terutama pada fase awal GKI sementara semua orang belajar bagaimana bekerja di hulu tetapi tidak dapat mengendurkan jadwal produk untuk melakukannya. Harapkan persyaratan upstreaming menjadi lebih ketat dari waktu ke waktu.

Persyaratan tambalan

Tambalan harus sesuai dengan standar pengkodean kernel Linux yang dijelaskan di pohon sumber Linux , baik dikirim ke hulu atau ke ACK. scripts/checkpatch.pl dijalankan sebagai bagian dari pengujian pra-pengiriman Gerrit, jadi jalankan terlebih dahulu untuk memastikannya lolos. Untuk menjalankan skrip checkpatch dengan konfigurasi yang sama dengan pengujian prapengiriman, gunakan build/static_analysis/checkpatch_presubmit.sh dari checkout repo .

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 komit; 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 tinjauan upstream. Jika patchnya adalah:

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

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

Patch hulu

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

  • UPSTREAM: - Tambalan yang diambil dari 'android-mainline` kemungkinan besar akan diterima ke ACK jika ada kasus penggunaan yang masuk akal.
  • BACKPORT: - Tambalan dari hulu yang tidak akurat dan perlu dimodifikasi juga kemungkinan akan diterima jika ada kasus penggunaan yang masuk akal.
  • FROMGIT: - Patch yang diambil dari cabang pengelola sebagai persiapan untuk dikirim ke jalur utama Linux mungkin diterima jika ada tenggat waktu yang akan datang. Ini harus dibenarkan baik untuk konten dan jadwal.
  • FROMLIST: - Patch yang telah dikirimkan ke LKML tetapi belum diterima di cabang pengelola, kemungkinan besar tidak akan diterima, kecuali jika pembenarannya cukup meyakinkan bahwa patch tersebut akan diterima terlepas dari apakah patch tersebut mendarat di Linux hulu atau tidak (kami berasumsi bahwa itu tidak akan). Pasti ada masalah yang terkait dengan tambalan FROMLIST untuk memfasilitasi diskusi dengan tim kernel Android.

Patch khusus Android

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

Perubahan pada gki_defconfig

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

Kode yang tidak ada di hulu

Modifikasi kode yang sudah khusus Android tidak dapat dikirim ke hulu. Misalnya, meskipun driver pengikat dipertahankan di hulu, modifikasi fitur pewarisan prioritas driver pengikat tidak dapat dikirim ke hulu karena khusus Android. Jelaskan secara eksplisit dalam bug Anda dan tambal mengapa kode tidak dapat dikirim ke hulu. Jika memungkinkan, bagi tambalan menjadi beberapa bagian yang dapat dikirimkan ke hulu dan bagian khusus Android yang tidak dapat dikirim ke hulu untuk meminimalkan jumlah kode luar biasa yang dipertahankan di ACK.

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

Modul di luar pohon

Upstream Linux secara aktif tidak mendukung pembuatan modul out-of-tree. Ini adalah posisi yang masuk akal mengingat pengelola Linux tidak membuat jaminan tentang sumber dalam kernel atau kompatibilitas biner dan tidak ingin mendukung kode yang tidak ada di pohon. Namun , GKI membuat jaminan ABI untuk modul vendor, memastikan bahwa antarmuka KMI stabil untuk masa pakai 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 tambalan 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. (Jangan meminta varian non-GPL, EXPORT_SYMBOL() .)

Konfigurasi tersembunyi

Beberapa modul in-tree 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 out-of-tree, 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, itu 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 tetapi masih memerlukan implementasi khusus vendor, buat masalah di TI, dan konsultasikan dengan tim kernel Android .

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

Kait vendor

Dalam rilis sebelumnya, Anda dapat menambahkan modifikasi khusus vendor langsung ke kernel inti. Ini tidak mungkin dilakukan dengan GKI 2.0 karena kode khusus produk harus diimplementasikan dalam modul dan tidak akan diterima di hulu kernel inti atau di ACK. Untuk mengaktifkan fitur nilai tambah yang diandalkan mitra dengan dampak minimal pada kode kernel inti, GKI menerima kait vendor yang memungkinkan modul dipanggil dari kode kernel inti. Selain itu, struktur data utama dapat diisi dengan bidang data vendor yang tersedia untuk menyimpan data khusus vendor untuk mengimplementasikan fitur ini.

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

  • Kait vendor normal menggunakan DECLARE_HOOK() untuk membuat fungsi tracepoint dengan nama trace_ name di mana name adalah pengidentifikasi unik untuk trace. Berdasarkan konvensi, nama kait vendor normal dimulai dengan android_vh , jadi nama untuk sched_exit() akan menjadi android_vh_sched_exit .
  • Kait vendor terbatas diperlukan untuk kasus seperti kait penjadwal di mana fungsi yang dilampirkan harus dipanggil bahkan jika CPU sedang offline atau memerlukan konteks nonatomik. Kait vendor terbatas tidak dapat dilepas, sehingga modul yang terpasang pada kait terbatas tidak akan pernah dapat dibongkar. Hanya satu lampiran yang diizinkan, jadi upaya lain untuk melampirkan gagal dengan -EBUSY . Nama kait vendor terbatas dimulai dengan android_rvh .

Untuk menambahkan kait vendor, ajukan masalah di TI dan kirimkan tambalan (seperti semua tambalan khusus Android, masalah harus ada dan Anda harus memberikan pembenaran). Dukungan untuk kait vendor hanya ada di ACK, jadi jangan kirim tambalan ini ke Linux hulu.

Tambahkan bidang vendor ke struktur

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

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

Tambahkan kait vendor ke kode kernel sebagai titik jejak dengan mendeklarasikannya menggunakan DECLARE_HOOK() atau DECLARE_RESTRICTED_HOOK() dan kemudian menambahkannya ke kode sebagai titik jejak. 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 jika ada sesuatu yang dilampirkan. Namun, jika modul vendor mendaftarkan handler menggunakan register_trace_android_vh_sched_exit() , fungsi terdaftar akan dipanggil. Handler harus mengetahui konteks yang berkaitan dengan kunci yang ditahan, 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/sched.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 */
#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>

CATATAN : Struktur data yang digunakan dalam deklarasi hook harus didefinisikan sepenuhnya untuk menjamin stabilitas ABI. Kalau tidak, tidak aman untuk mereferensikan pointer buram atau menggunakan struct dalam konteks berukuran. #include <linux/sched.h> dalam contoh di atas membuat definisi struct task_struct tersedia dan memungkinkan pelacakan ABI.

Untuk membuat instance antarmuka yang diperlukan untuk hook vendor, 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() .

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

Lampirkan ke kait vendor

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

Fitur inti inti

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

Antarmuka pemrograman aplikasi pengguna (UAPI)

  • File header UAPI. Perubahan pada file header UAPI harus terjadi di upstream kecuali perubahan tersebut pada antarmuka khusus Android. Gunakan file header khusus vendor untuk menentukan antarmuka antara modul vendor dan kode ruang pengguna vendor.
  • sysfs node. Jangan tambahkan node sysfs baru ke kernel GKI (penambahan tersebut hanya berlaku di modul vendor). sysfs node yang digunakan oleh SoC- dan library agnostik perangkat dan kode Java yang terdiri dari kerangka kerja Android hanya dapat diubah dengan cara yang kompatibel dan harus diubah ke hulu jika mereka 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 userspace ditolak menggunakan SELinux. Terserah vendor untuk menambahkan label SELinux yang sesuai untuk memungkinkan akses oleh perangkat lunak vendor resmi.
  • Node debugFS. Modul vendor dapat mendefinisikan node dalam debugfs hanya untuk debugging (karena debugfs tidak dipasang selama pengoperasian normal perangkat).