Разработать код ядра для GKI

Общий образ ядра (GKI) уменьшает фрагментацию ядра за счет тесного согласования с исходным ядром Linux. Однако существуют веские причины, по которым некоторые исправления не могут быть приняты в исходном коде, и существуют графики выпуска продуктов, которые необходимо соблюдать, поэтому некоторые исправления сохраняются в источниках Android Common Kernel (ACK), на основе которых построен GKI.

Разработчики должны отправлять изменения кода в исходную версию, используя список рассылки ядра Linux (LKML) в качестве первого варианта, и отправлять изменения кода в ветку ACK android-mainline только тогда, когда есть веская причина, по которой восходящая версия нежизнеспособна. Ниже приведены примеры веских причин и способов их устранения.

  • Патч был отправлен в LKML, но не был принят к выпуску продукта. Чтобы справиться с этим патчем:

    • Предоставьте доказательства того, что исправление было отправлено в LKML, и комментарии, полученные для исправления, или примерное время, к которому исправление будет отправлено в исходную версию.
    • Определите порядок действий, чтобы разместить исправление в ACK, получить его одобрение в восходящем направлении, а затем удалить его из ACK, когда окончательная версия восходящего потока будет объединена с ACK.
  • Патч определяет EXPORT_SYMBOLS_GPL() для модуля поставщика, но не может быть отправлен вверх по течению, потому что нет модулей в дереве, которые потребляют этот символ. Чтобы обработать этот патч, предоставьте подробную информацию о том, почему ваш модуль не может быть отправлен вверх по течению, а также рассматриваемые вами альтернативы, которые вы рассмотрели до этого запроса.

  • Патч недостаточно универсален для основной ветки разработки, и нет времени на его рефакторинг перед выпуском продукта. Чтобы обработать это исправление, укажите примерное время, в течение которого исправленное исправление будет отправлено в исходную версию (исправление не будет принято в ACK без плана отправки исправленного исправления в исходную версию для проверки).

  • Исправление не может быть принято вышестоящей стороной, потому что... <вставьте здесь причину> . Чтобы обработать этот патч, обратитесь к команде разработчиков ядра Android и поработайте с нами над вариантами рефакторинга патча, чтобы его можно было отправить на рассмотрение и принять в исходную версию.

Есть еще много потенциальных оправданий. Когда вы отправляете сообщение об ошибке или исправлении, включите веское обоснование и ожидайте повторения и обсуждения. Мы понимаем, что ACK содержит некоторые исправления, особенно на ранних стадиях GKI, когда все учатся работать над разработкой, но не могут для этого ослабить графики выпуска продуктов. Ожидайте, что требования к восходящему потоку со временем станут более строгими.

Требования к патчу

Патчи должны соответствовать стандартам кодирования ядра Linux, описанным в дереве исходного кода Linux , независимо от того, отправляются ли они в исходный код или на ACK. Скрипт scripts/checkpatch.pl запускается как часть предварительного тестирования Gerrit, поэтому запустите его заранее, чтобы убедиться, что он пройден. Чтобы запустить скрипт CheckPatch с той же конфигурацией, что и тестирование, используя //build/kernel/static_analysis:checkpatch_presubmit . Для получения подробной информации см. Build/Kernel/Kleaf/Docs/CheckPatch.md .

ACK-патчи

Платы, представленные в ACK, должны соответствовать стандартам кодирования ядра Linux и руководящими принципами вклада . Вы должны включить тег Change-Id в сообщении Commit; Если вы отправляете патч в несколько веток (например, android-mainline и android12-5.4 ), вы должны использовать один и тот же Change-Id для всех экземпляров патча.

Сначала отправьте исправления в LKML для первичной проверки. Если патч:

  • Принятый выше по течению, он автоматически объединяется с android-mainline .
  • Не принято в исходную версию, отправьте его в android-mainline со ссылкой на исходящую отправку или объяснением, почему оно не было отправлено в LKML.

После того, как исправление будет принято либо в восходящем направлении, либо в android-mainline , его можно перенести в соответствующий ACK на основе LTS (например, android12-5.4 и android11-5.4 для исправлений, исправляющих код, специфичный для Android). Отправка в android-mainline позволяет тестирование с новыми кандидатами на выпуск и гарантирует, что патч находится в следующем ACK на основе LTS. Исключением являются случаи, когда исходный патч переносится в android12-5.4 (поскольку патч, скорее всего, уже находится в android-mainline ).

