Planification de la qualité de service dans SDV

Le framework de planification de la qualité de service (QoS) SDV fournit une allocation déterministe des ressources de processeur pour les bundles de services gérés par le gestionnaire de cycle de vie (LM). En abstrayant les attributs de planification Linux de bas niveau (stratégie, priorité, nice) dans des préréglages logiques, la plate-forme permet une séparation claire entre le développement de services et le réglage du système à l'échelle du véhicule. Cela permet de fournir la bande passante de calcul nécessaire aux charges de travail critiques pour la sécurité et sensibles au temps, tandis que le système limite les tâches en arrière-plan pour éviter l'instabilité.

Rôles et responsabilités

Le modèle de planification SDV suit un modèle de responsabilité déléguée pour s'assurer que les services restent portables tout en permettant à l'OEM de régler le système.

Rôle Responsabilité Livrable clé
Plate-forme SDV (Google) Définit le schéma sdv_service_bundles_scheduling.proto, implémente la logique d'application LM et fournit la bibliothèque cliente sdv_service_bundles_scheduling. Définitions Protobuf et bibliothèques de plate-forme
OEM (intégrateur système) Définit les valeurs concrètes pour chaque préréglage, par exemple ce que signifie ELEVATED sur un matériel spécifique, et contrôle la configuration à l'échelle du système. /product/etc/lifecycle_config.textproto
Développeur de services Sélectionne le nom de préréglage logique approprié et enregistre le chemin d'accès scheduling_config.textproto dans le fichier manifeste du bundle de services. scheduling_config.textproto et sdv_service_bundles_manifest.textproto

Workflow technique

  1. Intégration de la plate-forme : l'OEM définit le lifecycle_config.textproto spécifique au véhicule. Ce fichier établit les profils de planification (préréglages) à l'échelle du système et les mappe à des attributs de planification Linux concrets en fonction du matériel cible.
  2. Développement de bundles de services : le développeur regroupe un scheduling_config.textproto dans son package APEX, en recommandant un préréglage logique (par exemple, ELEVATED) et en définissant les noms de threads internes.
  3. Intégration du bundle de services : lors de la phase d'intégration du véhicule, l'OEM examine le préréglage recommandé par le développeur. L'OEM peut conserver le profil recommandé ou le remplacer, par exemple en rétrogradant un service ELEVATED en NORMAL, pour améliorer la stabilité globale du système avant de signer l'APEX.
  4. Résolution des attributs d'exécution : lorsqu'un service est démarré, le LM identifie le préréglage autorisé à partir du fichier manifeste du service et récupère les attributs Linux correspondants à partir de la configuration système de l'OEM.
  5. Synchronisation et application au démarrage : le LM applique les attributs résolus au processus de service avant de lui signaler de poursuivre l'exécution à l'aide du protocole de signalisation et de poursuite.

Concepts fondamentaux

Les concepts suivants sont au cœur du framework de planification SDV.

Préréglages de programmation

Les préréglages de planification sont le principal mécanisme de gestion de la qualité de service dans SDV. Au lieu de définir des paramètres de planification Linux bruts, les développeurs font référence à des préréglages prédéfinis par leur nom. Lors de l'exécution, le LM mappe ces noms logiques à des attributs de planification Linux concrets.

Voici les préréglages configurés à titre d'exemple dans lifecycle_config.textproto :

Nom du préréglage Règle de planification Valeur Nice typique Cas d'utilisation typique
NORMAL SCHED_OTHER 0 Services standards (CVC, multimédia, paramètres)
ELEVATED SCHED_OTHER -10 Composants et infrastructure du système principal
IDLE SCHED_IDLE 19 Analytics en arrière-plan et journalisation non critique
CUSTOM Valeur personnalisée -20 (initial) Services nécessitant des règles ou une affinité en temps réel

Lors de l'application d'un préréglage, le LM utilise l'appel système setpriority pour définir la valeur nice à l'échelle du processus. Cela affecte la part relative de processeur que le processus reçoit en cas de conflit sous le planificateur CFS (Completely Fair Scheduler) Linux par défaut.

Privilèges et sécurité

Pour éviter toute escalade de priorité non autorisée, la plate-forme SDV utilise un modèle de sécurité à plusieurs niveaux basé sur les domaines SELinux et les capacités Linux.

