null

Сказ о PCI(e) Passthrough или как сходить в пешее эротическое путешествие и вернуться обратно

Краткая справка:

PCI(e) passthrough - это механизм, позволяющий виртуальной машине управлять устройством PCI(e) хостовой системы. Это позволяет получить некоторые преимущества по сравнению с использованием виртуализированного оборудования, например меньшую задержку, более высокую производительность или дополнительные функции (например, разгрузку).

В начале было слово...

Потребовалось поднять систему виртуализации Proxmox VE на серверах HP ProLiant DL380 G7 для развёртывания Microsoft Exchange. По стечению обстоятельсв, сервера были укомплектованы InfiniBand адаптерами Mellanox ConnectX-2. Было решено использовать данные адаптеры для связи виртуальных машин с серверами хранения баз данных Exchange.

После определённых мучений с iLO3, гипервизор был успешно развёрнут, но вот незадача: создать виртуальный коммутатор (Bridge) поверх интерфейса infiniBand оказалось невозможно, из-за особенностей самого InfiniBand.

После бурной дискуссии с коллегами, решено было попробовать использовать нашего главного героя, а именно функционал PCI(e) passthrough для презентования виртуальной машине с Exchange всего адаптера InfiniBand. Задача была ясна и казалась простой к исполнению. Но пушной зверёк подкрался незаметно...

​​​​​​​Была создана виртуальная машина и в настройках её Hardware выбран проброс адаптера Mellanox:

И была запущена ВМ. После непродолжительного ожидания запуска было получено сообщение "Я устал, я мухожук":

 

После небольшой порции русской простанародной речи и проверки настроек BIOS (которые оказались корректными), была предпринята вторая попытка запуска ВМ. Пациент, как и раньше, оказался скорее мёртв, чем жив. После этого было решено спросить у ясеня dmesg, а что случилось. На что радостным эхом был получен ответ:
​​​​​​​

fio-pci 0000:0b:00.0: DMAR: Device is ineligible for IOMMU domain attach due to platform RMRR requirement.  Contact your platform vendor.

 

И с этого момента, мы отправляемся в наше увлекательное эротишное путешесвтие...

 

Есть два стула...

Для начала, разберёмся с тем, как же виртуальные машины используют память:

Каждая запущенная в системе ВМ получает новое виртуальное адресное пространство и не имеет прямого доступа к памяти хостовой системы (в нашем случае - Proxmox VE). Тем не менее, гостевая ОС работает с вирутальной оперативной памятью так же, как и с реальной, используя любые адреса памяти, которые ей нужны. Другими словами, гостевая ОС понятия не имеет (с точки зрения памяти), что она виртуализируется. Логически должна быть некоторая карта адресов (маппинг) для преобразования запросов гостевой ОС в адреса реальной памяти, поскольку несколько гостевых ОС должны совместно использовать одну и ту же физическую память хоста. Гипервизор (ПО ОС хоста) отвечает за поддержание сопоставления между ГАП (гостевое адресное пространство) и ФАХ (физические адреса хоста).

