Pianificazione della qualità del servizio in SDV

Il framework di pianificazione della qualità del servizio (QoS) di SDV fornisce un'allocazione deterministica delle risorse CPU per i bundle di servizi gestiti da Lifecycle Manager (LM). Astrattendo gli attributi di pianificazione Linux di basso livello (Policy, Priority, Nice) in preset logici, la piattaforma consente una netta separazione tra lo sviluppo dei servizi e l'ottimizzazione del sistema a livello di veicolo. Ciò contribuisce a fornire la larghezza di banda di calcolo necessaria per i carichi di lavoro mission critical e sensibili al tempo, mentre il sistema limita le attività in background per evitare l'instabilità.

Ruoli e responsabilità

Il modello di pianificazione SDV segue un pattern di responsabilità delegata per garantire che i servizi rimangano portatili, consentendo al contempo all'OEM di ottimizzare il sistema.

Ruolo Responsabilità Prodotto principale
Piattaforma SDV (Google) Definisce lo schema sdv_service_bundles_scheduling.proto, implementa la logica di applicazione LM e fornisce la libreria client sdv_service_bundles_scheduling. Definizioni Protobuf e librerie della piattaforma
OEM (integratore di sistemi) Definisce i valori concreti per ogni preset, ad esempio cosa significa ELEVATED su hardware specifico, e controlla la configurazione a livello di sistema. /product/etc/lifecycle_config.textproto
Sviluppatore di servizi Seleziona il nome del preset logico appropriato e registra il percorso scheduling_config.textproto nel manifest del bundle di servizi. scheduling_config.textproto e sdv_service_bundles_manifest.textproto

Flusso di lavoro tecnico

  1. Integrazione della piattaforma: l'OEM definisce lifecycle_config.textproto specifico per il veicolo. Questo file stabilisce i profili di pianificazione a livello di sistema (preset) e li mappa agli attributi di pianificazione Linux concreti in base all'hardware di destinazione.
  2. Sviluppo del bundle di servizi: lo sviluppatore raggruppa un scheduling_config.textproto all'interno del pacchetto APEX, consigliando un preset logico (ad esempio ELEVATED) e definendo eventuali nomi di thread interni.
  3. Integrazione del bundle di servizi: durante la fase di integrazione del veicolo, l'OEM esamina il preset consigliato dallo sviluppatore. L'OEM può mantenere il profilo consigliato o sostituirlo, ad esempio eseguire il downgrade di un servizio ELEVATED a NORMAL, per migliorare la stabilità complessiva del sistema prima di firmare l'APEX.
  4. Risoluzione degli attributi di runtime: quando viene avviato un servizio, LM identifica il preset autorizzato dal manifest del servizio e recupera gli attributi Linux corrispondenti dalla configurazione di sistema dell'OEM.
  5. Sincronizzazione e applicazione all'avvio: LM applica gli attributi risolti al processo del servizio prima di segnalare di continuare l'esecuzione utilizzando il protocollo signal-and-continue.

Concetti principali

I seguenti concetti sono fondamentali per il framework di pianificazione SDV.

Preset di pianificazione

I preset di pianificazione sono il meccanismo principale per la gestione della QoS in SDV. Anziché definire i parametri di pianificazione Linux non elaborati, gli sviluppatori fanno riferimento ai preset predefiniti per nome. In fase di runtime, LM mappa questi nomi logici agli attributi di pianificazione Linux concreti.

I seguenti preset sono configurati come esempio in lifecycle_config.textproto:

Nome preset Policy di pianificazione Valore nice tipico Caso d'uso tipico
NORMAL SCHED_OTHER 0 Servizi standard (HVAC, media, impostazioni)
ELEVATED SCHED_OTHER -10 Componenti e infrastruttura di sistema principali
IDLE SCHED_IDLE 19 Analisi in background e logging non critico
CUSTOM Definito dall'utente -20 (iniziale) Servizi che richiedono policy o affinità in tempo reale

Quando applica un preset, LM utilizza la chiamata di sistema setpriority per impostare il valore nice a livello di processo. Ciò influisce sulla quota di CPU relativa che il processo riceve durante la contesa con lo scheduler CFS (Completely Fair Scheduler) predefinito di Linux.

Privilegi e sicurezza

Per impedire l'escalation non autorizzata della priorità, la piattaforma SDV utilizza un modello di sicurezza a livelli basato su domini SELinux e funzionalità Linux.

Domini di sicurezza

  • untrusted_service_bundle: il dominio predefinito per i preset standard (NORMAL, ELEVATED, IDLE). Il kernel limita i processi in questo dominio, impedendo loro di modificare i propri parametri di pianificazione.
  • priority_service_bundle: concesso a qualsiasi bundle di servizi che utilizza un preset in cui is_privileged: true è impostato in lifecycle_config.textproto a livello di sistema. La transizione a questo dominio concede al processo la funzionalità CAP_SYS_NICE, consentendogli di gestire la propria allocazione delle risorse.

