Tworzenie kodu jądra dla GKI

Ogólny obraz jądra (GKI) ogranicza fragmentację jądra dzięki ścisłemu dopasowaniu używając jądra systemu Linux. Istnieją jednak ważne powody, dla których niektóre poprawki nie mogą być akceptowane w górę, a także harmonogramy produktów, które muszą być dotrzymywane. Dlatego niektóre poprawki są utrzymywane w źródłach Android Common Kernel (ACK), z których jest tworzony GKI.

Deweloperzy muszą przesyłać zmiany kodu do upstream za pomocą listy mailingowej Linux Kernel (LKML) jako pierwszego wyboru i przesyłać zmiany kodu do gałęzi ACKandroid-mainline tylko wtedy, gdy istnieje ważny powód, dla którego upstream nie jest odpowiedni. Poniżej znajdziesz przykłady akceptowanych przyczyn i sposobów ich rozwiązania.

  • Aktualizacja została przesłana do LKML, ale nie została zaakceptowana na czas, aby można było ją uwzględnić w wersji produktu. Aby obsłużyć tę poprawkę:

    • Podaj dowody na to, że poprawka została przesłana do LKML, oraz komentarze lub szacowany czas, do którego przesłanego wyżej.
    • Zdecyduj, jak wdrożyć poprawkę w ACK, uzyskać jej zatwierdzenie w upstreamie, a potem usunąć z ACK, gdy ostateczna wersja upstream zostanie scalona z ACK.
  • Poprawka definiuje EXPORT_SYMBOLS_GPL() dla modułu dostawcy, ale nie można została przesłana, ponieważ nie ma żadnych modułów w drzewie, które pochłaniały ten . Aby umożliwić obsługę tej poprawki, podaj szczegółowe informacje na temat tego, dlaczego nie można przesłane w górę i alternatywy brane pod uwagę przed jej utworzeniem użytkownika.

  • Poprawka nie jest wystarczająco ogólna dla wdrożenia z góry i nie ma czasu na dokonać ich refaktoryzacji przed wprowadzeniem produktu na rynek. Aby obsłużyć tę poprawkę, podaj szacowany czas, w jakim przesyła się poprawkę z refaktoryzowaną poprawka nie zostanie zaakceptowana w narzędziu ACK bez planu przesłania zmodyfikowanej wersji poprawkę do sprawdzenia).

  • Nie można zaakceptować poprawki przez upstream, ponieważ... <insert reason here>. Aby obsłużyć tę łatkę, skontaktuj się z zespołem odpowiedzialnym za jądro Androida i wspólnie z nami zastanów się nad możliwościami przekształcenia łatki, aby można ją było przesłać do sprawdzenia i zaakceptować w głównym źródle.

Potencjalnych uzasadnień jest znacznie więcej. Przesyłając błąd lub poprawkę, dołącz wiarygodne uzasadnienie i bądź gotowy na kilka iteracji i dyskusji. Zdajemy sobie sprawę, że ACK wprowadza pewne poprawki, zwłaszcza na początku w kolejnych fazach GKI, kiedy wszyscy uczą się, jak pracować na zewnątrz, ale nie mogą się odprężyć. harmonogramy usług. Należy spodziewać się, że wymagania dotyczące nadrzędnych ciągów znaków będą rosnąć i ciągły czas.

Wymagania dotyczące poprawek

Aktualizacje muszą być zgodne ze standardami kodowania jądra Linuxa opisanymi w źródłowym drzewie Linuxa, niezależnie od tego, czy są przesyłane do upstream czy do ACK. scripts/checkpatch.pl jest uruchamiany w ramach testów wstępnych za pomocą Gerrit. by mieć pewność, że nie jest śledzona. Aby uruchomić skrypt poprawki z tą samą konfiguracją testów przed przesłaniem, użyj //build/kernel/static_analysis:checkpatch_presubmit. Więcej informacji: build/kernel/kleaf/docs/checkpatch.md.

Poprawki potwierdzenia

