O Android 10 inclui o daemon do Android Live-Lock
(llkd
), desenvolvido para captar e atenuar os impasses do kernel. O componente llkd
fornece uma implementação independente padrão, mas você também pode
integrar o código llkd
a outro serviço, seja como parte do
loop principal ou como uma linha de execução separada.
Cenários de detecção
O llkd
tem dois cenários de detecção: estado D ou Z persistente e assinatura
de pilha persistente.
Estado D ou Z persistente
Se uma linha de execução estiver no estado D (sono ininterruptível) ou Z (zumbi) sem progresso
de encaminhamento por mais tempo que ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms
, o
llkd
encerrará o processo (ou processo pai). Se uma verificação subsequente mostrar que
o mesmo processo continua existindo, o llkd
confirma uma condição de bloqueio ativo e
causa pânico no kernel de uma maneira que fornece o relatório de bug mais detalhado para a
condição.
O llkd
inclui um watchdog automático que emite um alarme se o llkd
travar. O watchdog é
o dobro do tempo esperado para fluir pelo loop principal, e a amostragem é feita a cada
ro.llk_sample_ms
.
Assinatura de pilha persistente
Para versões de userdebug, o llkd
pode detectar bloqueios ao vivo do kernel usando a verificação
persistente de assinatura de pilha. Se uma linha de execução em qualquer estado, exceto Z, tiver um símbolo de kernel ro.llk.stack
listado persistente que é informado por mais de
ro.llk.timeout_ms
ou ro.llk.stack.timeout_ms
, o llkd
vai encerrar o processo
(mesmo que haja progresso de programação de encaminhamento). Se uma verificação subsequente mostrar que
o mesmo processo continua existindo, o llkd
confirma uma condição de bloqueio ativo e
causa pânico no kernel de uma maneira que fornece o relatório de bug mais detalhado para a
condição.
A verificação lldk
persiste continuamente quando a condição de bloqueio em tempo real existe e
procura as strings compostas symbol+0x
ou symbol.cfi+0x
no
arquivo /proc/pid/stack
no Linux. A lista de símbolos está em ro.llk.stack
e
é definida como a lista separada por vírgulas de
cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
.
Os símbolos precisam ser raros e de curta duração o suficiente para que, em um sistema típico, a
função seja detectada apenas uma vez em uma amostra durante o período de tempo limite de
ro.llk.stack.timeout_ms
(as amostras ocorrem a cada ro.llk.check_ms
). Devido à falta
de proteção ABA, essa é a única maneira de evitar um gatilho falso. A função
do símbolo precisa aparecer abaixo da função que chama a trava que pode competir. Se
a trava estiver abaixo ou na função do símbolo, o símbolo vai aparecer em todos os processos
afetados, não apenas naquele que causou a trava.
Cobertura
A implementação padrão de llkd
não monitora a criação de init
, [kthreadd]
ou
[kthreadd]
. Para que o llkd
cubra linhas de execução geradas com [kthreadd]
:
- Os drivers não podem permanecer em um estado D persistente,
OU
- Os drivers precisam ter mecanismos para recuperar a linha de execução caso ela seja encerrada
externamente. Por exemplo, use
wait_event_interruptible()
em vez dewait_event()
.
Se uma das condições acima for atendida, a lista de negação llkd
poderá ser ajustada para
cobrir os componentes do kernel. A verificação de símbolos da pilha envolve uma lista de
restrições de processo adicional para evitar violações de política de segurança em serviços que bloqueiam operações
ptrace
.
Propriedades do Android
O llkd
responde a várias propriedades do Android (listadas abaixo).
- As propriedades com o nome
prop_ms
estão em milissegundos. - As propriedades que usam o separador de vírgula (,) para listas usam um separador inicial para
preservar a entrada padrão e, em seguida, adicionam ou subtraem entradas com prefixos de adição
(+) e subtração (-) opcionais, respectivamente. Para essas listas, a string
false
é sinônimo de uma lista vazia, e as entradas em branco ou ausentes recorrem ao valor padrão especificado.
ro.config.low_ram
O dispositivo está configurado com memória limitada.
ro.debuggable
O dispositivo está configurado para userdebug ou eng build.
ro.llk.sysrq_t
Se a propriedade for eng
, o padrão não será ro.config.low_ram
nem ro.debuggable
.
Se true
, despeje todas as linhas de execução (sysrq t
).
ro.llk.enable
Permitir que o daemon de bloqueio em tempo real seja ativado. O padrão é false
.
llk.ativar
Avaliar para builds de eng. O padrão é ro.llk.enable
.
ro.khungtask.enable
Permitir que o daemon [khungtask]
seja ativado. O padrão é false
.
khungtask.enable
Avaliar para builds de eng. O padrão é ro.khungtask.enable
.
ro.llk.mlockall
Ative a chamada para mlockall()
. O padrão é false
.
ro.khungtask.timeout
[khungtask]
limite de tempo máximo. O padrão é 12 minutos.
ro.llk.timeout_ms
Limite de tempo máximo D ou Z. O padrão é 10 minutos. Duplique esse valor para definir o
servidor de controle de alarme para llkd
.
ro.llk.D.timeout_ms
D limite de tempo máximo. O padrão é ro.llk.timeout_ms
.
ro.llk.Z.timeout_ms
Z limite de tempo máximo. O padrão é ro.llk.timeout_ms
.
ro.llk.stack.timeout_ms
Verifica o limite de tempo máximo dos símbolos da pilha persistente. O padrão é
ro.llk.timeout_ms
. Ativo apenas em builds userdebug ou eng.
ro.llk.check_ms
Exemplos de linhas de execução para D ou Z. O padrão é de dois minutos.
ro.llk.stack
Verifica se há símbolos de pilha do kernel que, se persistentemente presentes, podem indicar que um
subsistema está bloqueado. O padrão é
cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
,
uma lista separada por vírgulas de símbolos do kernel. A verificação não faz programação avançada
ABA, exceto por sondagem de cada ro.llk_check_ms
no período
ro.llk.stack.timeout_ms
. Portanto, os símbolos da pilha devem ser excepcionalmente raros e
transitórios. É muito improvável que um símbolo apareça de forma persistente em todas
as amostras da pilha. Verifica se há uma correspondência para symbol+0x
ou
symbol.cfi+0x
na expansão de pilha. Disponível apenas em builds de userdebug ou
eng. As preocupações de segurança em builds de usuário resultam em privilégios limitados que
impedem essa verificação.
ro.llk.blacklist.process
O llkd
não monitora os processos especificados. O padrão é 0,1,2
(kernel
, init
e [kthreadd]
) mais nomes de processos init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]
.
Um processo pode ser uma referência comm
, cmdline
ou pid
. Um padrão automatizado
pode ser maior do que o tamanho máximo atual da propriedade, que é 92.
ro.llk.blacklist.parent
O llkd
não monitora processos que têm os pais especificados. O padrão
é 0,2,adbd&[setsid]
(kernel
, [kthreadd]
e adbd
apenas para setsid
zumbificado). Um separador de "e comercial" (&) especifica que o processo pai é ignorado apenas
em combinação com o processo filho de destino. O caractere & foi selecionado porque
nunca faz parte de um nome de processo. No entanto, um setprop
no shell exige que o
caractere & seja escapado ou colocado entre aspas, embora o arquivo init rc
em que ele é
normalmente especificado não tenha esse problema. Um processo pai ou de destino pode ser uma
referência comm
, cmdline
ou pid
.
ro.llk.blacklist.uid
O llkd
não monitora processos que correspondem aos UIDs especificados.
Lista de números ou nomes de UIS separados por vírgulas. O padrão é vazio ou false
.
ro.llk.blacklist.process.stack
O llkd
não monitora o subconjunto especificado de processos para assinaturas de pilha de bloqueio
em tempo real. O padrão é o nome do processo
init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd
. Impede a violação
de sepolicy associada a processos que bloqueiam ptrace
(porque não podem ser
verificados). Ativo apenas em builds userdebug e eng. Para saber mais sobre os tipos
de build, consulte Como criar no Android.
Questões de arquitetura
- As propriedades são limitadas a 92 caracteres. No entanto, isso é ignorado para padrões
definidos no arquivo
include/llkd.h
nas origens. - O daemon
[khungtask]
integrado é muito genérico e falha em códigos de driver que ficam muito tempo no estado D. A mudança para S tornaria as tarefas mortais (e ressuscitáveis pelos drivers, se necessário).
Interface da biblioteca (opcional)
Você pode incorporar o llkd
a outro daemon privilegiado usando
a seguinte interface C do componente libllkd
:
#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
Se um nome de linha de execução for fornecido, uma linha de execução será gerada automaticamente. Caso contrário, o autor da chamada
precisa chamar llkCheckMilliseconds
no loop principal. A função retorna o
período de tempo antes da próxima chamada esperada para esse gerenciador.