Desenvolver código do kernel para GKI

A Imagem Genérica do Kernel (GKI) reduz a fragmentação do kernel alinhando-se de perto com o kernel Linux upstream. No entanto, há motivos válidos para que alguns patches não possam ser aceitos no upstream e há cronogramas de produtos que devem ser atendidos, portanto, alguns patches são mantidos nas fontes do Android Common Kernel (ACK) a partir das quais o GKI é criado.

Os desenvolvedores devem enviar alterações de código upstream usando a Linux Kernel Mailing List (LKML) como a primeira escolha e enviar alterações de código para a ramificação ACK android-mainline somente quando houver uma forte razão pela qual o upstream não é viável. Exemplos de motivos válidos e como lidar com eles estão listados a seguir.

  • O patch foi submetido à LKML, mas não foi aceito a tempo para o lançamento do produto. Para lidar com este patch:

    • Forneça evidências de que o patch foi enviado ao LKML e comentários recebidos para o patch, ou um tempo estimado para o envio do patch upstream.
    • Decida sobre um curso de ação para colocar o patch no ACK, aprove-o upstream e, em seguida, retire-o do ACK quando a versão final do upstream for mesclada no ACK.
  • O patch define EXPORT_SYMBOLS_GPL() para um módulo de fornecedor, mas não pode ser enviado para upstream porque não há módulos na árvore que consomem esse símbolo. Para lidar com esse patch, forneça detalhes sobre por que seu módulo não pode ser enviado upstream e as alternativas que você considerou antes de fazer essa solicitação.

  • O patch não é genérico o suficiente para upstream e não há tempo para refatorá-lo antes do lançamento de um produto. Para lidar com este patch, forneça uma estimativa de tempo em que um patch refatorado será enviado upstream (o patch não será aceito no ACK sem um plano para enviar um patch refatorado 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 trabalhe conosco nas opções para refatorar o patch para que ele possa ser enviado para revisão e aceito upstream.

Há muito mais justificativas em potencial. Quando você enviar seu bug ou seu patch, inclua uma justificativa válida e espere alguma interação e discussão. Reconhecemos que o ACK terá alguns patches, especialmente nas fases iniciais do GKI, enquanto todos estão aprendendo a trabalhar upstream, mas não podem relaxar os cronogramas dos produtos para fazê-lo. Espere que os requisitos de upstreaming se tornem mais rigorosos ao longo do tempo.

Requisitos de 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 , sejam enviados upstream ou para ACK. O scripts/checkpatch.pl é executado como parte do teste de pré-envio do Gerrit, portanto, execute-o com antecedência para garantir que ele seja aprovado. Para executar o script checkpatch com a mesma configuração que o teste de pré-envio, use build/static_analysis/checkpatch_presubmit.sh repo check-out do repositório.

Patches de ACK

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

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

  • Aceito upstream, ele é mesclado automaticamente em android-mainline .
  • Não aceito upstream, envie-o para android-mainline com uma referência ao envio upstream ou uma explicação do motivo pelo qual não foi enviado ao LKML.

Depois que um patch é aceito no upstream ou no android-mainline , ele pode ser transferido para o ACK baseado em LTS apropriado (como android12-5.4 e android11-5.4 para patches que corrigem o código específico do Android). O envio para android-mainline permite testar com novos candidatos a lançamento upstream e garante que o patch esteja no próximo ACK baseado em LTS. As exceções incluem casos em que um patch upstream é retroportado para android12-5.4 (porque o patch provavelmente já está em android-mainline ).

Patches upstream

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

  • UPSTREAM: - Patches selecionados de 'android-mainline' provavelmente serão aceitos no ACK se houver um caso de uso razoável.
  • BACKPORT: - Patches de upstream que não são escolhidos a dedo e precisam de modificação também provavelmente serão aceitos se houver um caso de uso razoável.
  • FROMGIT: - Patches escolhidos a dedo de uma ramificação do mantenedor em preparação para o envio para a linha principal do Linux podem ser aceitos se houver um prazo próximo. Estes devem ser justificados tanto pelo conteúdo quanto pelo cronograma.
  • FROMLIST: - Patches que foram submetidos ao LKML, mas ainda não foram aceitos em uma ramificação de mantenedor, provavelmente não serão aceitos, a menos que a justificativa seja convincente o suficiente para que o patch seja aceito independentemente de chegar ou não ao Linux upstream (assumimos que não vai). Deve haver um problema associado aos patches FROMLIST para facilitar a discussão com a equipe do kernel do Android.

Patches específicos do Android

Se você não conseguir fazer as alterações necessárias upstream, você pode tentar enviar patches fora da árvore para ACK diretamente. O envio de patches fora da árvore exige que você crie um problema na TI que cite o patch e o motivo pelo qual o patch não pode ser enviado upstream (consulte a lista anterior para obter exemplos). No entanto, existem alguns casos em que o código não pode ser enviado upstream. Esses casos são abordados da seguinte forma e devem seguir as diretrizes de contribuição para patches específicos do Android e ser marcados com o prefixo ANDROID: no assunto.

Alterações no gki_defconfig

Todas as alterações de CONFIG no gki_defconfig devem ser aplicadas às versões arm64 e x86, a menos que o CONFIG seja específico da arquitetura. Para solicitar uma alteração em uma configuração de CONFIG , crie um problema na TI para discutir a alteração. Qualquer alteração CONFIG que afete a Kernel Module Interface (KMI) depois de congelada é rejeitada. Nos casos em que os parceiros solicitam configurações conflitantes para uma única configuração, resolvemos os conflitos por meio da discussão dos bugs relacionados.

Código que não existe upstream

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

Outras alterações nesta categoria são atualizações para arquivos de representação KMI, listas de símbolos KMI, gki_defconfig , scripts ou configuração de compilação ou outros scripts que não existem upstream.

Módulos fora da árvore

O Upstream Linux desencoraja ativamente o suporte para a construção de módulos fora da árvore. Esta é uma posição razoável dado que os mantenedores do Linux não fazem garantias sobre a fonte no kernel ou compatibilidade binária e não querem suportar código que não esteja na árvore. No entanto, o GKI oferece garantias de ABI para os módulos do fornecedor, garantindo que as interfaces KMI sejam estáveis ​​durante a vida útil suportada de um kernel. Portanto, há uma classe de alterações para oferecer suporte a módulos de fornecedores que são aceitáveis ​​para ACK, mas não são aceitáveis ​​para upstream.

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

Configurações ocultas

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

Para habilitar uma configuração oculta, adicione uma instrução select em init/Kconfig.gki para que ela seja selecionada automaticamente com base na configuração do kernel CONFIG_GKI_HACKS_TO_FIX , que está habilitada em gki_defconfig . Use este mecanismo apenas para configurações ocultas; se a configuração não estiver oculta, ela deve ser especificada em gki_defconfig explicitamente ou como uma dependência.

Governadores carregáveis

Para estruturas de kernel (como cpufreq ) que suportam governadores carregáveis, você pode substituir o governador padrão (como o governador schedutil de cpufreq . implementação específica do fornecedor, crie um problema na TI e consulte a equipe do kernel do Android .

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

Ganchos de fornecedores

Nas versões anteriores, você podia adicionar modificações específicas do fornecedor diretamente no kernel principal. Isso não é possível com o GKI 2.0 porque o código específico do produto deve ser implementado em módulos e não será aceito nos kernels principais upstream ou no ACK. Para habilitar recursos de valor agregado nos quais os parceiros confiam com impacto mínimo no código do núcleo do kernel, a GKI aceita ganchos do fornecedor que permitem que os módulos sejam invocados do código do núcleo do núcleo. 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 do fornecedor vêm em duas variantes (normal e restrita) que são baseadas em pontos de rastreamento (não em eventos de rastreamento) aos quais os módulos do fornecedor podem se conectar. Por exemplo, em vez de adicionar uma nova função sched_exit() para fazer uma contabilidade na saída da tarefa, os fornecedores podem adicionar um gancho em do_exit() que um módulo de fornecedor pode anexar para processamento. Um exemplo de implementação inclui os ganchos de fornecedor a seguir.

  • Ganchos de fornecedores normais usam DECLARE_HOOK() para criar uma função de ponto de rastreamento com o nome trace_ name onde name é o identificador exclusivo para o rastreamento. Por convenção, os nomes de ganchos de fornecedores normais começam com android_vh , então o nome do gancho sched_exit() seria android_vh_sched_exit .
  • Ganchos de fornecedor restritos são necessários para casos como ganchos do agendador em que a função anexada deve ser chamada mesmo que a CPU esteja offline ou exija um contexto não atômico. Ganchos de fornecedores restritos não podem ser desanexados, então os módulos que se conectam a um gancho restrito nunca podem ser descarregados. Apenas um anexo é permitido, portanto, qualquer outra tentativa de anexar falha com -EBUSY . Os nomes de ganchos de fornecedores restritos começam com android_rvh .

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

Adicionar campos de fornecedor a estruturas

Você pode associar dados de fornecedores a estruturas de dados chave adicionando campos android_vendor_data usando as macros ANDROID_VENDOR_DATA ANDROID_VENDOR_DATA() . Por exemplo, para oferecer suporte a recursos de valor agregado, anexe campos a estruturas conforme mostrado no exemplo de código a seguir.

Para evitar possíveis conflitos entre campos necessários para fornecedores e campos necessários para OEMs, os OEMs nunca devem usar campos declarados usando macros ANDROID_VENDOR_DATA() . Em vez disso, os OEMs devem usar ANDROID_OEM_DATA() para declarar os 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

Adicione ganchos de fornecedor ao código do kernel como pontos de rastreamento declarando-os usando DECLARE_HOOK() ou DECLARE_RESTRICTED_HOOK() e, em seguida, adicionando-os ao código como um ponto de rastreamento. Por exemplo, para adicionar trace_android_vh_sched_exit() à função do kernel do_exit() existente:

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

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

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

NOTA : As estruturas de dados que são usadas na declaração de gancho precisam ser totalmente definidas para garantir a estabilidade da ABI. Caso contrário, não é seguro desreferenciar os ponteiros opacos ou usar o struct em contextos dimensionados. O #include <linux/sched.h> no exemplo acima disponibiliza a definição de struct task_struct e habilita o rastreamento de ABI.

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

Anexar aos ganchos do fornecedor

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

Recursos principais do kernel

Se nenhuma das técnicas anteriores permitir que você implemente um recurso de um módulo, você deverá adicionar o recurso como uma modificação específica do Android ao kernel principal. Crie um problema no rastreador de problemas (TI) para iniciar a conversa.

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

  • Arquivos de cabeçalho UAPI. As alterações nos arquivos de cabeçalho UAPI devem ocorrer upstream, a menos que as alterações sejam em interfaces específicas do Android. Use arquivos de cabeçalho específicos do fornecedor para definir interfaces entre os módulos do fornecedor e o código do espaço de usuário do fornecedor.
  • nós sysfs. Não adicione novos nós sysfs ao kernel GKI (essas adições são válidas apenas em módulos do fornecedor). Os nós sysfs usados ​​pelas bibliotecas independentes de SoC e de dispositivo e o código Java que compreende a estrutura do Android podem ser alterados apenas de maneiras compatíveis e devem ser alterados upstream se não forem nós sysfs específicos do Android. Você pode criar nós sysfs específicos do fornecedor para serem usados ​​pelo espaço de usuário do fornecedor. Por padrão, o acesso aos nós sysfs por espaço de usuário é negado usando SELinux. Cabe ao fornecedor adicionar os rótulos apropriados do SELinux para permitir o acesso do software do fornecedor autorizado.
  • Nós DebugFS. Os módulos do fornecedor podem definir nós no debugfs apenas para depuração (já que o debugfs não é montado durante a operação normal do dispositivo).