Desenvolver o código do kernel para GKI

A imagem genérica do kernel (GKI, na sigla em inglês) reduz a fragmentação do kernel ao se alinhar bem com o kernel upstream do Linux. No entanto, há motivos válidos alguns patches não podem ser aceitos upstream, e há programações de produtos que precisam ser atendidos, então alguns patches são mantidos no kernel comum do Android (ACK) fontes de origem da criação da GKI.

Os desenvolvedores precisam enviar mudanças de código upstream usando o Mailing do kernel do Linux List (LKML) como a primeira escolha e enviar alterações de código para o sistema ACK android-mainline somente quando há um forte motivo pelo qual o upstream não está viáveis. Confira a seguir alguns exemplos de motivos válidos e como lidar com eles.

  • O patch foi enviado ao LKML, mas não foi aceito a tempo para um produto lançamento. Para lidar com esse patch:

    • Forneça evidências de que o patch foi enviado ao LKML e comentários recebido para o patch ou um tempo estimado para o qual o patch enviados upstream.
    • Decidir sobre um curso de ação para aplicar o patch em ACK e fazer com que ele seja aprovado upstream e, em seguida, removê-la do ACK quando a versão upstream final mesclado no ACK.
  • O patch define EXPORT_SYMBOLS_GPL() para um módulo de fornecedor, mas não podia ser enviados upstream porque não há módulos em árvore que consomem esse símbolo. Para lidar com esse patch, forneça detalhes sobre os motivos pelos quais o módulo não pode ser enviado upstream e as alternativas que você considerou antes de fazer isso solicitação.

  • O patch não é genérico o suficiente para upstream e não há tempo para fazer a refatoração antes do lançamento de um produto. Para lidar com esse patch, forneça um estimado pelo qual um patch refatorado é enviado upstream (o tempo não serão aceitos em ACK sem um plano para enviar patch upstream para revisão).

  • O patch não pode ser aceito pelo upstream porque... <inserir motivo aqui>. Para lidar com esse patch, entre em contato com a equipe do kernel do Android e trabalhar conosco sobre as opções de refatoramento do patch para que ele possa ser enviado para revisão e aceito upstream.

Existem muitas outras justificativas possíveis. Quando você envia seu bug ou incluir uma justificativa válida e esperar alguma iteração e discussão. Reconhecemos que a ACK tem alguns patches, especialmente nos primeiros de GKI enquanto todos estão aprendendo a trabalhar de forma upstream, mas não conseguem relaxar de produtos para fazer isso. Os requisitos de upstreaming vão ficar rigorosas ao longo do tempo.

Requisitos do patch

Os patches devem estar em conformidade com os padrões de codificação do kernel do Linux descritos na Árvore de origem do Linux, se eles foram enviados de forma upstream ou para ACK. O scripts/checkpatch.pl O script é executado como parte do teste de pré-envio do Gerrit, portanto, execute-o com antecedência para que ele passe. Para executar o script checkpatch com a mesma configuração que o teste de pré-envio, use //build/kernel/static_analysis:checkpatch_presubmit. Para mais detalhes, consulte build/kernel/kleaf/docs/checkpatch.md.

Patches ACK

Os patches enviados para ACK devem estar em conformidade com os padrões de codificação do kernel do Linux e as diretrizes de contribuição. Você precisa incluir um Change-Id. tag na mensagem de confirmação. se você enviar o patch para várias ramificações (por exemplo, android-mainline e android12-5.4), é necessário usar a mesma Change-Id para todas as instâncias do patch.

Envie patches ao LKML primeiro para uma revisão upstream. Se o patch estiver:

  • Aceito upstream. Ele é mesclado automaticamente em android-mainline.
  • Não aceito upstream. Envie-o para android-mainline com um referência ao envio upstream ou uma explicação sobre por que não foi enviados ao LKML.

Depois que um patch é aceito de forma upstream ou em android-mainline, ele pode ser backport para a ACK baseada em LTS apropriada (como android12-5.4 e android11-5.4 para patches que corrigem código específico do Android). Enviando para O android-mainline permite testes com novos candidatos a lançamento upstream e garante que o patch esteja na próxima ACK baseada em LTS. Exceções incluem casos em que um patch upstream tem backport para android12-5.4 (porque o patch é provavelmente já estará em android-mainline).