Domaines de sécurité

  • untrusted_service_bundle : domaine par défaut pour les préréglages standards (NORMAL, ELEVATED, IDLE). Le noyau limite les processus de ce domaine, les empêchant de modifier leurs propres paramètres de planification.
  • priority_service_bundle : accordé à tout bundle de services utilisant un préréglage où is_privileged: true est défini dans lifecycle_config.textproto à l'échelle du système. La transition vers ce domaine accorde au processus la capacité CAP_SYS_NICE et lui permet de gérer sa propre allocation de ressources.

CAP_SYS_NICE

La plate-forme accorde la fonctionnalité CAP_SYS_NICE aux processus du domaine priority_service_bundle. Cette autorisation permet à un processus d'effectuer les opérations suivantes :

  • Augmenter sa propre valeur nice au-delà de son attribution initiale.
  • Modifiez sa règle de planification pour les cours en temps réel, comme SCHED_FIFO ou SCHED_RR.
  • Définissez l'affinité du processeur à l'aide de sched_setaffinity.
  • Configurez des paramètres de délai spécialisés pour SCHED_DEADLINE.

En limitant cette fonctionnalité à un domaine dédié, vous protégez l'équilibre de la planification à l'échelle du système en limitant les perturbations aux services explicitement autorisés.

Guide de configuration

Cette section fournit des conseils de configuration aux OEM et aux développeurs de services.

Guide de l'OEM : préréglages à l'échelle du système

L'OEM définit les profils de programmation dans /product/etc/lifecycle_config.textproto. La plate-forme fournit un exemple à l'adresse lifecycle_management/config/lifecycle_config.textproto que les OEM doivent ajuster en fonction du matériel du véhicule.

Exemple : Définir un préréglage en temps réel

Un OEM peut définir un préréglage CRITICAL pour les services liés à la sécurité qui ne doivent jamais être interrompus par des applications standards :

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

Guide du développeur : configuration du bundle de services

Les développeurs de services recommandent un préréglage et définissent éventuellement des noms de threads logiques dans un fichier scheduling_config.textproto. Pour que la plate-forme puisse le trouver, le chemin d'accès à ce fichier doit être enregistré dans sdv_service_bundles_manifest.textproto.

Exemple : Enregistrement du fichier manifeste

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

Exemple : Utiliser un préréglage standard

Pour la plupart des services, il suffit de référencer un nom prédéfini dans scheduling_config.textproto :

# configs/scheduling_config.textproto
scheduling_preset_name: "ELEVATED"

Exemple : Planification des threads de worker générés par le service

Un développeur de services peut créer des threads de nœud de calcul supplémentaires dans son code pour gérer des tâches spécialisées, comme une boucle de données de capteur à faible latence. Le développeur peut définir des attributs de planification nommés pour ces threads internes dans le fichier de configuration.

N'utilisez l'application manuelle que dans les services privilégiés qui utilisent ces métadonnées pour définir les propriétés de leurs threads de worker internes :

# 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
  }
}

Comportement et application du système

La plate-forme SDV utilise un modèle d'application strict pour activer les configurations de planification avant d'exécuter tout code spécifique au service.

Synchronisation au démarrage

Pour empêcher un service de s'exécuter à une priorité incorrecte (même pendant sa phase d'initialisation), le LM et le Service Bundle Runner (SBR) utilisent un protocole de synchronisation signal-and-continue :

  1. Création du processus : le LM duplique le processus SBR. À ce stade, le processus enfant SBR est en cours d'exécution, mais passe immédiatement à l'état bloqué, en attente d'un signal sur son stdin.
  2. Application des attributs : lorsque l'enfant est bloqué, le LM utilise l'appel système setpriority pour appliquer la valeur nice demandée et configure la règle de planification (par exemple, SCHED_IDLE).
  3. Transition de sécurité : le LM effectue la transition de domaine SELinux, en déplaçant l'enfant vers le domaine untrusted_service_bundle ou priority_service_bundle.
  4. Signal : Ce n'est qu'une fois que tous les paramètres ont été appliqués avec succès que le LM envoie un signal d'un octet au stdin de l'enfant.
  5. Exécution : le SBR reçoit le signal et commence à charger les bibliothèques de services et à appeler les méthodes de cycle de vie.

Impact sur le thread principal et le cycle de vie

