Programación de la calidad de servicio en SDV

El marco de trabajo de programación de calidad de servicio (QoS) de SDV proporciona una asignación determinista de recursos de CPU para los paquetes de servicios administrados por Lifecycle Manager (LM). Al abstraer los atributos de programación de Linux de bajo nivel (política, prioridad, nice) en ajustes predeterminados lógicos, la plataforma permite una separación clara entre el desarrollo de servicios y el ajuste del sistema en todo el vehículo. Esto ayuda a proporcionar el ancho de banda de procesamiento necesario para las cargas de trabajo críticas para la seguridad y sensibles al tiempo, mientras que el sistema restringe las tareas en segundo plano para evitar la inestabilidad.

Funciones y responsabilidades

El modelo de programación de SDV sigue un patrón de responsabilidad delegada para ayudar a garantizar que los servicios sigan siendo portátiles y, al mismo tiempo, permitir que el OEM ajuste el sistema.

Rol Responsabilidad Entrega clave
Plataforma de SDV (Google) Define el esquema sdv_service_bundles_scheduling.proto, implementa la lógica de aplicación de LM y proporciona la biblioteca cliente sdv_service_bundles_scheduling. Definiciones de Protobuf y bibliotecas de plataformas
OEM (integrador de sistemas) Define los valores concretos para cada ajuste predeterminado, como lo que significa ELEVATED en hardware específico, y controla la configuración en todo el sistema. /product/etc/lifecycle_config.textproto
Desarrollador de servicios Selecciona el nombre del ajuste predeterminado lógico adecuado y registra la ruta de acceso scheduling_config.textproto en el manifiesto del paquete de servicios. scheduling_config.textproto y sdv_service_bundles_manifest.textproto

Flujo de trabajo técnico

  1. Integración de la plataforma: El OEM define el lifecycle_config.textproto específico del vehículo. Este archivo establece los perfiles de programación en todo el sistema (ajustes predeterminados) y los asigna a atributos de programación de Linux concretos según el hardware de destino.
  2. Desarrollo de paquetes de servicios: El desarrollador agrupa un scheduling_config.textproto dentro de su paquete APEX, recomienda un ajuste predeterminado lógico (por ejemplo, ELEVATED) y define cualquier nombre de subproceso interno.
  3. Integración de paquetes de servicios: Durante la fase de integración del vehículo, el OEM revisa el ajuste predeterminado recomendado por el desarrollador. El OEM puede conservar el perfil recomendado o anularlo, como degradar un servicio ELEVATED a NORMAL, para mejorar la estabilidad general del sistema antes de firmar el APEX.
  4. Resolución de atributos de tiempo de ejecución: Cuando se inicia un servicio, LM identifica el ajuste predeterminado autorizado del manifiesto del servicio y recupera los atributos de Linux correspondientes de la configuración del sistema del OEM.
  5. Sincronización y aplicación de inicio: LM aplica los atributos resueltos al proceso de servicio antes de indicarle que continúe la ejecución con el protocolo de señal y continuación.

Conceptos básicos

Los siguientes conceptos son fundamentales para el marco de trabajo de programación de SDV.

Ajustes predeterminados de programación

Los ajustes predeterminados de programación son el mecanismo principal para administrar la QoS en SDV. En lugar de definir parámetros de programación de Linux sin procesar, los desarrolladores hacen referencia a ajustes predeterminados predefinidos por nombre. En el tiempo de ejecución, LM asigna estos nombres lógicos a atributos de programación de Linux concretos.

Los siguientes ajustes predeterminados se configuraron como ejemplo en lifecycle_config.textproto:

Nombre del ajuste predeterminado Política de programación Valor nice típico Caso de uso típico
NORMAL SCHED_OTHER 0 Servicios estándar (HVAC, medios, configuración)
ELEVATED SCHED_OTHER -10 Componentes y infraestructura principales del sistema
IDLE SCHED_IDLE 19 Análisis en segundo plano y registro no crítico
CUSTOM Definido por el usuario -20 (inicial) Servicios que requieren políticas o afinidad en tiempo real

Cuando aplica un ajuste predeterminado, LM usa la llamada al sistema setpriority para establecer el valor nice en todo el proceso. Esto afecta la parte relativa de la CPU que recibe el proceso durante la contención en el programador completamente justo (CFS) predeterminado de Linux.

Privilegios y seguridad

Para evitar el aumento de prioridad no autorizado, la plataforma de SDV emplea un modelo de seguridad por niveles basado en dominios SELinux y capacidades de Linux.

