Linux pci express sata

Linux pci express sata

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux

Цилюрик О.И. Модули ядра Linux
Назад Обслуживание периферийных устройств Вперед

Устройства на шине PCI

Архитектура PCI была разработана в качестве замены стандарту ISA с тремя основными целями: получить лучшую производительность при передаче данных между компьютером и его периферией, быть независимой от платформы, насколько это возможно, и упростить добавление и удаление периферийных устройств в системе. В настоящее время PCI широко используется в разных архитектурах: IA-32 / IA-64, Alpha, PowerPC, SPARC64 . Самой актуальной для автора драйвера является поддержка PCI автоопределения интерфейса плат: PCI устройства настраивается автоматически во время загрузки. Затем драйвер устройства получает доступ к информации о конфигурации устройства, и производит инициализацию. Это происходит без необходимости совершать какое-либо тестирование.

Каждое периферийное устройство PCI идентифицируется по подключению такими физическими параметрами, как: номер шины, номер устройства и номер функции. Linux дополнительно вводит и поддерживает такое логическое понятие как домен PCI. Каждый домен PCI может содержать до 256 шин. Каждая шина содержит до 32 устройств, каждое устройство может быть многофункциональным и поддерживать до 8 функций. В конечном итоге, каждая функция может быть однозначно идентифицирована на аппаратном уровне 16-ти разрядным ключом. Однако, драйверам устройств в Linux, не требуется иметь дело с этими двоичными ключами, потому что они используют для работы с устройствами специальную структуру данных pci_dev .

Примечание: Часто то, что мы житейски и физически (плата PCI) понимаем как устройство, в этой системе терминологически правильно называется: функция, устройство же может содержать до 8-ми эквивалентных (по своим возможностям) функций.

Адресацию PCI устройств в своей Linux системе смотрим:

Другое представление той же информации (тот же хост) можем получить так:

Здесь отчётливо видно (слева) поля, например для контроллера VGA: 0000:00:02.0 — выделены домен (16 бит), шина (8 бит), устройство (5 бит) и функция (3 бита). Поэтому, когда мы говорим об устройстве (далее), мы имеем в виду набор: номера домена + номер шины + номер устройства + номер функции.

С другой стороны, каждое устройство по типу идентифицируется двумя индексами: индекс производителя (Vendor ID) и индекс типа устройства (Device ID). Эта пара однозначно идентифицирует тип устройства. Использование 2-х основных идентификаторов устройств PCI (Vendor ID + Device ID) глобально регламентировано, и их актуальный перечень поддерживается в файле pci.ids , последнюю по времени копию которого можно найти в нескольких местах интернет, например по URL: http://pciids.sourceforge.net/ . Эти два параметра являются уникальным (среди всех устройств в мире) ключом поиска устройств, установленных на шине PCI. Для поиска (перебора устройств, установленных на шине PCI) в программном коде модуля в цикле используется итератор:

— где from — это NULL при начале поиска (или возобновлении поиска с начала), или указатель устройства, найденного на предыдущем шаге поиска. Если в качестве Vendor ID и/или Device ID указана константа PCI_ANY_ID=-1 , то предполагается выбор всех доступных устройств с таким идентификатором. Если искомое устройство не найдено (или больше таких устройств не находится в цикле), то очередной вызов возвратит NULL . Если возвращаемое значение не NULL , то возвращается указатель структуры описывающей устройство, и счётчик использования для устройства инкрементируется. Когда устройство удаляется (модуль выгружается) для декремента этого счётчика использования необходимо вызвать:

После нахождения устройства, но прежде начала его использования необходимо разрешить использование устройства вызовом: pci_enable_device( struct pci_dev *dev) , часто это выполняется в функции инициализации устройства: поле probe структуры struct pci_driver (см. далее), но может выполняться и автономно в коде драйвера.

Каждое найденное устройство имеет своё пространство конфигурации, значения которого заполнены программами BIOS (или PnP OS, или BSP) — важно, что на момент загрузки модуля эта конфигурационное пространство всегда заполнено, и может только читаться (не записываться). Пространство конфигурации PCI устройства состоит из 256 байт для каждой функции устройства (для устройств PCI Express расширено до 4 Кб конфигурационного пространства для каждой функции) и стандартизированную схему регистров конфигурации. Четыре начальных байта конфигурационного пространства должны содержать уникальный ID функции (байты 0-1 — Vendor ID, байты 2-3 — Device ID), по которому драйвер идентифицирует своё устройство. Вот для сравнения начальные строки вывода команды для того же хоста (видно, через двоеточие, пары: Vendor ID — Device ID):

