Optimasi Waktu Booting

Halaman ini menyediakan serangkaian tip, yang dapat Anda pilih, untuk meningkatkan waktu booting.

Hapus simbol debug dari modul

Mirip dengan bagaimana simbol debug dihapus dari kernel pada perangkat produksi, pastikan Anda juga menghapus simbol debug dari modul. Menghapus simbol debug dari modul membantu waktu booting dengan mengurangi hal berikut:

  • Waktu yang diperlukan untuk membaca binari dari flash.
  • Waktu yang diperlukan untuk mendekompresi ramdisk.
  • Waktu yang diperlukan untuk memuat modul.

Menghapus simbol debug dari modul dapat menghemat beberapa detik saat boot.

Pengupasan simbol diaktifkan secara default di versi platform Android, tetapi untuk mengaktifkannya secara eksplisit, setel BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES dalam konfigurasi khusus perangkat Anda di bawah perangkat/ vendor / device .

Gunakan kompresi LZ4 untuk kernel dan ramdisk

Gzip menghasilkan output terkompresi yang lebih kecil dibandingkan dengan LZ4, namun LZ4 melakukan dekompresi lebih cepat dibandingkan Gzip. Untuk kernel dan modul, pengurangan ukuran penyimpanan absolut dari penggunaan Gzip tidak terlalu signifikan dibandingkan dengan manfaat waktu dekompresi LZ4.

Dukungan untuk kompresi ramdisk LZ4 telah ditambahkan ke platform Android yang dibangun melalui BOARD_RAMDISK_USE_LZ4 . Anda dapat mengatur opsi ini di konfigurasi khusus perangkat Anda. Kompresi kernel dapat diatur melalui defconfig kernel.

Beralih ke LZ4 akan memberikan waktu booting 500 ms hingga 1000 ms lebih cepat.

Hindari login driver Anda secara berlebihan

Di ARM64 dan ARM32, pemanggilan fungsi yang lebih dari jarak tertentu dari situs panggilan memerlukan tabel lompat (disebut tabel penautan prosedur, atau PLT) agar dapat menyandikan alamat lompat lengkap. Karena modul dimuat secara dinamis, tabel lompat ini perlu diperbaiki selama pemuatan modul. Panggilan yang memerlukan relokasi disebut entri relokasi dengan entri tambahan eksplisit (atau disingkat RELA) dalam format ELF.

Kernel Linux melakukan beberapa optimasi ukuran memori (seperti optimasi cache hit) ketika mengalokasikan PLT. Dengan komit hulu ini, skema pengoptimalan memiliki kompleksitas O(N^2), dengan N adalah jumlah RELA bertipe R_AARCH64_JUMP26 atau R_AARCH64_CALL26 . Jadi, memiliki lebih sedikit RELA jenis ini akan membantu mengurangi waktu buka modul.

Salah satu pola pengkodean umum yang meningkatkan jumlah RELA R_AARCH64_CALL26 atau R_AARCH64_JUMP26 adalah proses login driver yang berlebihan. Setiap panggilan ke printk() atau skema logging lainnya biasanya menambahkan entri CALL26 / JUMP26 RELA. Dalam teks penerapan di penerapan hulu , perhatikan bahwa bahkan dengan pengoptimalan, enam modul membutuhkan waktu sekitar 250 md untuk dimuat—hal ini karena keenam modul tersebut adalah enam modul teratas dengan jumlah logging terbanyak.

Mengurangi logging dapat menghemat sekitar 100 - 300ms pada waktu boot tergantung pada seberapa berlebihan logging yang ada.

Aktifkan pemeriksaan asinkron, secara selektif

Ketika sebuah modul dimuat, jika perangkat yang didukungnya telah diisi dari DT (devicetree) dan ditambahkan ke inti driver, maka pemeriksaan perangkat dilakukan dalam konteks panggilan module_init() . Ketika pemeriksaan perangkat dilakukan dalam konteks module_init() , modul tidak dapat menyelesaikan pemuatan hingga pemeriksaan selesai. Karena pemuatan modul sebagian besar bersifat serial, perangkat yang membutuhkan waktu relatif lama untuk menyelidiki akan memperlambat waktu booting.

Untuk menghindari waktu booting yang lebih lambat, aktifkan pemeriksaan asinkron untuk modul yang memerlukan waktu beberapa saat untuk memeriksa perangkatnya. Mengaktifkan pemeriksaan asinkron untuk semua modul mungkin tidak bermanfaat karena waktu yang diperlukan untuk membuat thread dan memulai probe mungkin sama lamanya dengan waktu yang diperlukan untuk menyelidiki perangkat.

