Обработка и маршрутизация прерываний

Эта глава обеспечивает инструкции, чтобы помочь Вам поддерживать использование устройств Удара молнии Message Signaled Interrupts (MSI) и понять, как реагировать на устройства, не поддерживающие MSI. Существует также раздел по операциям замены в горячем режиме с Устройствами PCI, описывающий, как изменить драйверы PCI так, чтобы они были в состоянии иметь дело с незапланированными разъединениями.

Самые современные устройства PCI поддерживают гибкость при контакте с маршрутизацией прерывания. С появлением PCI-X (расширенный PCI) и PCIe (PCI Express), сообщение Сообщенные Прерывания были представлены как внутриполосный механизм для утверждения прерываний.

Создание только способных к MSI устройств удара молнии

Apple строго рекомендует создать только способные к MSI устройства Удара молнии. Гарантируйте, чтобы Ваши драйверы OS X включили MSI при поддержке устройств Удара молнии. Для упрощения разработки Mac Pro, компьютеры имеют слоты PCIe та поддержка MSI.

Службы OS X все прерывания на CPU 0 и использовании вторичный контекст прерывания распараллеливают для задержки работы к прерываниям уровня задачи, а не уровням основного прерывания. Этот метод гарантирует, что многократные драйверы могут работать параллельно на доступном CPUs, что OS может запланировать потоки в реальном времени с точностью, и что система остается быстро реагирующей к взаимодействию с пользователем. Методы наиболее успешной практики, описанные в Основных принципах IOKit, мотивируют использование модели абстракции IOWorkLoop для драйверов устройств для задержки работы к потоку IOWorkLoop. Может быть небольшое количество устройств, требующих, чтобы работа была сделана в основном контексте прерывания, однако, драйверы должны провести наименьшее количество количества времени, возможного в контексте основного прерывания.

Обработка аппаратных исключений для поддержки MSI

Аппаратные средства должны поддерживать MSI, но в маловероятном случае устройство не поддерживает MSI, следующие три сценария, наиболее вероятно, произойдут. Эти три сценария обычно происходят в водительской подпрограмме фильтра, и важно понять, что разветвления результатов возвратились из подпрограммы фильтра:

  1. Фильтр вызывают, но устройство не сигнализировало прерывание.

    Это может быть вследствие другого устройства, совместно использующего тот же контакт прерывания. Подпрограмма фильтра должна возвратиться false.

  2. Фильтр вызывают, и устройство сигнализировало прерывание.

    Драйвер не может замаскировать источник прерывания, и при этом это не может обработать прерывание на этом уровне по причинам производительности или блокировке. Подпрограмма фильтра должна возвратиться true.

  3. Фильтр вызывают, и устройство сигнализировало прерывание.

    1. Драйвер может обработать прерывание на этом уровне и возвратить устройство не состоянию прерывания. Если действие прерывания должно быть выполнено, подпрограмма фильтра должна вызвать signalInterrupt().

    2. Драйвер не может обработать прерывание на этом уровне, но это может препятствовать тому, чтобы устройство сигнализировало другое прерывание (например, путем управления внутренней маской).

      Подпрограмма фильтра должна препятствовать тому, чтобы устройство сигнализировало другое прерывание и затем вызвать signalInterrupt() заставить действие прерывания быть выполненным. Действие прерывания должно обработать прерывание и возвратить устройство состоянию, где это может снова сигнализировать прерывание. Подпрограмма фильтра должна возвратиться false.

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

Включение MSI

Для включения MSI драйвер устройства должен сделать следующий, предположив, что провайдер IOPCIDevice экземпляр:

Перечисление 3-1  , разрешающее MSIs

int    index  = 0;
int    source = 0;
for ( index = 0; ; index++ )
{
         IOReturn result      = kIOReturnSuccess;
         int interruptType    = 0;
 
         result = provider->getInterruptType ( index, &interruptType );
         if ( result != kIOReturnSuccess )
               break;
         if ( interruptType & kIOInterruptTypePCIMessaged )
        {
            source = index;
            break;
        }
}

Используя работу замены в горячем режиме с устройствами PCI

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

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

Драйверы устройств PCI, используемые с устройствами Удара молнии, возможно, должны быть обновлены для обработки удивления или незапланированного удаления. В частности циклы MMIO и доступы Конфигурации PCI требуют особого внимания. Когда устройство PCI, подключенное к порту Thunderbolt, отсоединяется от системы, Корневой порт PCIe должен привести к таймауту любых выдающихся транзакций, отправленных в устройство, завершить транзакцию, как будто Неподдерживаемый Запрос произошел на шине, и возвратите значение 0xFFFFFFFF. Корневой порт имеет значение тайм-аута завершения, которое является многими миллисекундами долго и варьируется в зависимости от системного расположения. Планирование в реальном времени, особенно для аудио и видео потоков, может быть затронуто транзакциями, которые должны быть приведены к таймауту.

