Разработать код ядра для 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, поэтому запустите его заранее, чтобы убедиться, что он пройден. Чтобы запустить сценарий проверки патча с той же конфигурацией, что и при предварительном тестировании, используйте //build/kernel/static_analysis:checkpatch_presubmit . Подробности см. в build/kernel/kleaf/docs/checkpatch.md .

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-mainline», скорее всего, будут приняты в 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 , сценариев сборки или конфигурации или других сценариев, которые не существуют в исходной версии.

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

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

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

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

Некоторые модули в дереве автоматически выбирают скрытые конфигурации, которые нельзя указать в 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 */
}

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

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

Основные возможности ядра

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

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

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