Perangkat yang terhubung melalui bus lambat seperti I2C, perangkat yang melakukan pemuatan firmware dalam fungsi probenya, dan perangkat yang melakukan banyak inisialisasi perangkat keras dapat menyebabkan masalah pengaturan waktu. Cara terbaik untuk mengidentifikasi kapan hal ini terjadi adalah dengan mengumpulkan waktu pemeriksaan untuk setiap pengemudi dan mengurutkannya.

Untuk mengaktifkan pemeriksaan asinkron pada sebuah modul, tidak cukup hanya menyetel tanda PROBE_PREFER_ASYNCHRONOUS di kode driver. Untuk modul, Anda juga perlu menambahkan module_name .async_probe=1 di baris perintah kernel atau meneruskan async_probe=1 sebagai parameter modul saat memuat modul menggunakan modprobe atau insmod .

Mengaktifkan pemeriksaan asinkron dapat menghemat sekitar 100 - 500 ms pada waktu booting tergantung pada perangkat keras/driver Anda.

Selidiki driver CPUfreq Anda sedini mungkin

Semakin awal driver CPUfreq Anda memeriksa, semakin cepat Anda dapat menskalakan frekuensi CPU ke maksimum (atau maksimum yang dibatasi secara termal) selama boot. Semakin cepat CPU, semakin cepat pula bootingnya. Pedoman ini juga berlaku untuk driver devfreq yang mengontrol DRAM, memori, dan frekuensi interkoneksi.

Dengan modul, urutan beban dapat bergantung pada tingkat initcall dan urutan kompilasi atau tautan driver. Gunakan alias MODULE_SOFTDEP() untuk memastikan driver cpufreq termasuk di antara beberapa modul pertama yang dimuat.

Selain memuat modul lebih awal, Anda juga perlu memastikan semua dependensi untuk menyelidiki driver CPUfreq juga telah diperiksa. Misalnya, jika Anda memerlukan jam atau pegangan pengatur untuk mengontrol frekuensi CPU Anda, pastikan keduanya diperiksa terlebih dahulu. Atau Anda mungkin memerlukan driver termal untuk dimuat sebelum driver CPUfreq jika CPU Anda mungkin menjadi terlalu panas saat boot. Jadi, lakukan apa yang Anda bisa untuk memastikan CPUfreq dan driver devfreq yang relevan menyelidikinya sedini mungkin.

Penghematan dari pemeriksaan driver CPUfreq Anda lebih awal bisa sangat kecil hingga sangat besar tergantung pada seberapa awal Anda dapat memeriksanya dan pada frekuensi berapa bootloader meninggalkan CPU.

Pindahkan modul ke partisi init, vendor, atau vendor_dlkm tahap kedua

Karena proses init tahap pertama bersifat serial, tidak banyak peluang untuk memparalelkan proses booting. Jika modul tidak diperlukan untuk menyelesaikan init tahap pertama, pindahkan modul ke init tahap kedua dengan menempatkannya di partisi vendor atau vendor_dlkm .

Init tahap pertama tidak memerlukan pemeriksaan beberapa perangkat untuk mencapai init tahap kedua. Hanya fungsi konsol dan penyimpanan flash yang diperlukan untuk aliran boot normal.

Muat driver penting berikut:

  • penjaga
  • mengatur ulang
  • cpufreq

Untuk mode fastbootd pemulihan dan ruang pengguna, init tahap pertama memerlukan lebih banyak perangkat untuk diselidiki (seperti USB), dan ditampilkan. Simpan salinan modul ini di ramdisk tahap pertama dan di partisi vendor atau vendor_dlkm . Hal ini memungkinkannya untuk dimuat di init tahap pertama untuk pemulihan atau aliran boot fastbootd . Namun, jangan memuat modul mode pemulihan di init tahap pertama selama aliran boot normal. Modul mode pemulihan dapat ditunda ke init tahap kedua untuk mengurangi waktu boot. Semua modul lain yang tidak diperlukan pada tahap pertama init harus dipindahkan ke partisi vendor atau vendor_dlkm .

Dengan adanya daftar perangkat daun (misalnya, UFS atau serial), skrip dev needs.sh menemukan semua driver, perangkat, dan modul yang diperlukan untuk dependensi atau pemasok (misalnya, jam, regulator, atau gpio ) untuk diselidiki.