Funzionalità CAP_SYS_NICE

La piattaforma concede la funzionalità CAP_SYS_NICE ai processi nel dominio priority_service_bundle. Questa autorizzazione consente a un processo di eseguire le seguenti operazioni:

  • Aumentare il proprio valore nice oltre l'assegnazione iniziale.
  • Modificare la policy di pianificazione in classi in tempo reale come SCHED_FIFO o SCHED_RR.
  • Impostare l'affinità della CPU utilizzando sched_setaffinity.
  • Configurare i parametri di scadenza specializzati per SCHED_DEADLINE.

La limitazione di questa funzionalità a un dominio dedicato protegge l'equilibrio della pianificazione a livello di sistema limitando le interruzioni ai servizi autorizzati in modo esplicito.

Guida alla configurazione

Questa sezione fornisce indicazioni sulla configurazione sia per gli OEM sia per gli sviluppatori di servizi.

Guida per gli OEM: preset a livello di sistema

L'OEM definisce i profili di pianificazione in /product/etc/lifecycle_config.textproto. La piattaforma fornisce un esempio in lifecycle_management/config/lifecycle_config.textproto che gli OEM devono modificare in base all'hardware del veicolo.

Esempio: definire un preset in tempo reale

Un OEM potrebbe definire un preset CRITICAL per i servizi correlati alla sicurezza che non devono mai essere preempted dalle applicazioni standard:

# lifecycle_config.textproto
scheduling_presets {
  name: "CRITICAL"
  is_privileged: true
  thread_scheduling_configuration {
    policy: 1      # SCHED_FIFO
    priority: 80   # High real-time priority
  }
}

Guida per gli sviluppatori: configurazione del bundle di servizi

Gli sviluppatori di servizi consigliano un preset e, facoltativamente, definiscono i nomi dei thread logici in un file scheduling_config.textproto. Per essere rilevato dalla piattaforma, il percorso di questo file deve essere registrato in sdv_service_bundles_manifest.textproto.

Esempio: registrazione del manifest

# sdv_service_bundles_manifest.textproto
service_bundle_entries {
  name: "SensorService"
  scheduling_config_path: "configs/scheduling_config.textproto"
}

Esempio: utilizzare un preset standard

Per la maggior parte dei servizi, è sufficiente fare riferimento a un nome di preset in scheduling_config.textproto:

# configs/scheduling_config.textproto
scheduling_preset_name: "ELEVATED"

Esempio: pianificazione per i thread worker generati dal servizio

Uno sviluppatore di servizi potrebbe creare thread worker aggiuntivi nel codice per gestire attività specializzate, ad esempio un loop di dati dei sensori a bassa latenza. Lo sviluppatore può definire gli attributi di pianificazione denominati per questi thread interni nel file di configurazione.

Utilizza l'applicazione manuale solo nei servizi con privilegi che utilizzano questi metadati per impostare le proprietà dei thread worker interni:

# configs/scheduling_config.textproto
scheduling_preset_name: "CUSTOM"

# Map of logical thread names to their attributes (propagated as metadata)
thread_scheduling_configuration {
  key: "sensor-processing-thread"
  value {
    policy: 1        # SCHED_FIFO
    priority: 50
    cpu_affinity_ids: [2, 3]  # Pin to specific cores
  }
}

Comportamento e applicazione del sistema

La piattaforma SDV utilizza un modello di applicazione rigoroso per attivare le configurazioni di pianificazione prima di eseguire qualsiasi codice specifico del servizio.

Sincronizzazione all'avvio

Per impedire l'esecuzione di un servizio con una priorità errata (anche durante la fase di inizializzazione), LM e Service Bundle Runner (SBR) utilizzano un protocollo di sincronizzazione signal-and-continue:

  1. Creazione del processo: LM esegue il fork del processo SBR. A questo punto, il processo figlio SBR è in esecuzione, ma entra immediatamente in uno stato bloccato, in attesa di un segnale sul relativo stdin.
  2. Applicazione degli attributi: mentre il figlio è bloccato, LM utilizza la chiamata di sistema setpriority per applicare il valore nice richiesto e configura la policy di pianificazione (ad esempio SCHED_IDLE).
  3. Transizione di sicurezza: LM esegue la transizione del dominio SELinux, spostando il figlio nel dominio untrusted_service_bundle o priority_service_bundle.
  4. Segnale: solo dopo che tutti i parametri sono stati applicati correttamente, LM invia un segnale di un byte a stdin del figlio.
  5. Esecuzione: SBR riceve il segnale e inizia a caricare le librerie di servizi e a chiamare i metodi del ciclo di vita.

