Desenvolver código do kernel para GKI

A imagem genérica do kernel (GKI) reduz a fragmentação do kernel alinhando-se estreitamente com o kernel Linux upstream. No entanto, há razões válidas pelas quais alguns patches não podem ser aceitos no upstream e há cronogramas de produtos que devem ser atendidos, portanto, alguns patches são mantidos nas fontes 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 primeira opção e enviar alterações de código para o branch ACK android-mainline somente quando houver um forte motivo pelo qual o upstream não é viável. Exemplos de razões válidas e como lidar com elas estão listados a seguir.

  • O patch foi enviado ao 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 sobre o patch, ou um tempo estimado em que o patch será enviado ao upstream.
    • Decida um curso de ação para colocar o patch no ACK, aprová-lo no upstream e, em seguida, retirá-lo 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 pôde ser enviado ao upstream porque não há módulos na árvore que consumam esse símbolo. Para lidar com esse patch, forneça detalhes sobre por que seu módulo não pode ser enviado ao upstream e as alternativas que você considerou antes de fazer essa solicitação.

  • O patch não é genérico o suficiente para o upstream e não há tempo para refatorá-lo antes do lançamento do produto. Para lidar com esse patch, forneça um tempo estimado em que um patch refatorado será enviado ao upstream (o patch não será aceito no ACK sem um plano para enviar um patch refatorado ao upstream para revisão).

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

Existem muitas outras justificativas potenciais. Ao enviar seu bug ou patch, inclua uma justificativa válida e espere alguma iteração e discussão. Reconhecemos que o ACK trará alguns patches, especialmente nas fases iniciais do GKI, enquanto todos estão aprendendo como trabalhar no upstream, mas não podem relaxar os cronogramas dos produtos para fazê-lo. Espere que os requisitos de upstreaming se tornem mais rigorosos com o tempo.

Requisitos de patch

Os patches devem estar em conformidade com os padrões de codificação do kernel Linux descritos na árvore de origem do Linux , sejam eles enviados pelo upstream ou pelo ACK. O script 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 do teste de pré-envio, use //build/kernel/static_analysis:checkpatch_presubmit . Para obter detalhes, consulte build/kernel/kleaf/docs/checkpatch.md .

Patches ACK

Os patches enviados ao ACK devem estar em conformidade com os padrões de codificação do kernel Linux e com as diretrizes de contribuição . Você deve incluir uma tag Change-Id na mensagem de commit; 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 no upstream, ele é mesclado automaticamente em android-mainline .
  • Não aceito no upstream, envie-o para android-mainline com uma referência ao envio do upstream ou uma explicação do motivo pelo qual não foi enviado ao LKML.

Depois que um patch é aceito no upstream ou em android-mainline , ele pode ser portado para o ACK baseado em LTS apropriado (como android12-5.4 e android11-5.4 para patches que corrigem código específico do Android). O envio para android-mainline permite testes 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 é portado para android12-5.4 (porque é provável que o patch já esteja em android-mainline ).