Poprawki przesłane do potwierdzenia muszą być zgodne ze standardami kodowania jądra Linuksa oraz z wytycznymi dotyczącymi przesyłania treści. Musisz dodać Change-Id tag w komunikacie zatwierdzenia; jeśli prześlesz poprawkę do wielu gałęzi (na np. android-mainline i android12-5.4), musisz użyć tego samego Change-Id w przypadku wszystkich instancji poprawki.

Najpierw prześlij poprawki do LKML, aby przesłać je do weryfikacji nadrzędnej. Jeśli poprawka:

  • Zaakceptowana w górę, zostanie scalona automatycznie z android-mainline.
  • Nie akceptowane na wyższym poziomie, prześlij je na adres android-mainline, podając odniesienie do przesłanych danych lub wyjaśnienie, dlaczego nie zostały one przesłane do LKML.

Po zaakceptowaniu poprawki w źródle lub w elemencie android-mainline można ją wprowadzić przeniesiony do odpowiedniego potwierdzenia opartego na LTS (np. android12-5.4 czy android11-5.4, aby pobrać poprawki poprawiające kod Androida). Przesłanie do android-mainline umożliwia testowanie z nowymi wersjami kandydatów do publikacji w upstreamie i gwarantuje, że łatka zostanie uwzględniona w następnej wersji kandydata do publikacji na podstawie LTS. Wyjątkami są przypadki, w których poprawka z upstreamu jest przenoszona do wersji android12-5.4 (ponieważ prawdopodobnie jest już dostępna w wersji android-mainline).

Poprawki nadrzędne

Zgodnie z informacją w publikacji wytycznych, poprawki nadrzędne przeznaczone dla jąder ACK należą do następujących grup (wymienionych uporządkowane według prawdopodobieństwa zaakceptowania).

  • UPSTREAM: – poprawki wybrane z gałęzi „android-mainline” prawdopodobnie zostaną zaakceptowane w gałęzi ACK, jeśli istnieje uzasadniony przypadek użycia.
  • BACKPORT: – poprawki z upstream, które nie są dobierane selektywnie i wymagają modyfikacji, również mogą zostać zaakceptowane, jeśli istnieje uzasadnione zastosowanie.
  • FROMGIT: – plamy wybrane w ramach przygotowań z oddziału konserwacyjnego do przesłania do Linuksa mainline może zostać zaakceptowana, jeśli . Muszą być one uzasadnione zarówno pod względem treści, jak i harmonogramu.
  • FROMLIST: – poprawki, które zostały przesłane do LKML, ale nie zostały akceptowana do gałęzi konserwacyjnej, ale raczej nie zostanie zaakceptowana, chyba że uzasadnienie jest na tyle przekonujące, że poprawka została zaakceptowana. niezależnie od tego, czy zostanie otwarty w systemie Linux (zakładamy, że nie). OK musi być problemem powiązanym z FROMLIST poprawkami, aby umożliwić dyskusję z zespołem ds. jądra Androida.

Poprawki dotyczące Androida

Jeśli nie możesz przesłać wymaganych zmian na początku, spróbuj przesłać i gotowe, aby uzyskać potwierdzenie bezpośrednio. Wymagania dotyczące przesyłania poprawek spoza drzewa że tworzysz problem w dziale IT, podając poprawkę i uzasadnienie. poprawki nie można przesłać wyżej (przykłady znajdziesz na poprzedniej liście). W niektórych przypadkach nie można jednak przesłać kodu na wyższym poziomie. Te są opisane poniżej i muszą być zgodne z wytycznych w przypadku poprawek aplikacji na Androida i otaguj go prefiksem ANDROID: w tagu podmiotu danych.

Zmiany w gki_defconfig

Wszystkie zmiany (CONFIG) w elemencie gki_defconfig muszą zostać zastosowane zarówno w grupie arm64, w wersji x86, chyba że interfejs CONFIG jest związany tylko z architekturą. Aby poprosić o zmianę do ustawienia CONFIG, utwórz problem w dziale IT, aby omówić zmianę. Dowolne CONFIG zmiana, która wpływa na interfejs modułu jądra (KMI) po jego wprowadzeniu Zablokowane. W przypadku, gdy partnerzy proszą o sprzeczne ustawienia dla jednej konfiguracji, rozwiązujemy konflikty podczas dyskusji na temat powiązanych błędów.