Memindahkan modul ke tahap kedua init mengurangi waktu booting dengan cara berikut:

  • Pengurangan ukuran ramdisk.
    • Ini menghasilkan pembacaan flash yang lebih cepat ketika bootloader memuat ramdisk (langkah boot serial).
    • Ini menghasilkan kecepatan dekompresi yang lebih cepat ketika kernel mendekompresi ramdisk (langkah boot serial).
  • Init tahap kedua bekerja secara paralel, yang menyembunyikan waktu pemuatan modul dengan pekerjaan yang dilakukan di init tahap kedua.

Memindahkan modul ke tahap kedua dapat menghemat 500 - 1000 ms pada waktu booting tergantung pada berapa banyak modul yang dapat Anda pindahkan ke init tahap kedua.

Logistik pemuatan modul

Versi Android terbaru menampilkan konfigurasi papan yang mengontrol modul mana yang disalin ke setiap tahap, dan modul mana yang dimuat. Bagian ini berfokus pada subset berikut:

  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES . Daftar modul yang akan disalin ke ramdisk.
  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD . Daftar modul yang akan dimuat di tahap pertama init.
  • BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD . Daftar modul yang akan dimuat ketika recovery atau fastbootd dipilih dari ramdisk.
  • BOARD_VENDOR_KERNEL_MODULES . Daftar modul ini akan disalin ke partisi vendor atau vendor_dlkm di direktori /vendor/lib/modules/ .
  • BOARD_VENDOR_KERNEL_MODULES_LOAD . Daftar modul yang akan dimuat di init tahap kedua.

Modul boot dan pemulihan di ramdisk juga harus disalin ke partisi vendor atau vendor_dlkm di /vendor/lib/modules . Menyalin modul-modul ini ke partisi vendor memastikan modul tidak terlihat selama init tahap kedua, yang berguna untuk debugging dan mengumpulkan modinfo untuk laporan bug.

Duplikasi harus memakan ruang minimal pada partisi vendor atau vendor_dlkm selama set modul boot diminimalkan. Pastikan file modules.list vendor memiliki daftar modul yang difilter di /vendor/lib/modules . Daftar yang difilter memastikan waktu boot tidak terpengaruh oleh pemuatan modul lagi (yang merupakan proses yang mahal).

Pastikan modul mode pemulihan dimuat sebagai grup. Memuat modul mode pemulihan dapat dilakukan dalam mode pemulihan, atau pada awal init tahap kedua di setiap aliran boot.

Anda dapat menggunakan file Board.Config.mk perangkat untuk melakukan tindakan ini seperti yang terlihat dalam contoh berikut:

# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)

# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))

# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
     $(filter $(BOOT_KERNEL_MODULES_FILTER) \
                $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
#     $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
        $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
            $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
        $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
    $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
    $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
    $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))

Contoh ini menampilkan subset BOOT_KERNEL_MODULES dan RECOVERY_KERNEL_MODULES yang lebih mudah dikelola untuk ditentukan secara lokal di file konfigurasi board. Skrip sebelumnya menemukan dan mengisi masing-masing subset modul dari modul kernel terpilih yang tersedia, meninggalkan modul reaming untuk init tahap kedua.

Untuk init tahap kedua, kami menyarankan untuk menjalankan pemuatan modul sebagai layanan sehingga tidak memblokir aliran boot. Gunakan skrip shell untuk mengelola pemuatan modul sehingga logistik lainnya, seperti penanganan dan mitigasi kesalahan, atau penyelesaian pemuatan modul, dapat dilaporkan kembali (atau diabaikan) jika perlu.

Anda dapat mengabaikan kegagalan pemuatan modul debug yang tidak terjadi pada build pengguna. Untuk mengabaikan kegagalan ini, atur properti vendor.device.modules.ready untuk memicu tahapan selanjutnya dari bootflow skrip init rc untuk melanjutkan ke layar peluncuran. Referensikan contoh skrip berikut, jika Anda memiliki kode berikut di /vendor/etc/init.insmod.sh :

#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
  cfg_file=$1
else
  # Set property even if there is no insmod config
  # to unblock early-boot trigger
  setprop vendor.common.modules.ready
  setprop vendor.device.modules.ready
  exit 1
fi