Восходящие патчи

Как указано в руководящих принципах вклада , вверх по течению пятна, предназначенные для ядра ACK, попадают в следующие группы (перечисленные в порядке вероятности принятия).

  • UPSTREAM: - Плач вишневые из «Android -Mainline», вероятно, будут приняты в ACK, если есть разумный вариант использования.
  • BACKPORT: - Патчи из Upstream, которые не вишены чистыми и нуждающимися в модификации, также, вероятно, будут приняты, если есть разумный вариант использования.
  • FROMGIT: — Патчи, выбранные из ветки сопровождающего при подготовке к отправке в основную ветку Linux, могут быть приняты, если наступит ближайший срок. Они должны быть оправданы как для содержания, так и для графика.
  • FROMLIST: - Пласти, которые были представлены в LKML, но не были приняты в филиал сопровождающих, но вряд ли будут приняты, если только оправдание не является достаточно убедительным, чтобы патч был принят, независимо от того, приземляется или нет в Upstream Linux (мы предполагаем что этого не будет). Должна существовать проблема, связанная с патчами FROMLIST , чтобы облегчить обсуждение с командой разработчиков ядра Android.

Патчи для Android

Если вы не можете внести необходимые изменения в исходную версию, вы можете попытаться отправить исправления вне дерева напрямую в ACK. Для отправки внесистемных исправлений необходимо создать в ИТ-отделе проблему, в которой будет указано исправление и обоснование того, почему исправление не может быть отправлено в исходную версию (примеры см. в предыдущем списке). Однако есть несколько случаев, когда код не может быть отправлен вверх по течению. Эти случаи рассматриваются следующим образом и должны соответствовать рекомендациям по публикации патчей для Android и быть помечены префиксом ANDROID: в теме.

Изменения в gki_defconfig

Все изменения CONFIG в gki_defconfig должны применяться как к версиям Arm64, так и к версиям x86, если CONFIG не зависит от архитектуры. Чтобы запросить изменение параметра CONFIG , создайте проблему в ИТ-отделе для обсуждения изменения. Любое изменение CONFIG , которое влияет на интерфейс модуля ядра (KMI) после его заморозки, отклоняется. В случаях, когда партнеры запрашивают конфликтующие настройки для одной конфигурации, мы разрешаем конфликты путем обсуждения связанных ошибок.

Код, которого нет в исходной версии

Модификации кода, уже специфичные для Android, не могут быть отправлены в исходную версию. Например, даже несмотря на то, что драйвер связывателя поддерживается в исходном потоке, изменения в функциях наследования приоритета драйвера связывателя не могут быть отправлены в восходящий поток, поскольку они специфичны для Android. Четко укажите в своей ошибке и исправлении, почему код не может быть отправлен вверх по течению. Если возможно, разделите патчи на куски, которые можно отправить вверх по течению и андроид, которые нельзя отправить вверх по течению, чтобы минимизировать количество кода вне дерева, поддерживаемого в ACK.

Другие изменения в этой категории — это обновления файлов представления KMI, списков символов KMI, gki_defconfig , сценариев сборки или конфигурации или других сценариев, которые не существуют в исходной версии.

Внеструктурные модули

Вверх по течению Linux активно препятствует поддержке создания модулей вне дерева. Это разумная позиция, учитывая, что сопровождающие Linux не дают гарантий относительно совместимости исходных кодов ядра или двоичной совместимости и не хотят поддерживать код, которого нет в дереве. Тем не менее, GKI действительно дает ABI гарантиями для модулей поставщиков, гарантируя, что интерфейсы KMI стабильны для поддерживаемого срока службы ядра. Следовательно, существует класс изменений в поддержку модулей поставщиков, которые приемлемы для ACK, но не приемлемы для Upstream.

Например, рассмотрим патч, который добавляет макросы EXPORT_SYMBOL_GPL() , где модули, которые используют экспорт, не в дереве источника. Хотя вы должны попытаться запросить EXPORT_SYMBOL_GPL() в восходящем направлении и предоставить модуль, который использует новый экспортированный символ, если есть веское обоснование того, почему модуль не отправляется в восходящий поток, вместо этого вы можете отправить исправление в ACK. Вам нужно включить оправдание того, почему модуль не может быть вверх по течению в этом вопросе. (Не запрашивайте вариант без GPL, EXPORT_SYMBOL() .)

