Дополнение D. Работа с группами IOMMU
Содержание
Введённые в Red Hat Enterprise Linux 7, VFIO, или Virtual Function I/O (Виртуальные функции ввода/ вывода), являются неким набором модулей ядра Linux, которые предоставляют инфраструктуру драйвера пользовательского пространства. Такая инфраструктура применяет защиту устройств управления вводом выводом памяти (IOMMU, input–output memory management unit) для разрешения безопасного доступа к устройству драйверам пространства пользователя. VFIO включает драйверы пользовательского пространства, такие как DPDK (Data Plane Development Kit, наобр средств разработки уровня данных), помимо более распросранённых выделений Устройств PCI.
VFIO применяет IOMMU для изоляции устройств и предотвращения непреднамеренного Direct Memory Access (DMA, Прямого доступа к памяти) между двумя устройствами, работающими в одном и том же хосте физической машины, что оказывало бы воздействия на функционирование хоста и гостя. Группы IOMMU доступны в Red Hat Enterprise Linux 7, что является крупным улучшением относительно наследуемого назначения устройств KVM, которое доступно в Red Hat Enterprise Linux 6. Данное приложение выделяет следующие темы:
-
Обзор групп IOMMU
-
Важность изоляции устройств
-
Преимущества VFIO
Некое IOMMU создаёт какое- то виртуальное адресное пространство для определённого устройства, в котором всякий Виртуальный адрес ввода/ вывода (IOVA, I/O Virtual Address) может транслироваться в другой адрес в имеющейся физической системной памяти. Когда данная трансляция выполнена, данное устройство подключается к этому другому адресу в пределах физической системной памяти. Без наличия некоего IOMMU все устройства имеют какой- то плоский обзор всей физической памяти, так как они лишены трансляции адресов в памяти. С посощью IOMMU устройства приобретают определённое пространство IOVA в качестве какого- то пространства адресов, которое является полезным для назначения устройства.
Различные IOMMU имеют различные уровни функциональности. В недалёком прошлом, когда IOMMU были ограничены, предоставляя только трансляцию, причём зачастую только для некоторого небольшого окна в имеющемся адресном пространстве. К примеру, такой IOMMU получал бы только некое небольшое окно (1ГБ или менее того) пространства IOVA в нижней памяти, причём оно совместно применялось множеством устройств. Неким примером такой модели является таблица переназначения адресов графики (GART, graphics address remapping table) AMD при использовнии общеупотребимого IOMMU. Такие классические IOMMU в основном предоставляют две возможности: дублирующую буферизацию (bounce buffers) и объединение адресов (address coalescing).
-
Дублирующая буферизация необходима когда имеющиеся возможности адресации данного устройства меньше чем у его платформы. Например, если адресное пространство какого- то устройства ограничено 4ГБ (32 битами) оперативной памяти и этому драйверу был отведён буфер выше 4ГБ, данное устройство не будет иметь возможности доступа с прямой адресацией к такому буферу. Такое положение вещей с необходимостью влечёт применение дублирующей буферизации; некое буферное пространство располагается в нижней памяти, в которой данное устройство может выполнять операции DMA. Все данные в таком буфере всего лишь копируются в имеющийся выделенный буфер устройства после выполнения данной операции. Другими словами, данный буфер возвращается из нижних адресов памяти в верхние адреса памяти. IOMMU избегает дублирующей буферизации предоставляя некую трансляцию IOVA внутри адресного пространства самого устройства. Это делает возможным для данного устройства выполнять опреацию DMA напрямую в свой буфер, даже в том случае, когда его буфер выходит за пределы доступного данному устройству физического адресного пространства. Исторически такая функциональность IOMMU зачастую применялась исключительно в целях такого IOMMU, однако по мере применния PCI-Express (PCIe), данная возможность поддержки адресации свыше 4ГБ требуется для всех конечных точек, не относящихся к устаревшим моделям.
-
При традиционном выделении памяти блоки оперативной памяти выделяются и освобождаются на основе имеющихся потребностей конкретного приложения. Применение такого метода создаёт разбросанные по всему физическому адресному пространству пробелы в памяти. Было бы лучше, если бы такие пропуски в памяти собирались бы воедино. Данное IOMMU объединяет такие разбросанные в памяти списки через своё пространство IOVA, иногда именуемое как списки векторной адресации ( scatter-gather lists). Выполняя это, данное IOMMU создаёт непрерывные операции DMA и в конечном счёте увеличивает получаемую эффективность общей производительности ввода/ вывода. В некотором простейшем примере, какой- то драйвер может выделить два буфера размером 4кБ, которые не являются непрерывными в имеющемся физическом адресном пространстве. Данное IOMMU может выделить какой- то непрерывный диапазон для таких буферов в имеющемся физическом пространстве памяти. Такое IOMMU может выделять какой- то непрерывный диапазон для данных буферов, позволяя имеющемуся устройству ввода/ вывода выполнять единый 8кБ DMA вместо двух раздельных 4кБ DMA.
Хотя объединение памяти и двойная буферизация являются важными для высокопроизводительного ввода/ вывода в определённом хосте, в современных IOMMU самым важным свойством IOMMU, которое является существенным для среды виртуализации в конкретном хосте является возможность изоляции. Изоляция не была возможной в широком масштабе до появления PCI- Express, так как обыный PCI не помечал транзакции неким идентификатором конкретного запрашиваемого устройства (requester ID). Даже хотя PCI-X и включал до некоторойстепени такой идентификатор запроса, имевшиеся правила для взаимодействующих устройств, которые получали владение над данной транзакцией не предоставляли полной поддержки изоляции устройства.
С появлением PCIe все транзакции устройств помечаются неким идентификатором запроса, уникальным для такого устройства (выделенный номер шины/ устройства/ функции - bus/device/function - PCI, который часто сокращённо именуется как BDF), который применяется для ссылки на некую уникальную таблицу IOVA для такого устройства. Теперь подобная изоляция стала возможной, причём получаемое пространство IOVA может применяться не только для операций трансляции, таких как разгрузка не востребованной памяти и объединение памяти, но также может применяться и для ограничения доступа DMA с определённого устройства. Это делает возможнойизоляцию устройств друг от друга, предотвращая дублированное выделение пространства памяти, что является сущетвенным для надлежащего управления устройством виртуальной машины. Применение таких свойств для некоторой гостевой виртуальной машины вовлекает заполнение имеющегося пространства IOVA для данного назначенного устройства с установленным соответствием физического- гостя- к- физической- памяти - хоста для данной виртуальной машины. Раз это сделано, такое устройтсво выполняет операции DMA прозрачным образом в адресном пространстве своей гостевой виртуальной машины.
Группа IOMMU определяется как наименьший набор устройств, который может рассматриваться изолированным с точки зрения IOMMU. Самым первым шагом для достижения изоляции является грануляция. Если IOMMU не имеет возможности разделять устройства в отдельных пространствах IOVA, они не являются изолированными. К примеру, если множество устройств попытаются указать ссылку на одно и то же пространство IOVA, само IOMMU не сможет делать различия между ними. Именно это является основной причиной, по которой обычные PC x86 будут группировать все обычные устройства PCI вместе, причём все они будут помечены одними и теми же идентификатором запрашивающей стороны (requester ID), установленным мостом PCIe-to-PCI. Наследуемое назначение устройства KVM позволяет некоторому пользователю назначать эти устройства обычных PCI раздельно, однако такая настройка завершается неудачей, поскольку само IOMMU не может различать такие устройства. Как только VFIO начинает обслуживаться группами IOMMU, они препятствуют всем настройкам, которые не соответствуют этим наиболее основным требованиям гранулярности IOMMU.
Следующим шагом будет определение того, достигнет ли на самом деле данная транзакция определённого устройства самого IOMMU. Действующая спецификация PCIe делает возможным повторно выставлять маршруты для его транзакций в рамках имеющейся инфраструктуры интерконнекта. Исходящий вниз (downstream) порт PCIe может изменять маршрут транзакции из одного нисходящего потока на другой. Такие порты нисходящего потока коммутатора PCIe могут быть взаимосвязаны чтобы допускать изменение маршрута из одного порта в другой. Даже при наличии какого- то оконеного устройства со множеством функций, некая транзакция одной функции может быть напрямую доставлена в другую функцию. Такие транзакции из одного устройства в другое именуются одноранговыми (peer-to-peer) транзакциями и могут разрушать имеющуюся изоляцию устройств, работающих в отдельных пространствах IOVA. Допустим, предположим, что в случае, когда данная карта сетевого интерфейса, назначеная какой- то гостевой виртуальной машине, осуществит попытку операции записи DMA по некоторому виртуальному адресу внутри своего собственного пространства IOVA. Однако, это имеет место в том физическом пространстве, которок имеет тот же самый адрес, относящийся к какому- то дисковому контроллеру в одноранговом окружении, находящемся во владении определённого хоста. Поскольку конкретный IOVA для физической трансляции в определённое устройство осуществляется только в данном IOVA, любые попытки взаимодействия для оптимизации получаемого пути данных такой транзакции способны ошибочно перенаправить выполняемую операцию записи DMA в имеющийся контроллер диска до того как он получит данное IOMMU для трансляции.
Для решения такой проблеммы, имеющаяся спецификация PCI Express содержит поддержку для Служб управления доступом (ACS, Access Control Services) PCIe, которые предоставляют видимость и управление такими перенаправлениями. Это является неким существенным моментом для взаимной изоляции устройств, оторый часто упускается при взаимодействии и конечных точках со множеством функций. Без поддержки ACS на всех уровнях с самого устройства вплоть до IOMMU, причём она должна предполагать, что имеется возможность перенаправления. По данной причине, это разрушит изоляцию всех устройств вних от той точки, в которой упущена поддержка ACS в присутствующей топологии PCI. Группы IOMMU в некоторой среде PCI принимают во внимание такую изоляцию, собирая в группы устройства, которые способны понимать одноранговый DMA.
Суммируя, данное групирование IOMMU представляет собой наименьший набор устройств для которого имеет видимость конкретныое IOMMU и которая
изолируется от прочих групп. VFIO использует данную информацию для усиления безопасносго владения устройствами пространтсва пользователя.
За исключением присутствующих мостов, корневых портов и коммутаторов (всех примеров инфраструктуры интерконнекта
{- Прим. пер.- подробнее в нашем переводе Главы 4. Пространство адресов и маршрутизация транзакций из руководства "Технология PCI Express 3.0" Майка Джексона и
Рави Бадрака}), все устройства внутри какой- то группы IOMMU должны быть ограничены неким VFIO устройства, либо известным в своей
безопасности стабильном драйвере. Для PCI такими драйверами являются vfio-pci и pci-stub. pci-stub является допустимым просто потому, что
о нём известно, что его хост не взаимодействует с устройствами через такой драйвер. {Единственное исключение составляют назначение устаревшего
утройтва KVM, который часто взаимодействует с самим устройством, хотя и ограничен данным драйвером pci-stub. Red Hat Enterprise Linux 7 не содержит
назначения наследуемого устройства KVM, избегая такого взаимодействия и потенциального конфликта. Таким образом, не рекомендуется смешивать
применение VFIO и наследуемого назначения устройства KVM внутри одной и той же группы KVM.} Если возникает некая ошибка, котоя указывает, что
группа не жизнеспособна при использовании VFIO, это означает, что все присутствующие в этой группе устройства необходимо ограничить каким- то
подходящим драйвером хоста. Применение virsh nodedev-dumpxml
(см. Сбор установок настроек устройств) для изучения состава некоторой группы IOMMU и
virsh nodedev-detach
для связывания устройств с VFIO совместимыми драйверами поможет разешать
такие проблемы.
Данный пример демонстрирует как определять те устройства PCI, которые присутствуют в вашей целевой системе, Для дополнительных примеров и сведений отсылаем вас к разделу Назначение устройств GPU.
-
Список всех устройств
Идентификация всех устройств в вашей системе выполняется путём исполнения команды
virsh nodev-list device-type
. Данный пример показывает как определить местоположение конкретных устройств PCI. Для краткости вывод представлен в усечённом виде.# virsh nodedev-list pci pci_0000_00_00_0 pci_0000_00_01_0 pci_0000_00_03_0 pci_0000_00_07_0 [...] pci_0000_00_1c_0 pci_0000_00_1c_4 [...] pci_0000_01_00_0 pci_0000_01_00_1 [...] pci_0000_03_00_0 pci_0000_03_00_1 pci_0000_04_00_0 pci_0000_05_00_0 pci_0000_06_0d_0
-
Нахождение конкретной группы IOMMU какого- то устройства
Для каждого из перечисленных устройств дальнейшая информация о самом устройстве, в том числе его группирование IOMMU, может быть найдена с помощью команды
virsh nodedev-dumpxml name-of-device
. Например, для определения конкретной группы IOMMU для устройства PCI с названиемpci_0000_04_00_0
(адрес PCI0000:04:00.0
) воспользуйтесь следующей командой:# virsh nodedev-dumpxml pci_0000_04_00_0
Эта команда создаст некий XML дамп, аналогичный такому:
<device> <name>pci_0000_04_00_0</name> <path>/sys/devices/pci0000:00/0000:00:1c.0/0000:04:00.0</path> <parent>pci_0000_00_1c_0</parent> <capability type='pci'> <domain>0</domain> <bus>4</bus> <slot>0</slot> <function>0</function> <product id='0x10d3'>82574L Gigabit Network Connection</product> <vendor id='0x8086'>Intel Corporation</vendor> <iommuGroup number='8'> <!--Вам следует применять именно этот блочный элемент--> <address domain='0x0000' bus='0x00' slot='0x1c' function='0x0'/> <address domain='0x0000' bus='0x00' slot='0x1c' function='0x4'/> <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/> <address domain='0x0000' bus='0x05' slot='0x00' function='0x0'/> </iommuGroup> <pci-express> <link validity='cap' port='0' speed='2.5' width='1'/> <link validity='sta' speed='2.5' width='1'/> </pci-express> </capability> </device>
-
Просмотр данных шины PCI
В том собранном в коллекцию выводе, что приведён выше, присутствует одна группа IOMMU с 4 устройствами. Именно это является неким примером какого- то порта корня PCIe со множеством функций без поддержки ACS. Указанные две функции в слоте
0x1c
являются портами корня PCIe, который может быть определён путём исполнения командыlspci
(из соответствующего пакетаpciutils
):# lspci -s 1c 00:1c.0 PCI bridge: Intel Corporation 82801JI (ICH10 Family) PCI Express Root Port 1 00:1c.4 PCI bridge: Intel Corporation 82801JI (ICH10 Family) PCI Express Root Port 5
Повторите этот этап для своиз двух устройств в шинах
0x04
и0x05
, которые являются оконечными устройствами.# lspci -s 4 04:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection This is used in the next step and is called 04:00.0 # lspci -s 5 Это устройство применяется на следующем шаге и именуется как 05:00.0> 05:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5755 Gigabit Ethernet PCI Express (rev 02)
-
Назначение определённой конечной точки гостевой виртуальной машине
Чтобы назначит каждую из имеющихсяконечных точек какой- то виртуальной машине, та оконечная тчка, которую вы ещё не назначии на данный момент должна быть заключена в какой- то VFIO совместимый драйвер с тем, чтобы получаеая группа IOMMU не расщеплялась между пользователем и драйверами хоста. Если, скажем, воспользоваться полученным выше выводом, в котором вынастроили некую виртуальную машину только с
04:00.0
, следующая определяемая виртуальная машина будет отказывать в запуске пока05:00.0
не будет отключён от драйверов хоста. Чтобы отключить05:00.0
, исполните командуvirsh nodedev-detach
от имени root:# virsh nodedev-detach pci_0000_05_00_0 Device pci_0000_05_00_0 detached
Другим вариантом разрешения данной проблемы является назначение обеих конечных точек в одну виртуальную машину. Отметим, что
libvirt
автоматически осуществляет данную операцию для всех подключаемых устройств когда применяется значение yes для её атрибутаmanaged
внутри элемента<hostdev>
. Например:<hostdev mode='subsystem' type='pci' managed='yes'>
. Для дополнительных пояснений отсылаем к следующему Замечанию:Замечание libvirt
имеет два варианта обработки устройств PCI. Они могут быть либо управляемыми, либо не управляемыми. Это определяется тем значением, которое придаётся в атрибутеmanaged
внутри вводимого элемента<hostdev>
. Когда данное устройство является управляемым,libvirt
автоматически отключает это устройство от присутствующего драйвера и затем назначает его определяемой виртуальной машине привязывая её кvfio-pci
при загрузке (для этой виртуальной машины). Когда данная виртуальная машина завершается или удаляется из это устройство PCI отсоединяется от такой виртуалной машины,libvirt
отвязывает это устройство отvfio-pci
и повторно присоединяет его к первоначальному драйверу. Есди же данное устройство является не управляемым,libvirt
не будет автоматизировать данный процесс и вам придётся гарантировать, что все эти описанные моменты управления осуществлены перед назначением данного устройства какой- то виртуальной машине, а после того как данное устройство более не используется своей виртуальной машиной, вам придётся также переназначить это устройство. Забыв выполнить такие действия для неуправляемого устройства, вы придёте к тому, что ваша виртуальная машина завершится отказом. Таким образом, может оказаться более простым вариантом убелиться чтоlibvirt
осуществляет управление даным устройством.
Имеется множество вариантов для обрабатки групп IOMMU, которые содержат число устройств более желаемого. Для подключаемой (plug- in) карты самым первым вариантом было бы определение того, будет ли установка такой карты в различные слоты осуществлять желаемое группирование. В обычном наборе микросзем Intel, порты корня PCIe предоставляются и через сам процессор, и через Хаб контроллера платформы (PCH, Platform Controller Hub). Предоставляемые такими портами корня возможности могут отличаться существенным образом. Red Hat Enterprise Linux 7 имеет поддержку для выставления необходимой изоляции многочислнных портов корня PCH, даже несмотря на то, что многие из них не имеют естественной поддержки ACS PCIe. Таким образом, эти порты корня являются прекрасными целями для создания групп IOMMU меньшего размера. Для процессоров класса Intel® Xeon® (серия E5 и выше), а также "High End Desktop Processors", все порты корня PCIe на основе процессора обычно предоставляют естественную поддержку для ACS PCIe, однако клиентские процесоры нижнего уровня, такие как процессоры Core™ i3, i5 и i7, а также Xeon E3 не поддерживают. Для таких систем, самые гибкие конфигурации изоляции обычно предоставляют имеющиеся порты корня PCH.
Другой вариант состоит в том, чтобы работать с самими производителями оборудования для определения того, присутствует ли изоляция и подкорректировать используемеое ядро для распознания такой изоляции. Обычно это зависит от того, возможно ли одноранговое взаимодействие между функциями, либо в случае расположенных ниже по потоку портов, а также при определении того возможно ли перенаправление. Само ядро Red Hat Enterprise Linux 7 включает многообразные подстройки для таких устройств, а поддержка потребителей Red Hat может помочь вам в работе с производителями оборудования для определения того является ли доступной изоляция эквивалентная ACS и как наилучшим образом включить аналогичные корректировки в вашем ядре для выставления такой изоляции. Для производителей оборудования заметим, что многофункциональные точки, которые не поддерживают одноранговое взаимодействие, могут выставлять её с применением отдельной статической таблицы ACS в пространстве настройки, не предоставляя никаких возможностей. Добавление такой функциональности для данного оборудования позволит самому ядру автоматически определять такие функции как изолироанные и устранять данную проблему для всех пользователей вашего оборудования.
В том случае, когда приведённые выше соображения не доступны, обычная реакция состоит в том, что само ядро обязано предоставить некий вариант для отключения такой изоляции проверяя определённые устройства или конкретые типы устройств, задаваемые их пользователем. Обычно аргументация сводится к тому, что предыдущие технологии не применяли изоляцию до такой степени и "всё было нормально". К сожалению, обход стороной данных функций изоляции ведёт к неприемлемому окружению. Отсутствие знания о том, что имеется изоляция равносилно тому что вы не знаете на самом деле не имеете понятия о том изолированы ли устройства и было бы лучше определиться с этим до того, как возникнет чрезвычайная ситуация. Просчёты с такими возможностями изоляции устройств могут быть чрезвычайно сложными для переключения и ещё более трудными для отслеживания изоляции устройства как самой причины появления проблем. Задание VFIO состоит в первую и самую важную очередь в защите самого ядра хоста от принадлежащих пользователю устройств, а группы IOMMU являются тем самым механизмом, который используется IOMMU для обеспечения такой изоляции.