Первые 64 байт конфигурационной области стандартизованы, остальные зависят от устройства. Самыми актуальными для нас являются (кроме ID описанного выше) поля по смещению:

Вся регистрация устройства PCI и связывание его параметров с кодом модуля происходит исключительно через значения, считанные из конфигурационного пространства устройства. Обработку конфигурационной информации (уже сформированной при установке PCI устройства) показывает модуль (архив pci.tgz ) lab2_pci.ko (заимствовано из [6]):

Небольшой фрагмент результата выполнения этого модуля:

$ sudo insmod lab2_pci.ko

$ lsmod | grep lab

$ dmesg | tail -n221 | head -n30

Читайте также:  Linux куда монтируется сетевая папка

$ sudo rmmod lab2_pci

$ lsmod | grep lab2

Для использования некоторой группы устройства PCI, код модуля определяет массив описания устройств, обслуживаемых этим модулем. Каждому новому устройству в этом списке соответствует новый элемент. Последний элемент массива всегда нулевой, это и есть признак завершения списка устройств. Строки такого массива заполняются макросом PCI_DEVICE :

Созданная структура pci_device_id должна быть экспортирована в пользовательское пространство, чтобы позволить системам горячего подключения и загрузки модулей знать, с какими устройствами работает данный модуль. Эту задачу решает макрос MODULE_DEVICE_TABLE :

Кроме доступа к области конфигурационных параметров, программный код может получить доступ к областям ввода-вывода и регионов памяти, ассоциированных с PCI устройством. Таких областей ввода-вывода может быть до 6-ти (см. формат области конфигурационных параметров выше), они индексируются значением от 0 до 5. Параметры этих регионов получаются функциями:

— где bar во всех вызовах — это индекс региона: 0 . 5. Первые 2 вызова возвращают начальный и конечный адрес региона ввода-вывода ( pci_resource_end() возвращает последний используемый регионом адрес, а не первый адрес, следующий после этого региона.), следующий вызов — его размер, и последний — флаги. Полученные таким образом адреса областей ввода/вывода от устройства — это адреса на шине обмена (адреса шины, для некоторых архитектур — x86 из числа таких — они совпадают с физическими адресами памяти). Для использования в коде модуля они должны быть отображены в виртуальные адреса (логические), в которые отображаются страницы RAM посредством устройства управления памятью (MMU). Кроме того, в отличие от обычной памяти, часто эти области ввода/вывода не должны кэшироваться процессором и доступ не может быть оптимизирован. Доступ к памяти таких областей должен быть отмечен как «без упреждающей выборки». Всё, что относится к отображению памяти будет рассмотрено отдельно далее, в следующем разделе. Флаги PCI региона ( pci_resource_flags() ) определены в
; некоторые из них:

IORESOURCE_IO, IORESOURCE_MEM — только один из этих флагов может быть установлен.

IORESOURCE_PREFETCH — определяет, допустима ли для региона упреждающая выборка.

IORESOURCE_READONLY — определяет, является ли регион памяти защищённым от записи.

Основной структурой, которую должны создать все драйверы PCI для того, чтобы быть правильно зарегистрированными в ядре, является структура (
):

  • name — имя драйвера, оно должно быть уникальным среди всех PCI драйверов в ядре, обычно устанавливается таким же, как и имя модуля драйвера, когда драйвер загружен в ядре, это имя появляется в /sys/bus/pci/drivers/ ;
  • id_table — только что описанный массив записей pci_device_id ;
  • probe — функция обратного вызова инициализации устройства; в функции probe драйвера PCI, прежде чем драйвер сможет получить доступ к любому ресурсу устройства (область ввода/вывода или прерывание) данного PCI устройства, драйвер должен, как минимум, вызвать функцию :
  • remove — функция обратного вызова удаления устройства;
  • . и другие функции обратного вызова.

Обычно для создания правильную структуру struct pci_driver достаточно бывает определить, как минимум, поля :

Теперь устройство может быть зарегистрировано в ядре:

— вызов возвращает 0 если регистрация устройства прошла успешно.

При завершении (выгрузке) модуля выполняется обратная операция:

Источник

В Raspberry Pi OS появилась встроенная поддержка SATA

Спустя несколько месяцев тестирования различных SATA-карт на Raspberry Pi Compute Module 4, в ядро Raspberry Pi OS наконец-то была добавлена встроенная поддержка SATA.

До этого апдейта, если вы хотели использовать SATA HDD или SSD на их нативных скоростях и иметь при этом возможность собрать RAID-массив, то требовалось перекомпилировать ядро Linux, добавив поддержку SATA и AHCI.

