Я Lead Software Engineer, занимаюсь проектами по виртуализации для автомотив-сектора в GlobalLogic. Эта статья подготовлена на основе моего выступления на Root Linux Conference 2017, и является продолжением темы, которую недавно раскрыл Ларс Курт.
В течение последних 5 лет GlobalLogic активно занимается проектами в автомобильной сфере. Некоторые из них — изначально коммерческие, некоторые — экспериментальные прототипы (proof of concept или попросту PoC), часть из которых также впоследствии перешла на коммерческую основу. Одно из наиболее интересных и важных для нас направлений в этой сфере — это технологии виртуализации. В этой статье я расскажу о том, зачем они нужны и как работают, а также затрону основные проблемы, с которыми мы столкнулись в процессе разработки, а также способы их решения.
Начнем с того, почему технологии виртуализации сейчас особенно актуальны для автомобильной индустрии. В любом современном автомобиле среднего уровня (не говоря уже про машины премиум-класса) используется
Один из них отвечает за работу информационно-развлекательной системы автомобиля (радио, воспроизведение музыки, навигация — все то, что мы привыкли видеть в своем смартфоне). Как правило, эта система работает на базе Android, но могут быть исключения, например, Tizen.
Другой компьютер (в обычной жизни водитель взаимодействует с ним редко) отвечает за систему зажигания, сервисы экстренного оповещения при аварии и т. п. Этот компьютер работает под управлением операционной системы реального времени (real-time OS, RTOS), как правило, закрытой и/или проприетарной (впрочем, бывают и исключения, например, FreeRTOS или QNX). Главные требования к такой системе — стабильность, надежность и отзывчивость, поскольку это устройство обслуживает запросы, время выполнения которых очень ограничено и очень критично.
Также в автомобилях премиум-класса отдельный компьютер отвечает за работу кластера (приборная панель над рулем автомобиля). Как правило, такой компьютер работает под управлением какой-либо узкоспециализированной Linux-системы с композитным менеджером Weston. Главное требование к такой системе — быстрый и высококачественный рендеринг изображения. В противном случае показания спидометра или тахометра будут отображаться на панели с задержкой, что сделает их бесполезными для водителя.
Один за всех
Теперь рассмотрим типичную автомобильную платформу. Как правило, это очень производительная система:
Во-первых, это безопасность и устойчивость к отказам. Это самые важные преимущества, с точки зрения автомобильной промышленности, особенно с учетом участившихся попыток взлома современных авто (в первую очередь это касается самоуправляемых автомобилей, хотя и не только).
Во-вторых, это уменьшение стоимости аппаратной платформы. Как ни крути, один, даже очень производительный автомобильный компьютер, будет стоить дешевле посредственных двух-трех.
В-третьих, экономия денег и времени происходит на этапе поддержки и обновления программного обеспечения всей системы. Дело в том, что в случае с виртуализацией мы можем модернизировать и обновлять систему не целиком, а отдельными частями. Например, после выхода Android Z мы можем достаточно быстро портировать его в нашу информационно-развлекательную систему. При этом такое обновление никак не повлияет на работу кластера или других систем. Если же виртуализации у нас нет, то, обновляя информационно-развлекательную систему, мы будем вынуждены обновлять и другие системы, что, вероятно, потянет за собой вопросы сертификации, безопасности и т. п. В свою очередь, это не только усложняет процесс обновления (и требует большей квалификации разработчиков), но и растягивает его на длительное время.
Обратная сторона виртуализации — система становится значительно более сложной, так как мы добавляем дополнительный уровень абстракции над обычной операционной системой. Кроме того, мы должны обеспечивать корректное взаимодействие между несколькими операционными системами, учитывать вопросы безопасности и стабильности. Все это, разумеется, влияет на стоимость разработки — на ее создание требуется больше времени более опытных специалистов, но мы убеждены, что в конечном счете все это окупается описанными выше преимуществами.
Почему Xen?
Во-первых, Xen — это гипервизор так называемого первого типа (type 1), что позволяет его использовать на «голом железе» и стартовать практически любую операционную систему в качестве гостевой. Уже, как говорится, «из коробки» Xen поддерживает множество гостевых операционных систем: Windows, Linux, FreeBSD, QNX и еще много чего другого, пускай даже с некоторыми ограничениями.
Во-вторых, это проект с открытым исходным кодом, который поддерживается сильным и активным сообществом разработчиков. Проект активно развивается, поэтому постоянно закрываются уязвимости, добавляется новая функциональность, регулярно выходят новые стабильные версии системы. Уже изначально Xen соответствует достаточно строгим требованиям по безопасности и надежности, что доказано опытом использования его такими серьезными компаниями, как Amazon, BitDefender и др.
В-третьих, начиная с версии 4.4, в Xen появилась поддержка архитектуры ARM, что называется, «из коробки». Нам не приходится каждый раз добавлять отдельную поддержку, поскольку она стабильная, надежная и тестированная.
В-четвертых, масштабируемость этой системы в общем случае завязана только на мощности платформы. Мы можем одновременно запускать до 4 тысяч операционных систем, если у нас позволяют возможности оборудования.
И наконец, в-пятых, у Xen относительно небольшая кодовая база, что позволяет немного снизить порог вхождения, плюс легче искать возможные проблемы в определенных частях проекта. Небольшая кодовая база, кстати, также позитивно влияет на безопасность системы, потому что тяжелее провести какие-то коммиты, которые приведут к нарушению безопасности.
Как это работает?
Итак, у нас есть несколько операционных систем, которые управляются из так называемого Dom0. Это привилегированный домен, который может управлять аппаратной платформой почти без ограничений. В основном он ответствен за создание гостевых операционных систем, за их модификацию и удаление. Также очень эффективно, чтобы в Dom0 «бежал» Watchdog, который поддерживается Xen через специальный механизм. Так, если упадет какой-либо из гостевых доменов, Watchdog его легко поднимет без влияния на работу остальных систем.
Если в реальности произойдет какой-то сбой и в автомобиле упадет система, ответственная за работу тахометра, ничего страшного не произойдет. Это никак не повлияет на работу, например, спидометра или системы зажигания. Кому-то, возможно, это доставит временные неудобства, но в целом на стабильность работы и безопасность езды это не повлияет.
Кроме того, в Dom0, как правило, работают драйвера физических устройств. Это такой специальный тип драйверов, который позволяет использовать их одновременно несколькими операционными системами с помощью паравиртуализированного подхода. Суть этого подхода в том, что мы предоставляем интерфейс (через специально написанные драйвера-прослойки) для использования физических устройств гостевым операционным системам (так называемые DomU), например, для воспроизведения звука или вывода изображения на экран. При этом в реальности все драйвера этих устройств находятся в другом домене — Dom0.
Дальнейшее развитие этого подхода — дезагрегация драйверов. В частности, для разработки решений именно для автомобильной индустрии мы выделили отдельный драйвер-домен, который содержит в себе самые основные «железные» драйвера. Dom0 в данном случае подхватывает только небольшой набор драйверов, необходимых для старта и для управления остальными доменами. Например, если какой-либо драйвер падает, то драйвер-домен тоже падает, но Dom0 отслеживает это событие и автоматически перезапускает драйвер-домен.
Проблемы и решения
Ранее уже упоминалось, что в Xen есть поддержка архитектуры ARM. Что нужно, чтобы операционная система «бежала» на основе гипервизора на ARM? На ARM операционная система «бежит» в режиме EL2 на AArch64, а гостевые домены у нас в EL1-режиме. Основная проблема для автомотива и не только — это проблема перевода бутлоадера перед стартом гипервизора в нужный режим.
В общем случае для решения этой проблемы используется U-boot. Но это далеко не обязательно, есть достаточно много исключений. Плюс иногда этот перевод не общедоступен, поскольку иногда это достаточно проприетарная информация. Но тем не менее U-boot с 2016 года поддерживает автоматический перевод ARM архитектур в Virtualization mode. Т. е. мы прыгаем на само ядро, в данном случае на гипервизор уже в Virtualization mode. Поэтому основной проблемой является отсутствие стандартизированного подхода в общем случае и некоторая закрытость инфраструктуры, особенно если мы говорим о закрытых архитектурах, например, Qualcomm.
Память
Еще одна достаточно сложная и комплексная задача — это память. Давайте рассмотрим вариант с трехдоменной конфигурацией. Как работает память в Xen в общем случае? У нас есть понятие физического и машинного адреса. Физический — это тот адрес, который мы видим со стороны домена. Ну, домен думает, что он бежит по физическим адресам. В действительности в терминологии Xen он «бежит» через промежуточные адреса по машинным адресам. Они в лучшем случае могут не соответствовать тем адресам, по которым будут бежать гостевые домены либо Dom0 за крайне редким исключением.
Dom0 специально настроен и работает таким образом, что его внутреннее представление адресов и машинное представление адресов, с которыми он взаимодействует, одинаковое. Зачем это нам надо? Проблема в том, что в Dom0 находятся все физические драйверы. Как минимум одна из проблем — это проблема с DMA.
Когда мы создаем DMA-транзакцию, то должны работать с машинным адресам в терминологии Xen (то есть настроить буфера на эти адреса, но это не всегда возможно, так как для гостевых операционных систем машинные и физические адреса будут разными). Есть несколько способов сделать это так, чтобы сами гостевые операционные системы знали об этом и использовали тот факт, что у них физические адреса не соответствуют машинным. Один из них, очень грязный хак — это правка стартового начального адреса памяти, по которой «бежит» конкретная операционная система. Это так называемая RAM base.
По умолчанию Xen создает RAM base начиная с 0×80000000, но это можно изменить. И по факту это больше механическая работа, потому что это нельзя сделать в один проход. Вам надо править в нескольких местах, начиная с конфигурации конкретного домена, заканчивая конечным device tree. Но тем не менее это имеет право на жизнь — на схеме можно увидеть, что Xen выделил память для драйвера домена с 0xB0000000 — 0xBFFFFFFF, но сама операционная система драйвер-домена думала, что она бежит по адресам начиная с 0×80000000. Однако мы поправили стартовый адрес, и теперь у нас домен один в один замаплен.
Но по факту остается еще один недостаток этого метода — мы можем аллоцировать только последовательные куски памяти: пусть разного размера, но последовательные, что в общем уменьшает универсальность подхода и его гибкость.
Вторым подходом является набор памяти по частям. Xen изначально оперирует достаточно большими кусками памяти, и не всегда их можно «нарезать» таким механизмом. Нам, например, надо 766 МБ в драйвер-домене. Если не ошибаюсь, ближайшее целое значение памяти, которое мы можем выделить одним куском, — 1 ГБ, после этого будет уже мелкими кусочками по 2MB. Нам никто не мешает, в принципе, эти кусочки натягать из разных частей оперативной памяти и сделать так, чтобы каждый из них был один в один «замаплен». Это нам позволит переложить работу по трансляции адресов на ядро. Да, хорошо, у нас будут какие-то дырки, у нас будет и непрерывный кусок, но вся трансляция проходит на стороне Linux. На данный момент это наиболее эффективный подход, который мы используем.
Наиболее правильным и эффективным методом решения проблемы с DMA будет использование SMMU (IOMMU) (SMMU — system mmu, IOMMU — input/output mmu) — в этом случае трансляция адресов будет выполняться на аппаратном уровне, прозрачно для устройства. Но, к сожалению, его наличие в SoC зависит от производителя (и наличия поддержки этого устройства в BSP для данного SoC). А также поддержки со стороны Xen — но сообщество работает над этим решением.
Графика и прошивки
Следующая проблема, с которой можно столкнуться, — это проблема с прошивками. Например, у нас есть какой-то DSP-процессор (узкоспециализированный процессор, предназначенный для решения задач цифровой обработки сигналов), который обрабатывает видео и, как правило, использует резерв памяти в device tree для ARM. В общем случае доступ к этим прошивкам есть только у производителей устройств. И проблема в том, что нам надо получить доступ к функциональности этих прошивок, не смотря на то, что процессор использует резерв. К сожалению, эта проблема сложно решаема. Одно из банальных решений, к которому приходилось прибегать, — это бинарный патч в runtime. Мы знаем, по каким адресам у нас бежит домен, и мы в runtime просто патчим адреса, на которые настроена прошивка для DSP. Это очень грязный хак, но тем не менее иногда он имеет право на жизнь.
Другой подход, который в принципе сейчас невозможен в силу специфических требований некоторых заказчиков и производителей, — это отказ от использования какого-то программного обеспечения или устройств периферии в гостевых доменах. При этом, разумеется, мы хотим каким-то образом взаимодействовать, например, с GPU. Готового решения этой задачи нет, но наиболее правильным и эффективным методом, как указывалось раньше, будет использование SMMU.
По своей сложности топовые GPU (например, A72 Mali) уже превосходят привычные вычислительные процессоры. Это создает разработчикам дополнительные проблемы — сложность самого графического «железа», жесткие проприетарные лицензии, проблемы с документацией, которую очень непросто получить от производителя.
Также на данный момент в 99% GPU отсутствует поддержка аппаратной виртуализации, поэтому приходится искать какие-то отдельные варианты и механизмы для решения этих задач.
Один из способов использования GPU виртуализированными операционными системами, который мы реализовали, — это программный context switch. На каждый switch домена мы сохраняем состояние и набор регистров GPU и восстанавливаем с другого домена. То есть процесс повторяется: сохранили-восстановили; сохранили-восстановили. Этот механизм достаточно прост в создании, но проблема, как минимум, в том, что GPU весьма сложное устройство, и все эти операции — не мгновенные.
Чем больше регистров мы сохраняем, тем больше времени мы теряем в процессе context switch. Поэтому надо сохранять минимальный набор регистров. Плюс для такого подхода характерно включение/выключение GPU. На каждом этапе мы должны его выключать/включать, что в терминах железа не является быстрым процессом. Нам надо ждать каких-то битов в регистрах, ждать значения в памяти и прочее.
Вторым, более эффективным подходом является использование полуаппаратной виртуализации. Это виртуализация с поддержкой со стороны GPU. В таком случае мы перекладываем часть работы на железо, которое позволяет нам сохранять состояние графического процессора. Однако это требует весьма больших затрат в поддержке со стороны виртуализации в драйверах. Это реализовано в некоторых драйверах с помощью производителя, что позволяет упростить и более корректно это сделать. Дело в том, что любая поддержка виртуализации в принципе не зависима от гипервизора, на котором мы «бежим».
Если говорить о производительности решений, то первое решение, конечно, менее производительно, потому что мы должны все это прогнать через CPU, что весьма долго. Так, потеря производительности в первой платформе составляет порядка
PV-драйвера
Как уже говорилось ранее, PV-драйвера — это некий специальный подход к распределению возможностей реального железа между несколькими доменами. У нас есть какой-то backend, который взаимодействует с аппаратным программным обеспечением через настоящий драйвер в Linux и какой-то frontend, который предоставляет не обязательно полный доступ ко всей функциональности.
Таким образом, мы, например, можем проигрывать звук из нескольких доменов через одно физическое устройство. Можем корректно микшировать звук из разных доменов, заглушить какой-либо отдельный домен и т. п.
Поначалу «из коробки» были доступны только основные драйверы — PV-блок, который используется для виртуализации блочных устройств. Паравиртуальные драйверы для сети и PCI, SCSI и Frame buffer, а также USB (pvUSB) были доступны только на архитектуре x86. PvUSB было изначально доступно и в более старых версиях, но производительность была очень низкой, как и переносимость, но вот в 4.7 добавили базовую поддержку pvUSB. Однако коммюнити продолжает разрабатывать другие PV-драйвера.
К сожалению, этого все еще недостаточно. Ведь используя информационно-развлекательную систему автомобиля, мы хотим в первую очередь смотреть видео, фотографии и слушать музыку. Значит, нам нужны как минимум популярные кодеки. Кроме того, для управления системой в автомобиле мы вряд ли будем использовать мышку или клавиатуру, а, значит, нам также нужна поддержка сенсорного экрана и т. п. Всего этого в готовом виде нет, поэтому нам пришлось создавать все недостающие компоненты с нуля.
Некоторые из таких интерфейсов уже вошли или скоро войдут в мейнлайн. Их, возможно, будут использовать частично «из коробки», а в дальнейшем они будут включены в ядро системы.
Тренды и перспективы
Одно из самых интересных для автомотива направлений — это операционные системы реального времени. Как я уже говорил, работа некоторых бортовых встроенных компьютеров критична с точки зрения времени, они важны для того, чтобы оповестить об определенных событиях, чтобы защитить человека. Xen имеет некоторые механизмы поддержки RTOS, но, к сожалению, они еще не очень развиты.
По умолчанию Xen использует scheduler, который weight-based, то есть рассчитан на работу с весами. Он не гарантирует отзывчивость операционной системы. Есть также механизм RTDS scheduler, который предоставляет мягкое реальное время, но он на данный момент еще в разработке. Интересен для автомотива ARINC653 scheduler: он позволяет предоставить жесткое реальное время, но не имеет поддержки многоядерности. В дальнейшем, возможно, это будет исправлено, возможно, нет.
Также на сегодняшний день достаточно интересным трендом, как я уже говорил, является дезагрегация — использование отдельного драйвер-домена. Почему бы нам в качестве Dom0 не использовать какую-нибудь тонкую операционную систему реального времени, FreeRTOS, например? Это позволит нам более эффективно взаимодействовать с железом, отслеживать его состояние, поддерживать состояние системы в целом.
Однако на данный момент полноценной поддержки в Dom0 нет. Есть поддержка со стороны MiniOs, но пока что это не совсем честная поддержка. Это запуск нескольких операционных систем из Xen в момент его старта. То есть это не дает нам поддержки libxl-стека. Если мы захотим вручную создавать домены, то нам понадобятся другие механизмы.
Также нами была разработана частичная поддержка QNX в качестве гостевого домена DomU. Это позволило нам воспользоваться его преимуществами для дальнейшей сертификации. Кроме того, в качестве Dom0 можно использовать Linux с реал-тайм патчами. Но он, конечно, не предоставит механизм жесткого реального времени. Этого будет недостаточно для каких-то критичных по времени исполнения задач.
В целом можно сказать, что виртуализация для автомобильной промышленности — достаточно сложная область, и здесь хватает задач, для которых пока еще нет готовых решений. Но именно поэтому это очень интересная тема для всех, кто хочет попробовать себя в R&D и заняться созданием решений, которые, возможно, станут стандартом для целой индустрии. В любом случае это очень перспективное направление. Если оно интересно и вам — присоединяйтесь. Больше про Xen и все, что с ним связано, вы можете узнать на странице wiki.xen.org/wiki/Main_Page