Скрытые конфиги

Некоторые модули в дереве автоматически выбирают скрытые конфигурации, которые не могут быть указаны в gki_defconfig . Например, CONFIG_SND_SOC_TOPOLOGY выбирается автоматически, когда настраивается CONFIG_SND_SOC_SOF=y . Для размещения строительства модуля вне дерева GKI включает механизм для включения скрытых конфигураций.

Чтобы включить скрытую конфигурацию, добавьте оператор select в init/Kconfig.gki , чтобы он был автоматически выбран на основе конфигурации ядра CONFIG_GKI_HACKS_TO_FIX , которая включена в gki_defconfig . Используйте этот механизм только для скрытых конфигов; если конфигурация не скрыта, ее необходимо указать в gki_defconfig либо явно, либо как зависимость.

Загружаемые регуляторы

Для фреймворков ядра (таких как cpufreq ), которые поддерживают загружаемые регуляторы, вы можете переопределить регулятор по умолчанию (например, регулятор schedutil cpufreq ). Для фреймворков (таких как Thermal Framework), которые не поддерживают загружаемые регуляторы или драйверы, но все же требуют реализация зависит от поставщика, создайте проблему в ИТ-отделе и проконсультируйтесь с командой разработчиков ядра Android .

Мы будем работать с вами и вышестоящими сопровождающими, чтобы добавить необходимую поддержку.

Торговые крючки

В прошлых выпусках вы можете добавить модификации, специфичные для поставщика непосредственно в ядро ​​ядра. Это невозможно при GKI 2.0, потому что код, специфичный для продукта, должен быть реализован в модулях и не будет принят в ядра в ядрах вверх по течению или в ACK. Чтобы включить функции с добавленной стоимостью, на которые партнеры полагаются с минимальным влиянием на код ядра, GKI принимает крючки поставщиков, которые позволяют вызывать модули из кода ядра. Кроме того, ключевые структуры данных могут быть дополнены полями данных поставщика, которые доступны для хранения данных конкретного поставщика для реализации этих функций.

Крюки поставщиков бывают двух вариантов (нормальных и ограниченных), которые основаны на трацепоинтах (не следах трассировки), к которым могут присоединиться модули поставщиков. Например, вместо добавления новой функции sched_exit() для ведения учета при выходе из задачи поставщики могут добавить в do_exit() ловушку, к которой модуль поставщика может подключиться для обработки. Пример реализации включает следующие перехватчики поставщиков.

  • Обычные перехватчики поставщиков используют DECLARE_HOOK() для создания функции точки трассировки с trace_ name где name — это уникальный идентификатор трассировки. По соглашению имена обычных хуков поставщика начинаются с android_vh , поэтому имя хука sched_exit() будет android_vh_sched_exit .
  • Ограниченные перехватчики поставщиков необходимы в таких случаях, как перехватчики планировщика, когда присоединенная функция должна вызываться, даже если ЦП находится в автономном режиме или требует неатомарного контекста. Ограниченные перехватчики поставщиков не могут быть отсоединены, поэтому модули, подключенные к ограниченному перехватчику, никогда не смогут выгрузиться. Ограниченные имена поставщиков начинаются с android_rvh .

Чтобы добавить привязку поставщика, сообщите о проблеме в ИТ-отдел и отправьте исправления (как и в случае со всеми исправлениями для Android, проблема должна существовать, и вы должны предоставить обоснование). Поддержка перехватчиков поставщиков есть только в ACK, поэтому не отправляйте эти исправления в вышестоящую версию Linux.

Добавление полей поставщика в структуры

Вы можете связать данные поставщика с ключевыми структурами данных, добавив поля android_vendor_data с помощью макроса ANDROID_VENDOR_DATA() . Например, для поддержки дополнительных функций добавьте поля к структурам, как показано в следующем примере кода.

Чтобы избежать потенциальных конфликтов между полями, необходимыми поставщикам, и полями, необходимыми OEM-производителям, OEM-производители никогда не должны использовать поля, объявленные с помощью макроса ANDROID_VENDOR_DATA() . Вместо этого OEM-производители должны использовать ANDROID_OEM_DATA() для объявления полей 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 */
}