if [ -f $cfg_file ]; then
  while IFS="|" read -r action arg
  do
    case $action in
      "insmod") insmod $arg ;;
      "setprop") setprop $arg 1 ;;
      "enable") echo 1 > $arg ;;
      "modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
     . . .
    esac
  done < $cfg_file
fi

Dalam file rc perangkat keras, layanan one shot dapat ditentukan dengan:

service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
    class main
    user root
    group root system
    Disabled
    oneshot

Pengoptimalan tambahan dapat dilakukan setelah modul berpindah dari tahap pertama ke tahap kedua. Anda dapat menggunakan fitur daftar blokir modprobe untuk membagi aliran boot tahap kedua untuk menyertakan pemuatan modul yang ditangguhkan dari modul yang tidak penting. Pemuatan modul yang digunakan secara eksklusif oleh HAL tertentu dapat ditunda untuk memuat modul hanya ketika HAL dimulai.

Untuk meningkatkan waktu booting yang terlihat, Anda dapat secara khusus memilih modul di layanan pemuatan modul yang lebih kondusif untuk memuat setelah layar peluncuran. Misalnya, Anda dapat secara eksplisit terlambat memuat modul untuk dekoder video atau wifi setelah aliran boot init dihapus ( sinyal properti Android sys.boot_complete , misalnya). Pastikan HAL untuk modul yang memuat lambat terblokir cukup lama ketika driver kernel tidak ada.

Alternatifnya, Anda dapat menggunakan perintah wait<file>[<timeout>] init dalam skrip rc aliran boot untuk menunggu entri sysfs yang dipilih untuk menunjukkan bahwa modul driver telah menyelesaikan operasi pemeriksaan. Contohnya adalah menunggu driver tampilan selesai memuat di latar belakang pemulihan atau fastbootd , sebelum menampilkan grafik menu.

Inisialisasi frekuensi CPU ke nilai yang wajar di bootloader

Tidak semua SoC/produk dapat melakukan booting CPU pada frekuensi tertinggi karena masalah termal atau daya selama pengujian boot loop. Namun, pastikan bootloader menyetel frekuensi semua CPU online setinggi mungkin yang aman untuk SoC/produk. Hal ini sangat penting karena, dengan kernel yang sepenuhnya modular, dekompresi ramdisk init dilakukan sebelum driver CPUfreq dapat dimuat. Jadi, jika CPU dibiarkan pada frekuensi paling rendah oleh bootloader, waktu dekompresi ramdisk bisa memakan waktu lebih lama dibandingkan kernel yang dikompilasi secara statis (setelah disesuaikan dengan perbedaan ukuran ramdisk) karena frekuensi CPU akan sangat rendah ketika melakukan CPU intensif kerja (dekompresi). Hal yang sama berlaku untuk frekuensi memori/interkoneksi.

Inisialisasi frekuensi CPU dari CPU besar di bootloader

Sebelum driver CPUfreq dimuat, kernel tidak mengetahui frekuensi CPU yang kecil dan besar dan tidak menskalakan kapasitas terjadwal CPU untuk frekuensinya saat ini. Kernel mungkin memigrasikan thread ke CPU besar jika beban pada CPU kecil cukup tinggi.

Pastikan CPU besar setidaknya memiliki performa yang sama dengan CPU kecil untuk frekuensi yang dibiarkan oleh bootloader. Misalnya, jika CPU besar memiliki performa 2x lebih baik dari CPU kecil untuk frekuensi yang sama, namun bootloader menyetel frekuensi CPU kecil menjadi 1,5 GHz dan frekuensi CPU besar menjadi 300 MHz, maka kinerja booting akan turun jika kernel memindahkan thread ke CPU besar. Dalam contoh ini, jika aman untuk melakukan booting pada CPU besar pada 750 MHz, Anda harus melakukannya meskipun Anda tidak berencana untuk menggunakannya secara eksplisit.

Driver tidak boleh memuat firmware pada init tahap pertama

Mungkin ada beberapa kasus yang tidak dapat dihindari di mana firmware perlu dimuat pada init tahap pertama. Namun secara umum, driver tidak boleh memuat firmware apa pun di init tahap pertama, terutama dalam konteks pemeriksaan perangkat. Memuat firmware di init tahap pertama menyebabkan seluruh proses boot terhenti jika firmware tidak tersedia di ramdisk tahap pertama. Dan meskipun firmware sudah ada di ramdisk tahap pertama, tetap saja menyebabkan penundaan yang tidak perlu.