Patches upstream

Conforme especificado na contribuição diretrizes, patches upstream destinados a kernels ACK estão nos seguintes grupos (listados em ordem de probabilidade de serem aceitos).

  • UPSTREAM:: os patches escolhidos a dedo de "android-mainline" provavelmente serão aceitos em ACK se houver um caso de uso razoável.
  • BACKPORT:: patches do upstream que não são selecionados corretamente e precisam modificação provavelmente serão aceitas se houver um uso razoável caso.
  • FROMGIT:: patches selecionados a dedo de uma ramificação de mantenedor em preparação para envio para a linha principal do Linux pode ser aceito se houver prazo. Elas precisam ser justificadas com relação ao conteúdo e à programação.
  • FROMLIST: - Patches que foram enviados ao LKML, mas não foram aceitos em uma ramificação de mantenedor, mas provavelmente não serão aceitos, a menos que a justificativa é convincente o suficiente para que o patch seja aceito. mesmo se ele chegar ou não no Linux upstream (presumimos que não haja). precisa ser um problema associado a patches do FROMLIST para facilitar a discussão com a equipe do kernel do Android.

Patches específicos do Android

Se não for possível enviar as mudanças necessárias upstream, tente enviar patches fora da árvore para ACK diretamente. O envio de patches fora da árvore exige que você crie um problema na TI que cita o patch e a justificativa O patch não pode ser enviado de forma upstream (consulte a lista anterior para exemplos). No entanto, em alguns casos, não é possível enviar o código upstream. Esses casos são cobertos da seguinte forma e devem seguir a contribuição diretrizes para patches específicos do Android e ser marcado com o prefixo ANDROID: no assunto.

Mudanças no gki_defconfig

Todas as mudanças de CONFIG em gki_defconfig precisam ser aplicadas às arquiteturas arm64 e Versões x86, a menos que o CONFIG seja específico da arquitetura. Para solicitar uma alteração para uma configuração CONFIG, crie um problema na TI para discutir a mudança. Qualquer um Mudança no CONFIG que afeta a interface de módulo do kernel (KMI) após ser congelado é rejeitada. Nos casos em que os parceiros solicitam problemas para uma única configuração, resolvemos os conflitos discutindo os bugs relacionados.

Código que não existe upstream

Modificações no código que já são específicos do Android não podem ser enviadas upstream. Por exemplo, mesmo que o driver de vinculação seja mantido upstream, as modificações aos recursos de herança prioritária do driver de vinculação não podem ser enviados upstream porque elas são específicas do Android. Seja explícito no bug e corrija por que o o código não pode ser enviado upstream. Se possível, divida os patches em partes que podem ser enviadas partes upstream e específicas do Android que não podem ser enviadas upstream para minimizar a quantidade de código fora da árvore mantido em ACK.

Outras mudanças nessa categoria são atualizações nos arquivos de representação KMI, listas de símbolos, gki_defconfig, scripts ou configurações de build ou outros scripts que não existem upstream.

Módulos fora da árvore

O upstream do Linux não recomenda ativamente o suporte à criação de módulos fora da árvore. Essa é uma posição razoável, já que os mantenedores do Linux não garantem sobre compatibilidade de código-fonte ou binário no kernel e não quer dar suporte a códigos que não está na árvore. No entanto, a GKI faz garantias de ABI para módulos do fornecedor, garantindo que as interfaces KMI sejam estáveis para os o ciclo de vida de um kernel. Portanto, há uma classe de mudanças para dar suporte ao módulos aceitáveis para ACK, mas não para upstream.

Por exemplo, considere um patch que adiciona macros EXPORT_SYMBOL_GPL() onde o módulos que usam a exportação não estão na árvore de origem. Você precisa tentar para solicitar um upstream EXPORT_SYMBOL_GPL() e fornecer um módulo que usa o símbolo recém-exportado, se houver uma justificativa válida para o motivo do módulo não estiver sendo enviado upstream, envie o patch para ACK. Você é necessário incluir a justificativa para o motivo pelo qual o módulo não pode ser enviado no problema. Não solicite a variante não GPL, EXPORT_SYMBOL().

Configurações ocultas