En synchronisant avant de charger les bibliothèques de services, la plate-forme maintient la priorité demandée pour le thread d'exécution principal dans tous les rappels de cycle de vie :

  • onCreate : l'injection de dépendances et l'allocation initiale des ressources se produisent à la priorité appropriée.
  • onStart : le préréglage régit la transition vers l'état actif et toutes les boucles de travail initiales.
  • onStop et onDestroy : la plate-forme effectue des opérations de nettoyage à la même priorité pour éviter de priver les activités système critiques lors de l'arrêt.

Pool de threads Binder et héritage de priorité

Les threads du pool de threads Binder géré par la plate-forme n'exécutent pas le travail avec la priorité du thread qui a créé le pool. En revanche, pour les transactions synchrones, le thread du serveur hérite de la priorité de l'appelant. Le pilote de noyau Binder contrôle ce mécanisme d'héritage de priorité.

Affinage au niveau du thread

Les bundles de services nécessitant un contrôle précis (par exemple, les boucles de traitement en temps réel) doivent appliquer manuellement les configurations à leurs threads de nœud de calcul. Pour appliquer des configurations au niveau du thread, procédez comme suit :

  1. Demandez un préréglage privilégié (où is_privileged: true est défini).
  2. Utilisez la fonction get_scheduling_configuration de la bibliothèque sdv_service_bundles_scheduling pour lire thread_scheduling_configuration.
  3. Appliquez des attributs aux threads de nœud de calcul à l'aide de sched_setattr.

Application de SELinux et des fonctionnalités

La plate-forme utilise SELinux et la fonctionnalité Linux CAP_SYS_NICE pour appliquer les préréglages de planification :

  • Pour les services utilisant des préréglages tels que NORMAL ou IDLE, la planification est configurée par le LM au démarrage. Ces services ne disposent pas de la fonctionnalité CAP_SYS_NICE et ne peuvent pas modifier leur priorité ni leur règle.
  • Les services utilisant un préréglage privilégié (is_privileged: true) bénéficient de la fonctionnalité CAP_SYS_NICE. Cela leur permet de contrôler manuellement la planification de leurs threads internes, comme l'exigent les tâches en temps réel.

Validation et exemples

Pour vérifier le comportement de planification, vous devez combiner l'analyse des journaux et la mesure des performances au niveau du système. La plate-forme SDV fournit un exemple dédié pour illustrer ces techniques.

Exemple de planification QoS

Situé dans samples/qos_scheduling, cet exemple inclut plusieurs services de test des performances conçus pour s'exécuter en parallèle et rendre compte de leur progression.

  • PerformanceTesterNormal : s'exécute avec le préréglage NORMAL.
  • PerformanceTesterElevated : s'exécute avec le préréglage ELEVATED.
  • PerformanceTesterRealtime : utilise le préréglage CUSTOM pour appliquer SCHED_FIFO à ses threads de worker internes.
  • PolicyOffender : service de diagnostic qui tente de définir une priorité en temps réel lors de l'utilisation d'un préréglage NORMAL. Cela permet de vérifier que la plate-forme bloque bien les escalades non autorisées.

Exécuter la suite de validation

  1. Activez la configuration d'orchestration spécifique à la qualité de service :

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

  2. Redémarrez l'appareil pour démarrer les services dans leurs domaines de planification respectifs :

    adb reboot

  3. Filtrez les journaux pour afficher les résultats comparatifs des performances :

    adb logcat | grep sdv_sample_qos_common

    Le service PerformanceTesterElevated signale un nombre d'unités de travail effectuées nettement plus élevé que PerformanceTesterNormal. Les erreurs EPERM ou de refus SELinux sont incluses dans les journaux du service PolicyOffender.

Rétrocompatibilité.

  • Pour assurer la rétrocompatibilité, tout ancien bundle de services qui spécifie un scheduling_config_path dans son fichier manifeste, mais ne spécifie pas de scheduling_preset_name dans le fichier de configuration, est automatiquement traité comme un service privilégié. Cela préserve les capacités nécessaires (par exemple, CAP_SYS_NICE) pour les anciens services qui s'appuient sur le réglage manuel des threads.
  • Le message de configuration racine DeadlineSchedulingConfiguration doit être renommé dans une prochaine version. Le nom actuel est historique et ne reflète plus précisément le fait que le message gère tous les types de programmation, et pas seulement la programmation des échéances.