Определение привязок поставщиков

Добавьте крючки поставщика в код ядра в качестве Tracepoints, объявив их, используя DECLARE_HOOK() или DECLARE_RESTRICTED_HOOK() , а затем добавив их в код в качестве TracePoint. Например, чтобы добавитьtrace_android_vh_sched_exit trace_android_vh_sched_exit() к существующей функции ядра do_exit() :

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

Функция trace_android_vh_sched_exit() изначально проверяет, только если что-то прикреплено. Однако, если модуль поставщика регистрирует обработчик с использованием register_trace_android_vh_sched_exit() , зарегистрированная функция вызывается. Обработчик должен знать о контексте в отношении удерживаемых замков, государства RCS и других факторов. Крюк должен быть определен в файле заголовка в каталоге include/trace/hooks .

Например, следующий код дает возможное объявление для trace_android_vh_sched_exit() в файле 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>

Чтобы создать экземпляры интерфейсов, необходимых для перехватчика поставщика, добавьте заголовочный файл с объявлением перехватчика в файл drivers/android/vendor_hooks.c и экспортируйте символы. Например, следующий код завершает объявление ловушки 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);

ПРИМЕЧАНИЕ . Структуры данных, которые используются в объявлении крючка, должны быть полностью определены, чтобы гарантировать стабильность ABI. В противном случае это небезопасно для того, чтобы быть непрозрачными указателями или использовать структуру в размерных контекстах. Включение, которое предоставляет полное определение таких структур данных, должно находиться в разделе #ifndef __GENKSYMS__ файла drivers/android/vendor_hooks.c . Файлы заголовка include/trace/hooks не должны включать файл заголовка ядра с определениями типа, чтобы избежать изменений CRC, которые разбивают KMI. Вместо этого вперед объявите типы.

Прикрепить к крючкам продавцов

Чтобы использовать крючки поставщиков, модуль поставщика должен зарегистрировать обработчик для крючка (обычно выполняемый во время инициализации модуля). Например, следующий код показывает обработчик модуля foo.ko для 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);
    ...
}

Используйте хуки поставщиков из файлов заголовков.

Чтобы использовать крючки поставщиков из файлов заголовка, вам может потребоваться обновить файл заголовка поставщика Hook для Undefine TRACE_INCLUDE_PATH , чтобы избежать ошибок сборки, которые указывают, что файл заголовка точки точки не может быть найдена. Например,

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.

Чтобы исправить этот тип ошибки сборки, примените эквивалентное исправление к включаемому вами заголовочному файлу привязки поставщика. Для получения дополнительной информации см . 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

Определение UNDEF_TRACE_INCLUDE_PATH рассказывает include/trace/define_trace.h для undefine TRACE_INCLUDE_PATH после создания точек трассировки.

Основные функции ядра

Если ни один из предыдущих методов не позволяет вам реализовать функцию из модуля, то вы должны добавить функцию в качестве модификации Android-специфического в ядро ​​Core. Создайте проблему в системе отслеживания проблем (IT), чтобы начать разговор.

Интерфейс программирования пользовательских приложений (UAPI)

  • Файлы заголовка UAPI. Изменения в файлах заголовка UAPI должны происходить вверх по течению, если изменения не в Android-специфические интерфейсы. Используйте файлы заголовков, зависящие от поставщика, для определения интерфейсов между модулями поставщика и кодом пользовательского пространства поставщика.
  • Sysfs узлы. Не добавляйте новые узлы sysfs в ядро ​​GKI (такие дополнения допустимы только в модулях поставщиков). Узлы SYSFS, используемые в библиотеках, оживленных SOC и устройствах, и кодом Java, которые включают в себя Android-структуру, могут быть изменены только в совместимых способах и должны быть изменены вверх по течению, если они не Android-специфические узлы SYSFS. Вы можете создавать узлы sysfs для конкретного поставщика, которые будут использоваться пользовательским пространством поставщика. По умолчанию доступ к узлам sysfs от пользовательского пространства отрицается с использованием Selinux. Поставщик должен добавить соответствующие метки Selinux, чтобы разрешить доступ авторизованным программным обеспечением поставщика.
  • Debugfs узлы. Модули поставщиков могут определять узлы в debugfs только для отладки (поскольку debugfs не установлены во время нормальной работы устройства).