Dominios de seguridad

  • untrusted_service_bundle: Es el dominio predeterminado para los ajustes predeterminados estándar (NORMAL, ELEVATED, IDLE). El kernel restringe los procesos en este dominio, lo que impide que cambien sus propios parámetros de programación.
  • priority_service_bundle: Se otorga a cualquier paquete de servicios que use un ajuste predeterminado en el que is_privileged: true esté configurado en el lifecycle_config.textproto en todo el sistema. La transición a este dominio otorga al proceso la capacidad CAP_SYS_NICE y le permite administrar su propia asignación de recursos.

Capacidad CAP_SYS_NICE

La plataforma otorga la capacidad CAP_SYS_NICE a los procesos en el dominio priority_service_bundle. Este permiso permite que un proceso haga lo siguiente:

  • Elevar su propio valor nice más allá de su asignación inicial
  • Cambiar su política de programación a clases en tiempo real como SCHED_FIFO o SCHED_RR
  • Establecer la afinidad de la CPU con sched_setaffinity
  • Configurar parámetros de fecha límite especializados para SCHED_DEADLINE

Restringir esta capacidad a un dominio dedicado protege el equilibrio de programación en todo el sistema, ya que limita las interrupciones a los servicios autorizados de forma explícita.

Guía de configuración

En esta sección, se proporciona orientación sobre la configuración para OEMs y desarrolladores de servicios.

Guía para OEMs: Ajustes predeterminados en todo el sistema

El OEM define los perfiles de programación en /product/etc/lifecycle_config.textproto. La plataforma proporciona un ejemplo en lifecycle_management/config/lifecycle_config.textproto que se espera que los OEMs ajusten según el hardware del vehículo.

Ejemplo: Define un ajuste predeterminado en tiempo real

Un OEM podría definir un ajuste predeterminado CRITICAL para los servicios relacionados con la seguridad que nunca deben ser interrumpidos por las aplicaciones estándar:

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

Guía para desarrolladores: Configuración de paquetes de servicios

Los desarrolladores de servicios recomiendan un ajuste predeterminado y, de manera opcional, definen nombres de subprocesos lógicos en un archivo scheduling_config.textproto. Para que la plataforma lo descubra, la ruta de acceso a este archivo debe registrarse en sdv_service_bundles_manifest.textproto.

Ejemplo: Registro de manifiesto

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

Ejemplo: Usa un ajuste predeterminado estándar

Para la mayoría de los servicios, es suficiente hacer referencia a un nombre de ajuste predeterminado en scheduling_config.textproto:

# configs/scheduling_config.textproto
scheduling_preset_name: "ELEVATED"

Ejemplo: Programación para subprocesos de trabajo generados por el servicio

Un desarrollador de servicios puede crear subprocesos de trabajo adicionales en su código para controlar tareas especializadas, como un bucle de datos de sensores de baja latencia. El desarrollador puede definir atributos de programación con nombre para estos subprocesos internos en el archivo de configuración.

Usa la aplicación manual solo en servicios con privilegios que usan estos metadatos para establecer propiedades para sus subprocesos de trabajo internos:

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

Comportamiento y aplicación del sistema

La plataforma de SDV emplea un modelo de aplicación estricto para activar las configuraciones de programación antes de ejecutar cualquier código específico del servicio.

Sincronización de inicio

Para evitar que un servicio se ejecute con una prioridad incorrecta (incluso durante su fase de inicialización), LM y Service Bundle Runner (SBR) usan un protocolo de sincronización de señal y continuación:

  1. Creación de procesos: LM bifurca el proceso SBR. En este punto, el proceso secundario SBR se está ejecutando, pero ingresa de inmediato a un estado bloqueado, a la espera de una señal en su stdin.
  2. Aplicación de atributos: Mientras el elemento secundario está bloqueado, LM usa la llamada al sistema setpriority para aplicar el valor nice solicitado y configura la política de programación (por ejemplo, SCHED_IDLE).
  3. Transición de seguridad: LM realiza la transición del dominio SELinux y mueve el elemento secundario al dominio untrusted_service_bundle o priority_service_bundle.
  4. Señal: Solo después de que todos los parámetros se apliquen correctamente, LM enviará una señal de un byte al stdin del elemento secundario.
  5. Ejecución: SBR recibe la señal y comienza a cargar las bibliotecas de servicios y a llamar a los métodos del ciclo de vida.

Impacto en el subproceso principal y el ciclo de vida