Конечно же, всегда можно было использовать HDD и SSD через адаптеры SATA – USB, но это решение отнимало 10-20% от их быстродействия и не давало возможности собрать RAID-массив, по крайней мере не без дополнительных ухищрений.

Перекомпиляция ядра – это в общем-то не ракетостроение, и я даже собрал среду кросс-компиляции, чтобы данный процесс упростить. Но все же задача эта помимо того, что напрягает, так еще отнимает драгоценное время и требует регулярного повторения, если мы хотим поддерживать актуальность Pi.

Однако в этом месяце Raspberry Pi OS получила-таки встроенную поддержку практически всех контроллеров PCI Express – SATA. Теперь для ее активации достаточно лишь выполнить:

Это означает, что все обладатели Compute Module 4 могут вставить SATA-карту и подключать HDD или SSD, которые при условии достаточного питания будут прекрасно работать.

Подобная возможность меня особенно радует, потому что одна из основных мотиваций добавить поддержку возникла, когда я проводил тестирование совместимости PCI Express-карт с Pi для соответствующей базы данных. При этом сам код, добавляющий поддержку, стал моим первым пул-реквестом к ветке ядра Linux для Raspberry Pi.

А если задуматься, то буквально год назад я вообще не имел опыта компиляции ядра.

Нативная поддержка SATA означает возможность использования таких инструментов, как OpenMediaVault, для создания RAID NAS на базе Raspberry Pi без необходимости обслуживания кастомного ядра или выполнения дополнительной настройки.

В качестве же личного опыта я понял, что писать патчи для ядра Linux не так сложно, как мне казалось (хотя, если честно, мне не пришлось работать со списком рассылки, поскольку мой патч ушел прямиком в ветку дерева ядра Pi OS).

Читайте также:  Intel manager engine interface driver windows 10

Хочу также отметить техническую скромность Raspberry Pi, благодаря которой я успешно реализовал проект. Именно дешевизна этого устройства и удобство его перепрошивки добавили смелости моим экспериментам, так как я знал, что в случае ошибки, она обойдется недорого.

Думаю, что многие, кто привык к работе с громоздкими настольными системами и серверами, не понимают то чувство облегчения, которое испытываешь от возможности использовать крохотный любительский компьютер, наподобие Pi.

Жаль только, что пока нельзя загружать Pi с SATA-диска. Можно загрузить его с USB, microSD, а на последних OS даже с NVMe, но в своей текущей версии загрузчик Raspberry Pi при старте не сканирует устройства SATA. Хотя это только пока.

Источник

Прерывания от внешних устройств в системе x86. Часть 2. Опции загрузки ядра Linux

В предыдущей части мы рассмотрели эволюцию доставки прерываний от устройств в x86 системах (PIC → APIC → MSI), общую теорию и все необходимые термины.

В этой практической части мы рассмотрим как откатиться к использованию устаревших методов доставки прерываний в Linux, а именно рассмотрим опции загрузки ядра:

  • pci=nomsi
  • noapic
  • nolapic

Также мы посмотрим на порядок, в котором ОС смотрит таблицы роутинга прерываний (ACPI/MPtable/$PIR) и какое влияние на него окажет добавление опций загрузки:

  • pci=noacpi
  • acpi=noirq
  • acpi=off

Возможно вы пробовали комбинации из всех этих опций, когда какое-либо устройство не работало из-за проблемы с прерываниями. Разберём, что именно они делают и как они меняют вывод /proc/interrupts.

Загрузка без дополнительных опций

Смотреть прерывания в данной статье мы будем на кастомной плате с Intel Haswell i7 с чипсетом lynxPoint-LP на которой запущен coreboot.

Информацию о прерываниях мы будем выводить через команду

Вывод при загрузке без дополнительных опций:

Файл /proc/interrupts предоставляет таблицу о количестве прерываний на каждом из процессоров в следующем виде:

  • Первая колонка: номер прерывания
  • Колонки CPUx: счётчики прерываний на каждом из процессоров
  • Следующая колонка: вид прерывания:
    • IO-APIC-edge — прерывание по фронту на контроллер I/O APIC
    • IO-APIC-fasteoi — прерывание по уровню на контроллер I/O APIC
    • PCI-MSI-edge — MSI прерывание
    • XT-PIC-XT-PIC — прерывание на PIC контроллер (увидим позже)
  • Последняя колонка: устройство, ассоциированное с данным прерыванием

Так вот, как и положено в современной системе, для устройств и драйверов, поддерживающих прерывания MSI/MSI-X, используются именно они. Остальные прерывания роутятся через I/O APIC.