Alguns módulos em árvore selecionam automaticamente configurações ocultas que não podem ser especificadas em gki_defconfig. Por exemplo, CONFIG_SND_SOC_TOPOLOGY foi selecionado automaticamente quando CONFIG_SND_SOC_SOF=y é configurado. Para acomodar a criação de módulos fora da árvore, a GKI inclui um mecanismo para ativar configurações ocultas.

Para ativar uma configuração oculta, adicione uma instrução select em init/Kconfig.gki para que ela é selecionado automaticamente com base na configuração do kernel CONFIG_GKI_HACKS_TO_FIX, que é ativado em gki_defconfig. Use esse mecanismo apenas para configurações ocultas. Se a configuração não estiver oculta, será necessário especificar em gki_defconfig explicitamente ou como uma dependência.

Governadores carregáveis

Para frameworks do kernel (como cpufreq) com suporte a gerenciadores carregáveis, pode substituir o governador padrão (como o governador schedutil de cpufreq). Para estruturas (como a estrutura térmica) que não são compatíveis com governadores carregáveis ou drivers, mas ainda exigir uma implementação específica do fornecedor, crie um problema do departamento de TI e consulte a equipe do kernel do Android.

Trabalharemos com você e com os mantenedores upstream para adicionar o suporte necessário.

Ganchos do fornecedor

Em versões anteriores, era possível adicionar modificações específicas de fornecedores diretamente no núcleo do núcleo. Isso não é possível com o GKI 2.0 porque o código específico do produto deve serão implementadas em módulos e não serão aceitas nos kernels principais upstream ou em ACK. Para ativar os recursos de valor agregado em que os parceiros contam com impacto mínimo no código do kernel principal, a GKI aceita hooks de fornecedores que permitem que os módulos sejam invocados do código do kernel principal. Além disso, as principais estruturas de dados podem ser preenchidas com campos de dados do fornecedor que estão disponíveis para armazenar dados específicos do fornecedor para implementar esses recursos.

Os ganchos para fornecedores têm duas variantes (normal e restrita) que são baseadas tracepoints (não eventos de trace) aos quais os módulos do fornecedor podem se anexar. Por exemplo: em vez de adicionar uma nova função sched_exit() para fazer a contabilização na tarefa. sair, os fornecedores podem adicionar um hook em do_exit() que um módulo de fornecedor pode ser anexado a para processamento. Um exemplo de implementação inclui os ganchos de fornecedor a seguir.

  • Os hooks normais de fornecedores usam DECLARE_HOOK() para criar uma função de tracepoint. com o nome trace_name, em que name é o identificador exclusivo do rastros. Por convenção, os nomes de hook de fornecedor normais começam com android_vh. Portanto, o nome do hook sched_exit() seria android_vh_sched_exit.
  • Os hooks de fornecedores restritos são necessários para casos como hooks de programador em que a função anexada precisa ser chamada mesmo se a CPU estiver off-line ou precisar em um contexto não atômico. Os hooks restritos de fornecedores não podem ser desconectados, portanto, os módulos anexados a um gancho restrito nunca podem ser descarregados. Restrito os nomes de hook de fornecedor começam com android_rvh.

Para adicionar um gancho de fornecedor, registre um problema na TI e envie patches (como em todos os com patches específicos do Android, um problema precisa existir e você deve fornecer justificativa). O suporte a hooks de fornecedor é apenas em ACK, portanto, não envie esses e patches para o Linux upstream.

Adicionar campos de fornecedores às estruturas

Você pode associar os dados do fornecedor com as principais estruturas de dados adicionando Campos android_vendor_data usando as macros ANDROID_VENDOR_DATA(). Para exemplo, para oferecer suporte a atributos de valor agregado, anexe campos a estruturas, conforme mostrado no exemplo de código a seguir.

Para evitar possíveis conflitos entre campos exigidos por fornecedores e campos exigido pelos OEMs, eles nunca poderão usar campos declarados usando ANDROID_VENDOR_DATA() de macros. Em vez disso, os OEMs precisam usar ANDROID_OEM_DATA(). para declarar campos 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 */
}

Definir ganchos de fornecedor