Как основная инструкция, разработчики должны изменить свои драйверы для обработки возвращаемого значения 0xFFFFFFFF. Если какой-либо поток, обратный вызов, фильтр прерывания или путь выполнения кода в драйвере получают 0xFFFFFFFF указание устройство было отключено, тогда все потоки, обратные вызовы, фильтры прерывания, обработчики прерываний и другие пути выполнения кода в том драйвере, должно прекратить чтения MMIO и сразу пишет, и подготовитесь к завершению.

Если 0xFFFFFFFF юридическое значение для определенного смещения регистра, одного дополнительного чтения различного регистра, который, как известно, никогда не возвращается 0xFFFFFFFF если устройство все еще подключается, предпочтительный механизм для определения. Наконец, если Набор I/O уже выполнил завершение и вызвал водительское willTerminate() метод, никакие дальнейшие доступы не должны быть выполнены.

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

Когда никакой дальнейший доступ не должен быть выполнен, Apple рекомендует контролировать использование записей MMIO. Записи MMIO являются отправленными транзакциями, и для драйверов устройств возможно стоять в очереди многократные записи подряд.

Типичный способ для разработчика решить эту проблему состоит в том, чтобы обеспечить единственную подпрограмму узкого места для всех чтений MMIO и иметь ту проверку подпрограммы состояние устройства прежде, чем начать фактическую транзакцию. Пример такой подпрограммы с помощью полей с прямым порядком байтов для ее апертур с отображенной памятью следует:

Перечисление 3-2  единственная подпрограмма узкого места (чтение-запись MMIO)

class AppleSamplePCI
{
      ...
      bool                     fDeviceRemoved;
      volatile unit8_t *       fBaseAddressRegister;
      ...
      unit32_t        ReadRegister ( uint32_t  offset );
      virtual bool    willTerminate ( IOService * provider, IOOptionBits options );
};
 
unit32_t
AppleSamplePCI::ReadRegister ( unint32_t offset )
{
 
         unint32_t   result = 0xFFFFFFFF;
 
         if  ( !fDeviceRemoved )
         {
                result = OSReadLittleInt32 ( fBaseAddressRegister, offset );
                if ( result == 0xFFFFFFFF )
                    fDeviceRemoved = true;
         }
         return result;
}
 
bool
AppleSamplePCI::willTerminate ( IOService * provider, IOOptionBits options )
{
         fDeviceRemoved = true;
         return super::willTerminate ( provider, options );
}

Подобные подпрограммы могут быть записаны для транзакций цикла Конфигурации PCI, которые могут получить возвращаемое значение 0xFFFFFFFF и чтения MMIO меньших размеров.

Поддержка пауза PCIe

Поскольку Удар молнии позволяет дополнение и удаление произвольных чисел периферийных устройств, подключенных в произвольной топологии, задаче деления, адресное пространство дерева PCI может быть сложным. Иногда, особенно когда большие количества устройств присоединяются, возможно исчерпать части того адресного пространства. Когда это происходит, новое устройство не может быть включено, не перемещая существующие устройства.

Для решения этой проблемы OS X v10.9 поддерживает Паузу PCIe — специальное состояние управления питанием, в котором временно приостановлены весь драйвер и работа устройства. Каждый раз, когда исчерпание адресного пространства происходит, OS X может попросить, чтобы драйверы приостановили операции. После того, как драйверы приостанавливаются, OS X изменяет расположение адресного пространства приостановленных устройств для создания места для новых устройств, и затем говорит драйверам возобновлять нормальное функционирование.

Для поддержки паузы необходимо добавить дополнительное Info.plist ключ и новое состояние управления питанием (kIOPCIDevicePausedState) к Вашему драйверу, как описано в следующих разделах.

Объявите поддержку приостановки в Вашем файле Info.plist

Для объявления поддержки приостановки добавьте следующий ключ к каждому из лиц в водительском Info.plist файл:

<key>IOPCIPauseCompatible</key>
<true/>

Добавьте Поддержку kIOPCIDevicePausedState Состояния электропитания

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

Если Вы не добавляете состояний электропитания к своему драйверу, Ваш драйвер помещается в от состояния во время паузы, потому что состояние паузы не включает kIOPMPowerOn флаг. Любые клиенты драйвера в иерархии управления питанием тогда должны изменить свои состояния для соответствия.

Измененные драйверы могут поддерживать паузу явно путем добавления состояния электропитания с kIOPMConfigRetained отметьте набор inputPowerRequirement поле, заставляя то состояние быть выбранным системой управления питанием, когда устройство должно ввести приостановленное состояние. outputPowerCharacter значение водительского нового состояния электропитания тогда диктует то, что видят водительские клиенты управления питанием. В зависимости от этого флага клиенты Вашего драйвера могут или могут не изменить состояния.