Упрощённо схему роутинга прерываний можно нарисовать так (красным помечены активные пути, чёрным неиспользуемые).

Поддержка MSI/MSI-X устройством должна быть обозначена как соответствующая Capability в его конфигурационном пространстве PCI.

В подтверждении приведём небольшой фрагмент вывода lspci для устройств, для которых обозначено, что они используют MSI/MSI-X. В нашем случае это SATA контроллер (прерывание ahci), 2 ethernet контроллера (прерывания eth58* и eth59*), графический контроллер (i915) и 2 контроллера HD Audio (snd_hda_intel).

Как мы видим, у этих устройств присутствует строка либо «MSI: Enable+», либо «MSI-X: Enable+»

Начнём деградировать систему. Для начала загрузимся с опцией pci=nomsi.

pci=nomsi

Благодаря этой опции MSI прерывания станут IO-APIC/XT-PIC в зависимости от используемого контроллера прерываний

В данном случае у нас всё ещё приоритетный контроллер прерываний APIC, так что картина будет такая:

Все прерывания MSI/MSI-X ожидаемо исчезли. Вместо них устройства теперь используют прерывания вида IO-APIC-fasteoi.

Обратим внимание на то, что раньше до включения этой опции у eth58 и eth59 было по 9 прерываний! А сейчас только по одному. Ведь как мы помним, без MSI одной функции PCI доступно только одно прерывание!

Немного информации из dmesg по инициализации ethernet контроллеров:

— загрузка без опции pci=nomsi:

— загрузка с опцией pci=nomsi

Из-за уменьшения количества прерываний на устройство, включение данной опции может приводить к существенному ограничению производительности работы драйвера (это без учёта того, что согласно исследованию Intel Reducing Interrupt Latency Through the Use of Message Signaled Interrupts прерывания через MSI в 3 раза быстрее чем через IO-APIC и в 5 раз быстрее чем через PIC).

noapic

Данная опция отключает I/O APIC. MSI прерывания всё ещё могут идти на все CPU, но прерывания от устройств смогут идти только на CPU0, так как PIC связан только с CPU0. Но LAPIC работает и другие CPU могут работать и обрабатывать прерывания.

Как видим, все прерывания IO-APIC-* превратились в XT-PIC-XT-PIC, причём эти прерывания роутятся только на CPU0. Прерывания MSI остались без изменений и идут на все CPU0-3.

nolapic

Отключает LAPIC. MSI прерывания не могут работать без LAPIC, I/O APIC не может работать без LAPIC. Поэтому все прерывания от устройств будут идти на PIC, а он работает только с CPU0. И без LAPIC остальные CPU даже работать в системе не будут.

Комбинации:

На самом деле всего одна для нового варианта: «noapic pci=nomsi». Все прерывания от устройств смогут идти только на CPU0 через PIC. Но LAPIC работает и другие CPU могут работать и обрабатывать прерывания.

Одна, потому что с «nolapic» можно ничего не комбинировать, т.к. эта опция и так сделает недоступным I/O APIC и MSI. Так что если вы когда-то прописывали опции загрузки «noapic nolapic» (или самый распространённый вариант «acpi=off noapic nolapic»), то судя по всему вы набирали лишние буквы.

Читайте также:  Как можно использовать linux

Итак, что будет от опций «noapic pci=nomsi»:

Таблицы роутинга прерываний и опции «acpi=noirq», «pci=noacpi», «acpi=off»

Как операционная система получает информацию о роутинге прерываний от устройств? BIOS подготавливает информацию для ОС в виде:

  • ACPI таблиц (методы _PIC/_PRT)
  • _MP_ таблицы (MPtable)
  • $PIR таблицы
  • Регистров 0x3C/0x3D конфигурационного пространства PCI устройств

Следует отметить, что для обозначения прерываний MSI BIOSу не надо ничего дополнительно делать, вся вышеупомянутая информация нужна только для линий APIC/PIC прерываний.

Таблицы в списке выше обозначены в порядке приоритета. Рассмотрим это подробней.

Допустим BIOS предоставил все эти данные и мы грузимся без каких-либо дополнительных опций:

  • ОС находит таблицы ACPI
  • ОС выполняет метод ACPI «_PIC», передаёт ему аргумент, что нужно грузиться в режиме APIC. Тут код метода обычно сохраняет выбранный режим в переменной (допустим PICM=1)
  • Для получения данных о прерываниях ОС вызывает метод ACPI «_PRT». Он внутри себя проверяет переменную PICM и возвращает роутинг для APIC случая