Kod, który nie istnieje w górę łańcucha

Modyfikacji kodu, który jest już dostosowany do Androida, nie można przesłać do zespołu Google. Na przykład, mimo że sterownik separatora jest nadal utrzymywany na początku, modyfikacje do funkcji dziedziczenia priorytetowego sterownika powiązania nie można wysłać z niego do funkcji dziedziczenia bo są związane tylko z Androidem. W opisie błędu i łatki wyraźnie wyjaśnij, dlaczego kod nie może zostać wysłany do zespołu. Jeśli to możliwe, podziel poprawki na części, które można przesłać w górę łańcucha, i części dotyczące Androida, których nie można przesłać w górę łańcucha, aby zminimalizować ilość kodu spoza drzewa utrzymywanego w ACK.

Inne zmiany w tej kategorii to aktualizacje plików reprezentujących KMI, KMI listy symboli, gki_defconfig, skrypty kompilacji lub konfiguracje bądź inne skrypty nie występują w hierarchii powyżej.

Moduły zewnętrzne

Na urządzeniach z systemem Linux aktywnie zniechęca do tworzenia modułów zewnętrznych. Takie stanowisko jest rozsądne, ponieważ specjaliści od konserwacji Linuksa nie dają gwarancji dotyczące zgodności źródła w jądrze lub plików binarnych i nie chcą obsługiwać kodu których nie ma w drzewie. Jednak GKI zapewnia gwarancje ABI dla modułów dostawców, co gwarantuje stabilność interfejsów KMI w całym okresie obsługiwanym przez jądro. Dlatego istnieje klasa zmian, które obsługują moduły dostawców i są akceptowane przez ACK, ale nie przez upstream.

Rozważ na przykład poprawkę dodającą makra EXPORT_SYMBOL_GPL(), w których modułów, które korzystają z eksportu, nie ma w drzewie źródłowym. Musisz przesłać prośbę do upstream o utworzenie modułu EXPORT_SYMBOL_GPL(), który używa nowo wyeksportowanego symbolu. Jeśli jednak istnieje uzasadniona przyczyna, dla której moduł nie może zostać przesłany do upstream, możesz zamiast tego przesłać poprawkę do ACK. Musisz uzasadnić, dlaczego moduł nie może być przesyłany w górę. (Nie żądaj wersji bez GPL, EXPORT_SYMBOL()).

Ukryte konfiguracje

Niektóre moduły w drzewie automatycznie wybierają ukryte konfiguracje, których nie można określić w sekcji gki_defconfig. Na przykład CONFIG_SND_SOC_TOPOLOGY jest wybierana automatycznie, gdy CONFIG_SND_SOC_SOF=y jest skonfigurowana. Aby umożliwić tworzenie modułów poza drzewem, GKI zawiera mechanizm umożliwiający stosowanie ukrytych konfiguracji.

Aby włączyć ukrytą konfigurację, dodaj instrukcję select w pliku init/Kconfig.gki, aby była automatycznie wybierana na podstawie konfiguracji jądra CONFIG_GKI_HACKS_TO_FIX, która jest włączona w pliku gki_defconfig. Używaj tego mechanizmu tylko w przypadku ukrytych konfiguracji. Jeśli konfiguracja nie jest ukryta, musi być określona w gki_defconfig wprost lub jako zależność.

Ładowane ograniczenia

W przypadku frameworków jądra (takich jak cpufreq), które obsługują ładowanie regulatorów, można zastąpić domyślny regulator (taki jak schedutil schedutil). Dla: konstrukcje (takie jak struktura termiczna), które nie obsługują ładowalnych gubernatorów; lub sterowników, ale nadal wymagają implementacji konkretnego dostawcy, stwarza problem w IT i skonsultować się z zespołem ds. jądra Androida.

Skontaktujemy się z Tobą i zespołami ds. obsługi klienta, aby zapewnić niezbędne wsparcie.

Punkty zaczepienia dostawcy