Например, Ваша таблица состояния электропитания могла бы быть похожей на это:

static const IOPMPowerState powerStates[kYourDriverPowerStateCount] = {
 
    // version, capabilityFlags, outputPowerCharacter,
    // inputPowerRequirement, staticPower, unbudgetedPower
    // powerToAttain timeToAttain settleUpTime
    // timeToLower settleDownTime powerDomainBudget
 
    // Device off; inputPowerRequirement is 0,
    // which matches the flags for kIOPCIDeviceOffState.
    { kIOPMPowerStateVersion1, 0, 0,
      0, 0, 0,
      0, 0, 0,
      0, 0, 0 },
 
    // Sleep mode; inputPowerRequirement has kIOPMSoftSleep flag set,
    // which matches the flags for kIOPCIDeviceDozeState.
    { kIOPMPowerStateVersion1, 0, kIOPMSoftSleep,
      kIOPMSoftSleep, 0, 0,
      0, 0, 0,
      0, 0, 0 },
 
    // Device paused; inputPowerRequirement has kIOPMConfigRetained flag set,
    // which matches the flags for kIOPCIDevicePausedState.
    { kIOPMPowerStateVersion1, kIOPMConfigRetained, kIOPMConfigRetained,
      kIOPMConfigRetained, 0, 0,
      0, 0, 0,
      0, 0, 0 },
 
    // Device active; inputPowerRequirement has kIOPMPowerOn flag set,
    // which matches the flags for kIOPCIDeviceOnState.
    { kIOPMPowerStateVersion1, kIOPMPowerOn | kIOPMUsable, kIOPMPowerOn,
      kIOPMPowerOn, 0, 0,
      0, 0, 0,
      0, 0, 0 }
};

Чтобы избежать разрушать службу, запишите свой код в пути, делающем ввод и выход из состояния паузы максимально быстро. В частности Вы не должны выполнять весь код, который Вы использовали бы для a kIOPCIDeviceOffState/kIOPCIDeviceOnState переход, потому что устройство остается включенным через изменение состояния, делая полную реинициализацию ненужной.

Однако, когда OS X говорит Вашему драйверу приостанавливаться, Ваш драйвер должен все еще сделать многие вещи, которые это сделало бы, когда компьютер входит в безопасный сон — т.е. это должно сказать устройству прекращать выпускать дополнительные транзакции и затем ожидать, пока все выдающиеся транзакции не закончились перед тем, чтобы говорить OS X, который изменило состояние электропитания устройства.

В то время как приостанавливается драйвер:

  • Драйвер не должен получать доступ к устройству с помощью I/O с отображенной памятью или транзакций пространства конфигурации

  • Устройство не должно генерировать прерывания, или MSI или основанные на контакте прерывания

  • Устройство не должно генерировать запросы DMA

  • Устройство не должно быть целью никаких запросов DMA

Когда драйвер возобновляется после паузы должен действовать драйвер, как будто компьютер просто проснулся от безопасного сна, но не выполняя ненужной инициализации аппаратного обеспечения (потому что устройство осталось приводимым в действие). В частности это должно определить, изменило ли устройство адреса, и если это имеет, это должно использовать новые физические адреса для всей будущей связи с устройством. Следующие значения, возможно, изменились:

  • Индексные регистры устройства (BARs)

  • Номер шины устройства

  • Свойства реестра, отражающие эти значения: "ranges", "assigned-addresses", и "reg"

  • Возможность MSI устройства регистрирует блочные значения для адреса и значение, но не число выделенного MSIs

Следующие значения не изменятся:

  • Регистры блока конфигурации управления питанием PCI устройства — т.е. устройство не будет помещено в состояние сна устройства (D3 во время выполнения)

  • Виртуальные адреса BARs

    Любые отображения, ранее создаваемые драйвером с IOPCIDevice::mapDeviceMemoryWithRegister или IOPCIDevice::mapDeviceMemoryWithIndex поскольку доступ I/O с отображенной памятью к аппаратным средствам автоматически повторно отображается с тем же виртуальным адресом и новым физическим адресом

  • Любые другие регистры конфигурации

  • Набор доступных ресурсов BAR

    Любое настоящее ресурса BAR в паузе, как гарантируют, будет перераспределено — т.е. устройство никогда не будет терять ресурсы через реконфигурирование.

  • Иерархия реестра устройства

  • Число или вид присвоений прерывания (совместно использованный по сравнению с MSI)

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

Возврат Операций I/O как Ошибки

Драйверы должны гарантировать любые I/Os, которые находятся в рейсе во время неожиданного удаления, должным образом возвращаются как ошибки к верхним уровням, выпустившим запросы I/O. Точный способ, которым это сделано, является определенной семьей I/O.