Adicionar hooks de fornecedores ao código do kernel como tracepoints declarando-os com o uso do DECLARE_HOOK() ou DECLARE_RESTRICTED_HOOK() e, em seguida, adicioná-las ao código como um tracepoint. Por exemplo, para adicionar trace_android_vh_sched_exit() ao função atual do kernel do_exit():

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

Inicialmente, a função trace_android_vh_sched_exit() verifica apenas se algo está anexado. No entanto, se um módulo de fornecedor registrar um gerenciador usando register_trace_android_vh_sched_exit(), a função registrada será chamada. A o gerenciador precisa estar ciente do contexto em relação a bloqueios retidos, estado RCS e outros fatores. O gancho precisa ser definido em um arquivo principal no include/trace/hooks.

Por exemplo, o código a seguir fornece uma possível declaração para trace_android_vh_sched_exit() no arquivo 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>

Para instanciar as interfaces necessárias para o hook de fornecedor, adicione o arquivo de cabeçalho. com a declaração de hook para drivers/android/vendor_hooks.c e exportar o símbolos. Por exemplo, o código a seguir completa a declaração da Hook 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);

OBSERVAÇÃO: as estruturas de dados usadas na declaração do hook precisam ser totalmente definidos para garantir a estabilidade da ABI. Caso contrário, não é seguro desreferencie os ponteiros opacos ou use a estrutura em contextos dimensionados. A inclusão que fornece a definição completa dessas estruturas de dados, Seção #ifndef __GENKSYMS__ de drivers/android/vendor_hooks.c. Cabeçalho os arquivos em include/trace/hooks não devem incluir o arquivo de cabeçalho do kernel com o definições de tipo para evitar alterações de CRC que possam corromper o KMI. Em vez disso, encaminhe declarar os tipos.

Anexar aos ganchos do fornecedor

Para usar os ganchos do fornecedor, o módulo do fornecedor precisa registrar um gerenciador para o gancho (normalmente feito durante a inicialização do módulo). Por exemplo, o código a seguir mostra o gerenciador foo.ko do módulo para 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);
    ...
}

Usar ganchos de fornecedor a partir de arquivos principais

Para usar ganchos de fornecedor a partir de arquivos de cabeçalho, pode ser necessário atualizar o gancho de fornecedor arquivo principal para remover a definição de TRACE_INCLUDE_PATH para evitar erros de build que indicam um arquivo de cabeçalho do ponto de rastreamento não foi encontrado. Por exemplo:

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.

Para corrigir esse tipo de erro de build, aplique a correção equivalente ao hook de fornecedor arquivo de cabeçalho que está sendo incluído. Para mais informações, consulte 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

Definir UNDEF_TRACE_INCLUDE_PATH instrui o include/trace/define_trace.h a remover a definição de TRACE_INCLUDE_PATH depois de criar os pontos de rastreamento.

Principais recursos do kernel

Se nenhuma das técnicas anteriores permitir a implementação de um recurso de um módulo, adicione o recurso como uma modificação específica do Android no grão Crie um problema no Issue Tracker (TI) para iniciar a conversa.

Interface de programação do aplicativo do usuário (UAPI)

  • Arquivos de cabeçalho UAPI. Mudanças no Arquivos de cabeçalho UAPI precisam ocorrer upstream, a menos que as mudanças sejam em interfaces específicas do Android. Usar arquivos principais específicos do fornecedor para definir interfaces entre os módulos do fornecedor e o código do espaço do usuário do fornecedor.
  • de nós do sysfs. Não adicione novos nós sysfs ao kernel de GKI (como adições são válidos apenas nos módulos do fornecedor). nós sysfs usados pelos recursos SoC- e bibliotecas independentes de dispositivo e código Java que compreendem o framework do Android só podem ser alterados de maneiras compatíveis e precisam ser alterados upstream se não são nós sysfs específicos do Android. Você pode criar nós sysfs específicos do fornecedor a serem usados pelo espaço do usuário do fornecedor. Por padrão, o acesso aos nós sysfs por espaço do usuário é negado ao usar o SELinux. Cabe ao fornecedor adicione os rótulos SELinux apropriados para permitir o acesso por o software de um fornecedor.
  • Nós do DebugFS. Os módulos do fornecedor podem definir nós no debugfs para somente depuração (já que debugfs não é montado durante a operação normal do dispositivo).