В случае если мы грузимся с опцией noapic:

  • ОС находит таблицы ACPI
  • ОС выполняет метод ACPI «_PIC», передаёт ему аргумент, что нужно грузиться в режиме PIC. Тут код метода обычно сохраняет выбранный режим в переменной (допустим PICM=0)
  • Для получения данных о прерываниях ОС вызывает метод ACPI «_PRT». Он внутри себя проверяет переменную PICM и возвращает роутинг для PIC случая

Если таблица ACPI отсутствует или функционал роутинга прерываний через ACPI отключен с помощью опций acpi=noirq или pci=noacpi (или ACPI полностью выключен с помощью acpi=off), то ОС смотрит для роутинга прерываний таблицу MPtable (_MP_):

  • ОС не находит/не смотрит таблицы ACPI
  • ОС находит таблицу MPtable (_MP_)

Если таблица ACPI отсутствует или функционал роутинга прерываний через ACPI отключен с помощью опций acpi=noirq или pci=noacpi (или ACPI полностью выключен с помощью acpi=off) и если таблица MPtable (_MP_) отсутствует (или передана опция загрузки noapic или nolapic):

  • ОС не находит/не смотрит таблицу ACPI
  • ОС не находит/не смотрит таблицу MPtable (_MP_)
  • ОС находит таблицу $PIR

Если и таблицы $PIR нет, или она не полна, то операционная система для угадывания прерываний будет смотреть значения регистров 0x3C/0x3D конфигурационного пространства PCI устройств.

Суммируем всё вышеизложенное следующей картинкой:

Следует помнить, что не каждый BIOS предоставляет все 3 таблицы (ACPI/MPtable/$PIR), так что если вы передали опцию загрузчику отказаться от использования ACPI или ACPI и MPtable для роутинга прерываний, далеко не факт, что ваша система загрузится.

Замечание 1: в случае если мы попытаемся загрузиться в режиме APIC с опцией acpi=noirq и без наличия MPtable, то картина прерываний будет как и в случае обычной загрузки с единственной опцией noapic. Операционная система сама перейдёт в режим PIC прерываний.
В случае если мы попытаемся загрузиться вообще без таблиц ACPI (acpi=off) и не предоставив MPtable, то картина будет такая:

Это проиcходит из-за того, что без ACPI таблицы MADT (Multiple APIC Description Table) и необходимой информации из MPtable, операционная система не знает APIC идентификаторы (APIC ID) для других процессоров и не может с ними работать, но LAPIC основного процессора работает, так как мы это не запрещали, и MSI прерывания могут на него приходить. То есть будет так:

Замечание 2: в целом роутинг прерываний при использовании ACPI в случае APIC совпадает с роутингом прерываний через MPtable. А роутинг прерываний через ACPI в случае использования PIC совпадает с роутингом прерываний через $PIR. Так что и выводы /proc/interrupts отличаться не должны. Однако в процессе исследований заметил одну странность. При роутинге через MPtable в выводе почему-то присутствует каскадное прерывание «XT-PIC-XT-PIC cascade».

Немного странно, что так происходит, но в документации ядра вроде говорится, что это нормально.

Заключение:

В заключении ещё раз обозначим разобранные опции.

Опции выбора контроллера прерываний:

  • pci=nomsi — MSI прерывания станут IO-APIC/XT-PIC в зависимости от используемого контроллера прерываний
  • noapic — Отключает I/O APIC. MSI прерывания всё ещё могут идти на все CPU, остальные прерывания от устройств смогут идти только на PIC, а он работает только с CPU0. Но LAPIC работает и другие CPU могут работать и обрабатывать прерывания
  • noapic pci=nomsi — Все прерывания от устройств могут идти только на PIC, а он работает только с CPU0. Но LAPIC работает и другие CPU могут работать и обрабатывать прерывания
  • nolapic — Отключает LAPIC. MSI прерывания не могут работать без LAPIC, I/O APIC не может работать без LAPIC. Все прерывания от устройств будут идти на PIC, а он работает только с CPU0. И без LAPIC остальные CPU не будут работать.

Опции выбора приоритетной таблицы роутинга прерываний:

  • без опций — роутинг через APIC с помощью таблиц ACPI
  • noapic — роутинг через PIC с помощью таблиц ACPI
  • acpi=noirq (pci=noacpi/acpi=off) — роутинг через APIC с помощью таблицы MPtable
  • acpi=noirq (pci=noacpi/acpi=off) noapic (nolapic) — роутинг через PIC с помощью таблицы $PIR

В следующей части посмотрим как coreboot настраивает чипсет для роутинга прерываний.

Источник

Оцените статью