Al sincronizar antes de cargar las bibliotecas de servicios, la plataforma mantiene la prioridad solicitada para el subproceso de ejecución principal en todas las devoluciones de llamada del ciclo de vida:

  • onCreate: Toda la inyección de dependencias y la asignación inicial de recursos se producen con la prioridad correcta.
  • onStart: El ajuste predeterminado rige la transición al estado activo y cualquier bucle de trabajo inicial.
  • onStop y onDestroy: La plataforma realiza operaciones de limpieza con la misma prioridad para evitar que se agoten las actividades críticas del sistema durante el apagado.

Grupo de subprocesos de Binder y herencia de prioridad

Los subprocesos del grupo de subprocesos de Binder administrado por la plataforma no ejecutan el trabajo con la prioridad del subproceso que creó el grupo. En cambio, para las transacciones síncronas, el subproceso del servidor hereda la prioridad de la persona que llama. El controlador del kernel de Binder controla este mecanismo de herencia de prioridad.

Ajuste a nivel del subproceso

Los paquetes de servicios que requieren un control detallado (por ejemplo, bucles de procesamiento en tiempo real) deben aplicar manualmente las configuraciones a sus subprocesos de trabajo. Sigue estos pasos para aplicar configuraciones a nivel del subproceso:

  1. Solicita un ajuste predeterminado con privilegios (en el que se establece is_privileged: true).
  2. Usa la función get_scheduling_configuration de la biblioteca sdv_service_bundles_scheduling para leer la thread_scheduling_configuration.
  3. Aplica atributos a los subprocesos de trabajo con sched_setattr.

Aplicación de SELinux y capacidades

La plataforma usa SELinux y la capacidad CAP_SYS_NICE de Linux para aplicar ajustes predeterminados de programación:

  • Para los servicios que usan ajustes predeterminados como NORMAL o IDLE, LM configura la programación durante el inicio. Estos servicios no tienen la capacidad CAP_SYS_NICE y no pueden cambiar su prioridad ni política.
  • Los servicios que usan un ajuste predeterminado con privilegios (is_privileged: true) reciben la capacidad CAP_SYS_NICE. Esto les permite controlar manualmente la programación de sus subprocesos internos, según sea necesario para las tareas en tiempo real.

Verificación y muestras

Para verificar el comportamiento de la programación, se requiere una combinación de análisis de registros y medición del rendimiento a nivel del sistema. La plataforma de SDV proporciona una muestra dedicada para demostrar estas técnicas.

Muestra de programación de QoS

Ubicada en samples/qos_scheduling, esta muestra incluye varios servicios de prueba de rendimiento diseñados para ejecutarse en paralelo y mostrar su progreso.

  • PerformanceTesterNormal: Se ejecuta con el ajuste predeterminado NORMAL.
  • PerformanceTesterElevated: Se ejecuta con el ajuste predeterminado ELEVATED.
  • PerformanceTesterRealtime: Usa el ajuste predeterminado CUSTOM para aplicar SCHED_FIFO a sus subprocesos de trabajo internos.
  • PolicyOffender: Es un servicio de diagnóstico que intenta establecer una prioridad en tiempo real mientras usa un ajuste predeterminado NORMAL. Se usa para verificar que la plataforma bloquee correctamente el aumento no autorizado.

Ejecuta el conjunto de pruebas de verificación

  1. Habilita la configuración de orquestación específica de QoS:

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

  2. Reinicia el dispositivo para iniciar los servicios en sus respectivos dominios de programación:

    adb reboot

  3. Filtra los registros para ver los resultados comparativos de rendimiento:

    adb logcat | grep sdv_sample_qos_common

    El servicio PerformanceTesterElevated informa que se completaron unidades de trabajo significativamente más altas en comparación con PerformanceTesterNormal. Los errores de denegación EPERM o SELinux se incluyen en los registros del servicio PolicyOffender.

Retrocompatibilidad

  • Para la retrocompatibilidad, cualquier paquete de servicios heredado que especifique una scheduling_config_path en su manifiesto, pero no especifique un scheduling_preset_name en el archivo de configuración, se tratará automáticamente como un servicio con privilegios. Esto conserva las capacidades necesarias (por ejemplo, CAP_SYS_NICE) para los servicios más antiguos que dependen del ajuste manual de subprocesos.
  • El mensaje de configuración raíz DeadlineSchedulingConfiguration está programado para cambiar de nombre en una versión futura. El nombre actual es histórico y ya no refleja con precisión que el mensaje controla todos los tipos de programación, no solo la programación de fecha límite.