Когда виртуальная машина запускается, гипервизор выделяет ей заранее определенный объем памяти (это может быть либо вся память сразу, либо минимальный порог. В PVE это параметр Minimum memory при включенной опции «раздувания» памяти (Ballooning Device) и сообщает гостевой ОС, что у нее есть адресное пространство. Гостевая ОС знает, что может использовать это адресное пространство, и ей все равно, где физически находится эта память. Хост-ОС теперь необходимо найти место для этой памяти гостевой ОС в одном или нескольких фрагментах физической памяти.

При отображении памяти (как описано в предыдущем разделе) ОС хоста должна позаботиться о трех вещах:

  1. Когда гостевая ОС запрашивает страницу из памяти, используя свой адрес (ГПА), она получает ее из памяти с адресом ФАХ (сопоставление)

  2. Память гостя не может быть затронута ничем, кроме гостя (защита)

  3. Процесс должен быть быстрым (иначе смысла нет)

Первые два пункта достижимы с помощью чистой программной эмуляции, но это делает процесс доступа к памяти медленным, поскольку он (процесс доступа к памяти) больше не может полагаться на прямой доступ к памяти (Direct Memory Access), а задействует ЦП для каждого сдвига байтов вперед и назад. Для решения этой проблемы были созданы технологии Intel VT-d/AMD-Vi.


VT-d и AMD-Vi позволяют давать указание аппаратному обеспечению выполнять сопоставление и применять домены (границы безопасности). В таком случае ОС хоста просто должна сообщить оборудованию адрес, который нужно транслировать на лету.

 

Каждое устройство в системе имеет некоторое зарезервированное адресное пространство памяти. Он используется устройством и хост-системой для связи и обмена данными. Этот зарезервированный адрес памяти определяется прошивкой (т. е. BIOS), поскольку и устройство, и ОС должны знать его для обмена данными. По сути, это немного отличается от обычного отображения памяти. Здесь у вас есть не просто ОС, использующая память, а ОС и устройство, использующие память. Теперь в игру вступает IOMMU (nput–output memory management unit, ака блок управления паматью для операций ввода-вывода).

По сути, он может переназначить ГПА на ФАХ как для ОС, так и для устройства, чтобы они могли общаться друг с другом. Когда память устройства переназначается, гостевая ОС общается с аппаратным обеспечением, как если бы оно действительно находилось под каким-то физическим адресом, который она ожидает, в то время как на самом деле IOMMU перемещает блок зарезервированной области памяти куда-то другое место  в адресном пространстве. Обычно это работает нормально, но инженеры Intel - очень альтернатиивно одарённые люди...

Хотя и Intel и AMD допускают переназначение памяти устройства IOMMU, у Intel возникла гениальная идея ввести RMRR (отчеты о зарезервированной области памяти). По сути прошивка/БИОС материнской платы публикует список областей памяти, где использование IOMMU якобы запрещено. Первоначальный замысел этой функции был благим (но реализация, как обычно, подкачала), позволяя USB-клавиатурам автоматически эмулироваться самим USB-контроллером до загрузки USB-драйвера, как если бы они были подключены через PS/2. Это также позволяет графическому процессору выводить изображение до загрузки ОС и даже до инициализации блока IOMMU. Однако, это потребовало кровавых жертв: эта память не должна переназначаться, поскольку только ОС и устройство используют IOMMU, а устройства на материнской плате, которые могут обмениваться данными, например, с предварительной загрузкой графического процессора, ничего не знают об этом сопоставлении.

Технически, спецификация VT-d говорит, что RMRR действует бессрочно, но на самом деле это не совсем так. Чем мы и воспользуемся в дальнейшем.

Ядро Linux в течение долгого времени (до версии 3.17) не учитывало RMRR при настройке IOMMU. Это было упущением, поскольку IOMMU API берет на себя исключительный контроль над переназначенным адресным пространством. Если такое пространство перераспределено, доступ к DMA из-за пределов домена IOMMU (т. е. из чего-то другого, кроме гостевой ОС хоста или виртуальной машины, например, устройства на материнской плате) не удастся, что может привести к непредсказуемым результатам.

Ядро Linux на данный момент исключает два конкретных класса устройств, ограниченных RMRR:

  1. USB-устройства (поскольку мы исторически верим, что они не делают странных вещей)

  2. Графические процессоры (негласное правило, что доступ к ним вне полосы пропускания осуществляется только до загрузки драйвера)​​​​​​.

Разрабатывая VT-d, Intel ожидали, что «регионы RMRR будут использоваться для устаревших применений (...). Разработчики платформ должны избегать или ограничивать использование зарезервированных регионов памяти»

Что же могло пойти не так?

HP (и, возможно, другие вендора, не могу проверить на текущий момент) решили пометить каждое (sic!) пространство памяти устройства PCI как RMRR! Вот так, на всякий случай ... просто их инструменты потенциально могут отслеживать эти устройства, пока агент ОС не установлен, НО подождите, есть еще! Они помечали так ВСЕ устройства, даже сторонние (sic!), физически установленные в слоты PCI/PCIe материнской платы! (Не ну не педерасты ли, а)?

​​​​​​​Это в свою очередь заблокировало возможность проброса в гостевую ОС любого PCIe устройства под управлением дистрибутива Linux с ядром, новее 3.17 (т.е это всё что новее Debian 8, где ядро было 3.16...)

Соответсвенно выходов (стульев) есть два:

1) «Пики» - лепить патч-хаки для ядра

2) «X...» - обратиться к вендору

​​​​​​​С учётом полититической ситуации на 2023г.  и старостью платформы (Gen. 7 всё-таки), путь 2 для нас отпал сам собой. Придётся идти по пути 1...

 

Relax, dude!

В ходе изысканий по решению проблемы, было найдено долгосрочное решение — использование релаксируемых областей резервирования (через патч для ядер >=3.17)

Что на самом деле делает этот патч?

Используется тот же механизм, что и в ванильном ядре, для обозначения USB и графических процессоров как «расслабляющих». Это имеет три преимущества:

  1. RMRR не полностью отключен, так как память помечена как зарезервированная с исключениями, а не просто зарезервированная. Это, в сочетании с управлением списками IOVA, гарантирует, что если какой-то код где-то должен работать по-другому с расслабляющими устройствами, он будет работать с этим патчем правильно.

  2. Этот патч не вносит несогласованного состояния в ядро. RMRR не скрываются от ядра удалением и не игнорируются только в одном месте. Этот патч просто меняет обозначение этих регионов с IOMMU_RESV_DIRECT(«мы знаем, что эта память зарезервирована, и мы будем жёстко держать её») на IOMMU_RESV_DIRECT_RELAXABLE («мы знаем, что эта память зарезервирована, но можем сделать исключение»).