Patches upstream

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

  • UPSTREAM: - Patches escolhidos a dedo em 'android-mainline' provavelmente serão aceitos no ACK se houver um caso de uso razoável.
  • BACKPORT: - Patches do upstream que não são selecionados corretamente 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 um branch mantenedor em preparação para envio à 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 enviados ao LKML, mas ainda não foram aceitos em um branch 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 upstream do Linux (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 realizar as alterações necessárias no upstream, poderá tentar enviar patches fora da árvore diretamente para o ACK. O envio de patches fora da árvore exige que você crie um problema na TI que cite o patch e a justificativa do motivo pelo qual o patch não pode ser enviado ao upstream (consulte a lista anterior para obter exemplos). No entanto, existem alguns casos em que o código não pode ser enviado ao upstream. Esses casos são abordados a seguir e devem seguir as diretrizes de contribuição para patches específicos do Android e ser marcados com o prefixo ANDROID: no assunto.

Mudanças em gki_defconfig

Todas as alterações CONFIG em 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 CONFIG , crie um problema na TI para discutir a alteração. Qualquer alteração CONFIG que afete a Kernel Module Interface (KMI) após seu congelamento será rejeitada. Nos casos em que os parceiros solicitam configurações conflitantes para uma única configuração, resolvemos os conflitos através da discussão dos bugs relacionados.

Código que não existe upstream

Modificações no código que já é específico do Android não podem ser enviadas upstream. Por exemplo, mesmo que o driver binder seja mantido upstream, as modificações nos recursos de herança prioritária do driver 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 ao upstream. Se possível, divida os patches em partes que podem ser enviadas pelo upstream e partes específicas do Android que não podem ser enviadas pelo upstream para minimizar a quantidade de código fora da árvore mantido no ACK.

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

Módulos fora da árvore

O Upstream Linux desencoraja ativamente o suporte à construção de módulos fora da árvore. Esta é uma posição razoável, visto que os mantenedores do Linux não oferecem 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 ABI para módulos de fornecedores, 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ê poderá enviar o patch para ACK. Você precisa incluir a justificativa do motivo pelo qual o módulo não pode ser transferido por upstream no problema. (Não solicite a variante não GPL, EXPORT_SYMBOL() .)

Configurações ocultas

Alguns módulos na árvore selecionam automaticamente 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 ativar 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 deverá 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 . Para estruturas (como a estrutura térmica) que não suportam governadores ou drivers carregáveis, mas ainda exigem um 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 originais para adicionar o suporte necessário.

Ganchos de fornecedor

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 contam com impacto mínimo no código principal do kernel, o GKI aceita ganchos de fornecedores que permitem que módulos sejam invocados a partir do código principal do kernel. 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 ser anexados. 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() ao qual um módulo do fornecedor pode anexar para processamento. Um exemplo de implementação inclui os seguintes ganchos de fornecedor.

  • Os ganchos normais do fornecedor usam DECLARE_HOOK() para criar uma função de tracepoint com o nome trace_ name , onde name é o identificador exclusivo do rastreamento. Por convenção, os nomes normais dos ganchos dos fornecedores 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 de agendador em que a função anexada deve ser chamada mesmo se a CPU estiver offline ou exigir um contexto não atômico. Os ganchos de fornecedor restritos não podem ser desanexados, portanto, os módulos anexados a um gancho restrito nunca poderão ser descarregados. 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 acontece com todos os patches específicos do Android, um problema deve existir e você deve fornecer uma justificativa). O suporte para ganchos de fornecedores está apenas em ACK, portanto, não envie esses patches para o Linux upstream.

Adicionar campos de fornecedor às estruturas

Você pode associar dados do fornecedor a estruturas de dados importantes adicionando campos android_vendor_data usando as macros ANDROID_VENDOR_DATA() . Por exemplo, para dar 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 os campos necessários aos fornecedores e os campos necessários aos 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 depois 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, ao estado do RCS e a 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 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 gancho do fornecedor, adicione o arquivo de cabeçalho com a declaração do gancho a 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() .

#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);

NOTA : As estruturas de dados usadas na declaração do gancho precisam ser totalmente definidas para garantir a estabilidade da ABI. Caso contrário, não é seguro desreferenciar os ponteiros opacos ou usar a estrutura em contextos dimensionados. O include que fornece a definição completa de tais estruturas de dados deve ficar dentro da seção #ifndef __GENKSYMS__ de drivers/android/vendor_hooks.c . Os arquivos de cabeçalho em include/trace/hooks não devem incluir o arquivo de cabeçalho do kernel com as definições de tipo para evitar alterações de CRC que quebrem o KMI. Em vez disso, declare os tipos.

Anexe aos ganchos do fornecedor

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

Recursos principais do kernel

Se nenhuma das técnicas anteriores permitir implementar 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 Issue Tracker (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 no upstream, a menos que 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 do usuário do fornecedor.
  • nós sysfs. Não adicione novos nós sysfs ao kernel GKI (tais adições são válidas apenas em módulos de fornecedores). Os nós sysfs usados ​​pelas bibliotecas independentes de dispositivo e SoC e o código Java que compreende a estrutura do Android podem ser alterados apenas de maneira compatível 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 pelo espaço do usuário é negado usando o SELinux. Cabe ao fornecedor adicionar os rótulos SELinux apropriados para permitir o acesso do software do fornecedor autorizado.
  • Nós DebugFS. Os módulos do fornecedor podem definir nós em debugfs apenas para depuração (já que debugfs não é montado durante a operação normal do dispositivo).