Impatto sul thread principale e sul ciclo di vita

Sincronizzando prima di caricare le librerie di servizi, la piattaforma mantiene la priorità richiesta per il thread di esecuzione principale in tutti i callback del ciclo di vita:

  • onCreate: tutta l'iniezione delle dipendenze e l'allocazione iniziale delle risorse avvengono con la priorità corretta.
  • onStart: il preset regola la transizione allo stato attivo e tutti i loop di lavoro iniziali.
  • onStop e onDestroy: la piattaforma esegue le operazioni di pulizia con la stessa priorità per evitare di bloccare le attività di sistema critiche durante l'arresto.

Pool di thread Binder ed ereditarietà della priorità

I thread nel pool di thread Binder gestito dalla piattaforma non eseguono il lavoro con la priorità del thread che ha creato il pool. Al contrario, per le transazioni sincrone, il thread del server eredita la priorità del chiamante. Il driver del kernel Binder controlla questo meccanismo di ereditarietà della priorità.

Ottimizzazione a livello di thread

I bundle di servizi che richiedono un controllo granulare (ad esempio, i loop di elaborazione in tempo reale) devono applicare manualmente le configurazioni ai thread worker. Per applicare le configurazioni a livello di thread:

  1. Richiedi un preset con privilegi (in cui is_privileged: true è impostato).
  2. Utilizza la funzione get_scheduling_configuration della libreria sdv_service_bundles_scheduling per leggere thread_scheduling_configuration.
  3. Applica gli attributi ai thread worker utilizzando sched_setattr.

Applicazione di SELinux e delle funzionalità

La piattaforma utilizza SELinux e la funzionalità Linux CAP_SYS_NICE per applicare i preset di pianificazione:

  • Per i servizi che utilizzano preset come NORMAL o IDLE, la pianificazione viene configurata da LM durante l'avvio. Questi servizi non hanno la funzionalità CAP_SYS_NICE e non possono modificare la priorità o la policy.
  • Ai servizi che utilizzano un preset con privilegi (is_privileged: true) viene concessa la funzionalità CAP_SYS_NICE. Ciò consente loro di controllare manualmente la pianificazione dei thread interni, come richiesto per le attività in tempo reale.

Verifica ed esempi

La verifica del comportamento di pianificazione richiede una combinazione di analisi dei log e misurazione del rendimento a livello di sistema. La piattaforma SDV fornisce un esempio dedicato per illustrare queste tecniche.

L'esempio di pianificazione QoS

Questo esempio, che si trova in samples/qos_scheduling, include diversi servizi di test del rendimento progettati per essere eseguiti in parallelo e segnalare i loro progressi.

  • PerformanceTesterNormal: viene eseguito con il preset NORMAL.
  • PerformanceTesterElevated: viene eseguito con il preset ELEVATED.
  • PerformanceTesterRealtime: utilizza il preset CUSTOM per applicare SCHED_FIFO ai thread worker interni.
  • PolicyOffender: un servizio di diagnostica che tenta di impostare una priorità in tempo reale utilizzando un preset NORMAL. Viene utilizzato per verificare che la piattaforma blocchi correttamente l'escalation non autorizzata.

Esegui la suite di verifica

  1. Attiva la configurazione di orchestrazione specifica per QoS:

    adb shell setprop persist.sdv.orchestrator_config_path /etc/orch/vm_qos_scheduling_orch_config.textproto

  2. Riavvia il dispositivo per avviare i servizi nei rispettivi domini di pianificazione:

    adb reboot

  3. Filtra i log per visualizzare i risultati comparativi del rendimento:

    adb logcat | grep sdv_sample_qos_common

    Il servizio PerformanceTesterElevated segnala un numero di unità di lavoro completate significativamente più elevato rispetto a PerformanceTesterNormal. I log del servizio PolicyOffender includono gli errori di negazione EPERM o SELinux.

Compatibilità con le versioni precedenti

  • Per la compatibilità con le versioni precedenti, qualsiasi bundle di servizi legacy che specifica un scheduling_config_path nel manifest, ma non specifica un scheduling_preset_name nel file di configurazione, viene trattato automaticamente come un servizio con privilegi. In questo modo vengono mantenute le funzionalità necessarie (ad esempio CAP_SYS_NICE) per i servizi precedenti che si basano sull'ottimizzazione manuale dei thread.
  • Il messaggio di configurazione principale DeadlineSchedulingConfiguration verrà rinominato in una release futura. Il nome attuale è storico e non riflette più con precisione il fatto che il messaggio gestisce tutti i tipi di pianificazione, non solo la pianificazione delle scadenze.