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

Универсальный образ ядра (GKI) уменьшает фрагментацию ядра, тесно согласовываясь с вышестоящим ядром Linux. Тем не менее, существуют веские причины, по которым некоторые исправления не могут быть приняты восходящими потоками, и существуют расписания продуктов, которые необходимо соблюдать, поэтому некоторые исправления поддерживаются в исходных кодах общего ядра Android (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/static_analysis/checkpatch_presubmit.sh repo проверки репозитория.

ACK-патчи

Патчи, отправляемые в ACK, должны соответствовать стандартам кодирования ядра Linux и рекомендациям по внесению изменений. Вы должны включить тег Change-Id в сообщение фиксации; если вы отправляете патч в несколько веток (например, 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», скорее всего, будут приняты в ACK, если есть разумный вариант использования.
  • BACKPORT: - Патчи из апстрима, которые не идеально подобраны и нуждаются в модификации, также, скорее всего, будут приняты, если есть разумный вариант использования.
  • FROMGIT: — Патчи, отобранные из ветки сопровождающего при подготовке к отправке в основную ветку Linux, могут быть приняты, если приближается крайний срок. Они должны быть обоснованы как по содержанию, так и по расписанию.
  • FROMLIST: - Патчи, отправленные в LKML, но еще не принятые в ветку сопровождающего, вряд ли будут приняты, если только обоснование не будет достаточно убедительным, чтобы патч был принят независимо от того, попадет он в исходную версию Linux или нет (мы предполагаем что не будет). Должна быть проблема, связанная с исправлениями FROMLIST , чтобы облегчить обсуждение с командой ядра Android.

Патчи для Android

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

Изменения в gki_defconfig

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

Код, который не существует выше по течению

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

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

Модули вне дерева

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

Например, рассмотрим патч, который добавляет 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 ). реализации для конкретного поставщика, создайте проблему в ИТ-отделе и проконсультируйтесь с командой разработчиков ядра Android .

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

Крючки продавца

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

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

  • Обычные хуки поставщиков используют DECLARE_HOOK() для создания функции точки трассировки с именем trace_ name где name — уникальный идентификатор трассировки. По соглашению обычные имена хуков поставщиков начинаются с android_vh , поэтому имя sched_exit() будет android_vh_sched_exit .
  • Перехватчики с ограниченным доступом необходимы для таких случаев, как перехватчики планировщика, когда присоединенная функция должна вызываться, даже если ЦП находится в автономном режиме или требует неатомарного контекста. Крючки с ограниченным доступом нельзя отсоединить, поэтому модули, прикрепленные к хуку с ограниченным доступом, никогда не могут быть выгружены. Разрешено только одно вложение, поэтому любые другие попытки присоединения завершаются с ошибкой -EBUSY . Имена хуков поставщиков с ограниченным доступом начинаются с 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 */
}

Определить крючки поставщиков

Добавьте обработчики поставщиков в код ядра в качестве точек трассировки, объявив их с помощью DECLARE_HOOK() или DECLARE_RESTRICTED_HOOK() а затем добавив их в код в качестве точек трассировки. Например, чтобы добавить 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/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>

ПРИМЕЧАНИЕ . Структуры данных, которые используются в объявлении ловушки, должны быть полностью определены, чтобы гарантировать стабильность ABI. В противном случае небезопасно разыменовывать непрозрачные указатели или использовать структуру в контексте размера. #include <linux/sched.h> в приведенном выше примере делает определение struct task_struct доступным и включает отслеживание ABI.

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

Прикрепите к крючкам поставщиков

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

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

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

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

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