Кроме того, этот механизм можно контролировать с помощью параметра загрузки, что делает его безопасным и простым в отключении по мере необходимости.

 

Я вижу свет! Думаю, я пойду на него...

В свете данной информации, был найден GIT репозиторий, где лежат данные патчи для ядра Proxmox VE: https://github.com/Aterfax/relax-intel-rmrr/blob/master/README.md

На выбор есть 2 варианта установки:
1) собрать ядро самому с патчем
​​​​​​​2) Взять предкомпилированное ядро

​​​​​​​Был выбран второй путь, т.к сроки на решение задачи были очень сжатыми.

​​​​​​​На системе с PVE выполнить следующее

​​​​​​​1) apt update && apt upgrade

​​​​​​​2) apt install unzip wget

​​​​​3) wget https://github.com/Aterfax/relax-intel-rmrr/releases/download/<pve_kernel_version>/release.zip

4) unzip release.zip

​​​​​​​5) cd build/proxmox/proxmox-kernel/debs/

​​​​​​6) dpkg -i *.deb

7) Дождаться утсановки пакетов

8) В связи с необходимостью грузится с патченным ядром, нужно указать его id в GRUB'е.

​​​​​​​Для этого:

​​​​​​​1) grep pve-relaxablermrr-advanced /boot/grub/grub.cfg

Получаем строку вида (у вас она будет немного иная):

set default="gnulinux-advanced-9107af81-f40c-4113-a93b-c7870b4a9480>gnulinux-6.2.11-1-pve-relaxablermrr-advanced-9107af81-f40c-4113-a93b-c7870b4a9480"
        menuentry 'Proxmox VE GNU/Linux, with Linux 6.2.11-1-pve-relaxablermrr' --class proxmox --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-6.2.11-1-pve-relaxablermrr-advanced-9107af81-f40c-4113-a93b-c78

Из неё нужна только строка

"gnulinux-advanced-9107af81-f40c-4113-a93b-c7870b4a9480>gnulinux-6.2.11-1-pve-relaxablermrr-advanced-9107af81-f40c-4113-a93b-c7870b4a9480"

​​Сохраняем себе в отдельное место эту строчку.

​​​​​​​Далее редактируем параметры загрузки ядра в /etc/default/grub

​​​​​​​Приводим в нём две строки ниже к виду (с поправкой на свои отличия):

GRUB_DEFAULT="gnulinux-advanced-9107af81-f40c-4113-a93b-c7870b4a9480>gnulinux-6.2.11-1-pve-relaxablermrr-advanced-9107af81-f40c-4113-a93b-c7870b4a9480"
​​​​​
​​​​​​​GRUB_CMDLINE_LINUX="intel_iommu=on,relax_rmrr iommu=pt intremap=no_x2apic_optout"

Сохраняем изменения и делаем update-grub

​​​​​​​Теперь нам необходимо дополнительно определить загружаемые модули. Для этого в /etc/modules добавляем:

​​​vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd

Сохраняем и закрываем файл

Также необходимо дополнительно определить, что для vfio_iommu_type1  разрешены небезопасные прерывания. Для этого создаём файл /etc/modprobe.d/vfio.conf и туда вписываем:
 

options vfio_iommu_type1 allow_unsafe_interrupts=1

​​​​​Сохраняем, закрываем.

​​​​​​​Обновляем initamfs: update-initramfs -k all -u

​​​​​​​И перезапускаем сервер: reboot

​​​​​​​После ребута, проверяем что загрузились с правильным ядром: uname -ar

Должно вернуть строку вида:


Linux pve2 6.2.11-1-pve-relaxablermrr #1 SMP PREEMPT_DYNAMIC PVE 6.2.11-1 (2023-04-20T09:59Z) x86_64 GNU/Linux


​​​​​​​​​​​​​​​​​​​​Если всё ок, то теперь можно запустить ВМ с проброшеным PCIe устроством и она будет работать:

1) В Linux

2) В маздай windows:
​​​​​​​

 

После этого остаётся настроить IB-адаптер согласно руководству для нужной ОС.

Назад

Коротко о себе:

Работаю человеком-оркестром, занимаюсь спасением IT-утопающих.

​​​​​​​Очень люблю Debian GNU/Linux , различное Hardware  и Hi-Fi аппаратуру.

 

​​​​​​​​​​Windows must die, it's time to say goodbye

Windows must die, no more Windows in my life

Windows must die, it's time to take control

Windows must die, and make my computer whole!

 

Ничего не найдено. n is 0