We wcześniejszych wersjach można było dodawać modyfikacje specyficzne dla dostawcy bezpośrednio do i jednego jądra systemu operacyjnego. Nie jest to możliwe w przypadku GKI 2.0, ponieważ kod związany z konkretnym produktem musi być implementowany w modułach i nie będzie akceptowany w jądrowych jądrach upstream ani w ACK. Aby umożliwić korzystanie z funkcji o dodanej wartości, na których polegają partnerzy, przy minimalnym wpływie na kod jądra, interfejs GKI akceptuje elementy wywołujące dostawców, które umożliwiają wywoływanie modułów z kodu jądra. Dodatkowo kluczowe struktury danych mogą być wypełniane polami danych dostawcy, które są dostępne do przechowywania danych dotyczących dostawcy, aby można było stosować te funkcje.

Punkty zaczepienia dostawców występują w 2 wariantach (normalnej i z ograniczeniami) opartej na: punkty śledzenia (nie zdarzenia śledzenia), do których moduły dostawcy mogą dołączać. Przykład: zamiast dodawać nową funkcję sched_exit() do księgowania zadania wyjścia, dostawcy mogą dodać w projekcie do_exit() element zaczepienia, do którego moduł dostawcy może dołączyć do przetworzenia. Przykładowa implementacja zawiera te elementy:

  • Standardowe elementy wywoławcze dostawcy używają funkcji DECLARE_HOOK() do tworzenia punktu śledzenia o nazwie trace_name, gdzie name to niepowtarzalny identyfikator ścieżki. Zgodnie z tradycją normalne nazwy punktów zaczepienia dostawców zaczynają się od android_vh, więc nazwa haka sched_exit() będzie miała postać android_vh_sched_exit.
  • Ograniczone elementy zaczepienia dostawcy są potrzebne w przypadkach takich jak punkty zaczepienia algorytmu szeregowania, dołączona funkcja musi być wywołana nawet wtedy, gdy procesor jest offline lub wymaga w kontekście nieatomowym. Elementy rozszerzeń dostawców z ograniczonym dostępem nie mogą być odłączane, więc moduły, które się do nich przyłączają, nigdy nie mogą zostać usunięte. Z ograniczonym dostępem nazwy punktów zaczepienia dostawcy zaczynają się od android_rvh.

Aby dodać punkt zaczepienia dostawcy, zgłoś problem do działu IT i prześlij poprawki (jak w przypadku wszystkich poprawki aplikacji dla Androida muszą istnieć jakieś problemy i należy je udostępnić, uzasadnienie). Obsługa haka dostawcy jest dostępna tylko w ACK, więc nie wysyłaj tych poprawek do upstream Linux.

Dodaj pola dostawców do struktur

Dane dostawcy możesz powiązać z kluczowymi strukturami danych, dodając android_vendor_data pól używa makr ANDROID_VENDOR_DATA(). Na przykład, aby obsługiwać funkcje o wartości dodanej, dołącz pola do struktury, jak pokazano w tym przykładowym kodzie.

Aby uniknąć potencjalnych konfliktów między polami wymaganymi przez dostawców i polami wymaganych przez producentów OEM, OEM nie może nigdy używać pól zadeklarowanych za pomocą Makra ANDROID_VENDOR_DATA(). Zamiast tego dostawcy OEM muszą używać ANDROID_OEM_DATA() zadeklarowanie android_oem_data pól.

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

Zdefiniuj punkty zaczepienia dostawcy

Dodaj punkty zaczepienia dostawcy do kodu jądra jako punkty śledzenia, zadeklarując je za pomocą DECLARE_HOOK() lub DECLARE_RESTRICTED_HOOK(), a następnie dodaj je do kodu jako za pomocą punktów śledzenia. Jeśli na przykład chcesz dodać funkcję trace_android_vh_sched_exit() do istniejącej funkcji jądra do_exit():

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

Funkcja trace_android_vh_sched_exit() sprawdza najpierw, czy coś jest dołączone. Jeśli jednak moduł dostawcy zarejestruje moduł obsługi za pomocą funkcji register_trace_android_vh_sched_exit(), zostanie wywołana zarejestrowana funkcja. Obsługujący musi być świadomy kontekstu w odniesieniu do zablokowanych blokad, stanu RCS i innych czynników. Hak musi być zdefiniowany w pliku nagłówka w katalogu include/trace/hooks.

