A Imagem genérica do Kernel (GKI, na sigla em inglês) reduz a fragmentação do kernel ao se alinhar de perto com o kernel do Linux upstream. No entanto, há motivos válidos para que alguns patches não possam ser aceitos upstream, e há cronogramas de produtos que precisam ser atendidos. Por isso, alguns patches são mantidos nas origens do kernel comum do Android (ACK, na sigla em inglês), em que a GKI é criada.
Os desenvolvedores precisam enviar mudanças de código upstream usando a lista de e-mails do kernel do Linux (LKML, na sigla em inglês) como primeira opção e enviar mudanças de código para a ramificação android-mainline do ACK somente quando houver um motivo forte para que o upstream não seja viável. Confira a seguir exemplos de motivos válidos e como lidar com eles.
O patch foi enviado para a LKML, mas não foi aceito a tempo para o lançamento de um produto. Para lidar com esse patch:
- Forneça evidências de que o patch foi enviado para a LKML e os comentários recebidos para o patch ou um tempo estimado para o envio do patch upstream.
- Decida um curso de ação para inserir o patch no ACK, aprová-lo upstream e, em seguida, removê-lo do ACK quando a versão upstream final for mesclada ao ACK.
O patch define
EXPORT_SYMBOLS_GPL()para um módulo do fornecedor, mas não pode ser enviado 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 o módulo não pode ser enviado upstream e as alternativas consideradas 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 de um produto. Para lidar com esse patch, forneça um tempo estimado para o envio de um patch refatorado upstream (o patch não será aceito no ACK sem um plano para enviar um patch refatorado upstream para análise).
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 em opções para refatorar o patch para que ele possa ser enviado para análise e aceito upstream.
Há muitas outras justificativas possíveis. Ao enviar o bug ou o patch, inclua uma justificativa válida e espere alguma iteração e discussão. Reconhecemos que o ACK contém alguns patches, especialmente nas fases iniciais da GKI, enquanto todos estão aprendendo a trabalhar upstream, mas não podem relaxar os cronogramas de produtos para fazer isso. Espere que os requisitos de upstream se tornem mais rigorosos com o tempo.
Requisitos de patch
Os patches precisam estar em conformidade com os padrões de codificação do kernel do Linux descritos na
árvore de origem do Linux,
sejam eles enviados upstream ou para o 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 mais detalhes, consulte
build/kernel/kleaf/docs/checkpatch.md.
Patches do ACK
Os patches enviados ao ACK precisam estar em conformidade com os padrões de codificação do kernel do Linux e
as diretrizes de contribuição.
É necessário incluir uma Change-Id
tag na mensagem de commit. Se você enviar o patch para várias ramificações (por
exemplo, android-mainline e android12-5.4), use o mesmo
Change-Id para todas as instâncias do patch.
Envie patches para a LKML primeiro para uma análise upstream. Se o patch for:
- Aceito upstream, ele será mesclado automaticamente em
android-mainline. - Não aceito upstream, envie-o para
android-mainlinecom uma referência ao envio upstream ou uma explicação de por que ele não foi enviado para a LKML.
Depois que um patch é aceito upstream ou em android-mainline, ele pode ser transferido para o ACK apropriado baseado em LTS (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 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 é transferido 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 aos kernels do ACK se enquadram nos seguintes grupos (listados em ordem de probabilidade de serem aceitos).
UPSTREAM:- Os patches escolhidos em 'android-mainline` provavelmente serão aceitos no ACK se houver um caso de uso razoável.BACKPORT:- Os patches upstream que não são escolhidos de forma limpa e precisam de modificação também provavelmente serão aceitos se houver um caso de uso razoável.FROMGIT:- Os patches escolhidos em uma ramificação de mantenedor em preparação para envio ao Linux mainline poderão ser aceitos se houver um prazo próximo. Eles precisam ser justificados para conteúdo e programação.FROMLIST:- Os patches que foram enviados para a 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, seja ou não no Linux upstream (presumimos que não será). É necessário que haja um problema associado aos patchesFROMLISTpara facilitar a discussão com a equipe do kernel do Android.
Patches específicos do Android
Se você não conseguir fazer as mudanças necessárias upstream, tente 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 o motivo pelo qual o patch não pode ser enviado upstream (consulte a lista anterior para exemplos).
No entanto, há alguns casos em que o código não pode ser enviado upstream. Esses
casos são abordados da seguinte maneira e precisam seguir as diretrizes
de contribuição
para patches específicos do Android e ser marcados com o prefixo ANDROID: no
assunto.
Mudanças no gki_defconfig
Todas as mudanças de CONFIG em gki_defconfig precisam ser aplicadas às versões arm64 e x86, a menos que a CONFIG seja específica da arquitetura. Para solicitar uma mudança em uma configuração CONFIG, crie um problema na TI para discutir a mudança. Qualquer mudança de CONFIG que afete a interface do módulo do kernel (KMI, na sigla em inglês) depois que ela for congelada será rejeitada. Nos casos em que os parceiros solicitam configurações conflitantes para uma única configuração, resolvemos os conflitos por meio de discussão sobre os bugs relacionados.
Código que não existe upstream
As modificações no código que já é específico 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 no bug e no patch sobre 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 mudanças nessa categoria são atualizações de arquivos de representação de KMI, listas de símbolos de KMI, gki_defconfig, scripts de build ou configuração ou outros scripts que não existem upstream.
Módulos fora da árvore
O Linux upstream desencoraja ativamente o suporte para a criação de módulos fora da árvore. Essa é uma posição razoável, já que os mantenedores do Linux não fazem garantias sobre a origem ou a compatibilidade binária no kernel e não querem oferecer suporte a códigos que não estão na árvore. No entanto, a GKI faz garantias de ABI para módulos de fornecedores, garantindo que as interfaces KMI sejam estáveis durante o tempo de vida útil com suporte de um kernel. Portanto, há uma classe de mudanças para oferecer suporte a módulos de fornecedores que são aceitáveis para o ACK, mas não para o upstream.
Por exemplo, considere um patch que adiciona macros EXPORT_SYMBOL_GPL() em que os módulos que usam a exportação não estão na árvore de origem. Embora seja necessário 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 que o módulo não seja enviado upstream, você poderá enviar o patch para o ACK. É necessário incluir a justificativa de por que 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 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 está 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 seja selecionada automaticamente com base na configuração do kernel CONFIG_GKI_HACKS_TO_FIX, que está ativada em gki_defconfig. Use esse mecanismo apenas para configurações ocultas. Se a configuração não estiver oculta, ela precisará ser especificada em gki_defconfig de forma explícita ou como uma dependência.
Governadores carregáveis
Para frameworks de kernel (como cpufreq) que oferecem suporte a governadores carregáveis, é possível substituir o governador padrão (como o governador schedutil do cpufreq). Para
frameworks (como o framework térmico) que não oferecem suporte a governadores
ou drivers carregáveis, mas ainda exigem uma implementação específica do fornecedor, crie um problema
na TI e consulte a equipe do kernel do Android.
Vamos trabalhar com você e os mantenedores upstream para adicionar o suporte necessário.
Hooks do fornecedor
Em versões anteriores, era possível adicionar modificações específicas do fornecedor diretamente ao kernel principal. Isso não é possível com a GKI 2.0 porque o código específico do produto precisa ser implementado em módulos e não será aceito nos kernels principais upstream ou no ACK. Para ativar recursos de valor agregado em que os parceiros confiam com o mínimo de impacto 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 hooks de fornecedores vêm em duas variantes (normal e restrita) com base em tracepoints (não eventos de rastreamento) que os módulos de fornecedores podem anexar. 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 hook em do_exit() que um módulo de fornecedor pode anexar para processamento. Um exemplo de implementação inclui os seguintes hooks de fornecedores.
- Os hooks de fornecedores normais usam
DECLARE_HOOK()para criar uma função de tracepoint com o nometrace_nameem quenameé o identificador exclusivo do rastreamento. Por convenção, os nomes de hooks de fornecedores normais começam comandroid_vh. Portanto, o nome do hooksched_exit()seriaandroid_vh_sched_exit. - Os hooks de fornecedores restritos são necessários para casos como hooks de agendador em que a função anexada precisa ser chamada mesmo que a CPU esteja off-line ou exija um contexto não atômico. Os hooks de fornecedores restritos não podem ser desanexados. Portanto, os módulos anexados a um hook restrito nunca podem ser descarregados. Os nomes de hooks de fornecedores restritos começam com
android_rvh.
Para adicionar um hook de fornecedor, registre um problema na TI e envie patches (como acontece com todos os patches específicos do Android, um problema precisa existir e você precisa fornecer uma justificativa). O suporte a hooks de fornecedores está apenas no ACK. Portanto, não envie esses patches para o Linux upstream.
Adicionar campos de fornecedores a estruturas
É possível associar dados do fornecedor a estruturas de dados importantes adicionando campos android_vendor_data usando as macros 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 os campos necessários para fornecedores e os campos necessários para OEMs, os OEMs nunca devem usar campos declarados usando macros ANDROID_VENDOR_DATA(). 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 hooks de fornecedores
Adicione hooks de fornecedores ao código do kernel como tracepoints declarando-os usando DECLARE_HOOK() ou DECLARE_RESTRICTED_HOOK() e, em seguida, adicione-os ao código como um tracepoint. Por exemplo, para adicionar trace_android_vh_sched_exit() à função de kernel do_exit() atual:
#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 gerenciador usando register_trace_android_vh_sched_exit(), a função registrada será chamada. O gerenciador precisa estar ciente do contexto em relação aos bloqueios mantidos, ao estado do RCS e a outros fatores. O hook precisa ser definido em um arquivo principal no diretório include/trace/hooks.
Por exemplo, o código a seguir oferece 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 do fornecedor, adicione o arquivo principal com a declaração do hook a drivers/android/vendor_hooks.c e exporte os símbolos. Por exemplo, o código a seguir conclui a declaração do 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 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 que fornece a definição completa dessas estruturas de dados precisa estar 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 mudanças de CRC que quebram a KMI. Em vez disso, encaminhe os tipos de declaração.
Anexar a hooks de fornecedores
Para usar hooks de fornecedores, o módulo de fornecedor precisa registrar um gerenciador para o hook (geralmente feito durante a inicialização do módulo). Por exemplo, o código a seguir mostra o gerenciador 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);
...
}
Usar hooks de fornecedores de arquivos de cabeçalho
Para usar hooks de fornecedores de arquivos principais, talvez seja necessário atualizar o arquivo principal do hook de fornecedor para cancelar a definição de TRACE_INCLUDE_PATH para evitar erros de build que indicam que um arquivo principal de ponto de trace 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 arquivo principal do hook de fornecedor que você está incluindo. 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
A definição de UNDEF_TRACE_INCLUDE_PATH informa include/trace/define_trace.h para
cancelar a definição de TRACE_INCLUDE_PATH após a criação dos tracepoints.
Recursos principais do kernel
Se nenhuma das técnicas anteriores permitir que você implemente um recurso de um módulo, adicione o recurso como uma modificação específica do Android ao kernel principal. Crie um problema no Issue Tracker (IT) para iniciar a conversa.
Interface de programação de aplicativos do usuário (UAPI, na sigla em inglês)
- Arquivos de cabeçalho da UAPI. As mudanças nos arquivos de cabeçalho da UAPI precisam ocorrer upstream, a menos que as mudanças sejam em interfaces específicas do Android. Use arquivos de cabeçalho específicos do fornecedor para definir interfaces entre módulos de fornecedores e código de espaço do usuário do fornecedor.
- Nós sysfs. Não adicione novos nós sysfs ao kernel da GKI. Essas adições são válidas apenas em módulos de fornecedores. Os nós sysfs usados pelas bibliotecas e pelo código Java agnósticos do SoC e do dispositivo que compõem o framework do Android só podem ser alterados de maneiras compatíveis e precisam 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 do 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 pelo software autorizado do fornecedor.
- Nós DebugFS. Os módulos de fornecedores podem definir nós em
debugfsapenas para depuração (já quedebugfsnão é montado durante a operação normal do dispositivo).