Na przykład ten kod zawiera możliwą deklarację dla trace_android_vh_sched_exit() w pliku 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>

Aby utworzyć instancje interfejsów wymagane przez punkt zaczepienia dostawcy, dodaj plik nagłówka z deklaracją webhooka do drivers/android/vendor_hooks.c i wyeksportuj symboli. Na przykład ten kod wypełnia deklarację Haczyk 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);

UWAGA: struktury danych używane w deklaracji webhooka muszą być w pełni zdefiniowane w celu zagwarantowania stabilności ABI. W przeciwnym razie nie jest bezpieczne usuwać nieprzezroczyste wskaźniki lub używać struktury w kontekstach o określonych rozmiarach. Zawierają który zawiera pełną definicję takich struktur danych, należy umieścić w Sekcja #ifndef __GENKSYMS__ z drivers/android/vendor_hooks.c. W nagłówku w include/trace/hooks nie powinny zawierać pliku nagłówka jądra z parametrem definicji typów, aby uniknąć zmian CRC, które zakłócają KMI. Zamiast tego przekazać dalej zadeklarować typy.

Dołącz do punktów zaczepienia dostawcy

Aby można było używać punktów zaczepienia dostawcy, moduł musi zarejestrować moduł obsługi tego punktu (zwykle odbywa się to podczas inicjowania modułu). Na przykład ten kod pokazuje moduł foo.ko modułu 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);
    ...
}

Używaj punktów zaczepienia dostawców z plików nagłówka

Aby używać punktów zaczepienia dostawcy w plikach nagłówka, może być konieczna aktualizacja punktu zaczepienia dostawcy pliku nagłówkowego, aby cofnąć definicję TRACE_INCLUDE_PATH i uniknąć błędów kompilacji wskazujących, nie udało się znaleźć pliku nagłówka punktu śledzenia. Na przykład

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.

Aby naprawić ten typ błędu kompilacji, zastosuj odpowiednią poprawkę do pliku nagłówka dostawcy, który uwzględniasz. Więcej informacji znajdziesz na stronie 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

Definiowanie UNDEF_TRACE_INCLUDE_PATH powoduje, że include/trace/define_trace.h odwołuje definicję TRACE_INCLUDE_PATH po utworzeniu punktów śledzenia.

Podstawowe funkcje jądra

Jeśli żadna z poprzednich metod nie umożliwia implementacji funkcji z modułu, musisz dodać tę funkcję jako modyfikację specyficzną dla Androida do jądra. Aby rozpocząć rozmowę, utwórz problem w narzędziu Issue Tracker (IT).

Interfejs programowania aplikacji użytkownika (UAPI)

  • Pliki nagłówka UAPI Zmiany w Pliki nagłówków UAPI musi następować po przesłaniu, o ile zmiany nie dotyczą interfejsów wyłącznie Androida. Użyj plików nagłówków specyficznych dla dostawcy, aby zdefiniować interfejsy między modułami dostawcy a jego kodem użytkownika.
  • węzłów sysfs. Nie dodawaj nowych węzłów sysfs do jądra GKI (takie dodatki są ważne tylko w modułach dostawców). Węzły sysfs używane przez biblioteki niezależne od SoC i urządzenia oraz kod Java, które stanowią podstawę platformy Android, można zmieniać tylko w zgodny sposób. Jeśli nie są to węzły sysfs specyficzne dla Androida, muszą być zmieniane w górę. Możesz tworzyć węzłów sysfs właściwych dla danego dostawcy, które mają być używane przez przestrzeń użytkownika dostawcy. Domyślnie dostęp do węzłów sysfs z przestrzeni użytkownika jest zabroniony przy użyciu SELinux. Dostawca musi dodać odpowiednie etykiety SELinux, aby umożliwić dostęp autoryzowanemu oprogramowaniu dostawcy.
  • Węzły DebugFS. Moduły dostawców mogą definiować węzły w debugfs tylko do debugowania (ponieważ debugfs nie jest montowany podczas normalnej pracy urządzenia).