понедельник, 11 апреля 2011 г.

10.5. Разделяемые прерывания


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

Конечно, современное техническое оснащение было разработано, чтобы позволить совместное использование прерываний; шина PCI требует этого. Таким образом, ядро Linux поддерживает совместное использование прерывания на всех шинах, даже на тех (таких, как шины ISA), где совместное использование традиционно не поддерживалось. Драйверы устройств для ядра версии 2.6 должны быть написаны для работы с разделяемыми прерываниями, если целевое оборудование поддерживает этот режим работы. К счастью, работа с разделяемыми прерываниями в большинстве случаев проста.
Установка обработчика разделяемого прерывания
Разделяемые прерывания устанавливаются через request_irq, похожую на такую же для неразделяемых, но есть два отличия:

В аргументе flags при запросе прерывания должен быть указан бит SA_SHIRQ.

Аргумент dev_id должен быть уникальным. Он будет любым указателем в адресном
пространстве модуля, но dev_id, безусловно, не может быть установлен в NULL.

Ядро хранит список обработчиков разделяемых прерываний, связанных с прерыванием, и dev_id может рассматриваться как подпись, которая их различает. Если бы два драйвера зарегистрировали NULL в качестве подписи на одном прерывании, всё могло бы запутаться во время выгрузки, в результате чего ядро при получении прерывания сказало бы Oops. По этой причине современные ядра громко жалуются, если при регистрации разделяемых прерываний в dev_id передан NULL. Когда запрашивается разделяемое прерывание request_irq успешна, если одно из следующего верно:
  • Эта линия прерывания свободна.
  • Все обработчики, уже зарегистрированные для этой линии, также уточнили, что
    это прерывание будет разделяться.


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

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

Отключение обработчика производится в обычном порядке с использованием free_irq. Для выбора правильного обработчика для отключения из списка обработчиков для разделяемого прерывания здесь используется аргумент dev_id. Вот почему указатель dev_id должен быть уникальным.

Драйверу, использующему обработчик разделяемого прерывания, необходимо позаботиться ещё об одном: он не может играть с enable_irq или disable_irq. Если это произойдёт, всё может поломаться для других устройств, разделяющих эту же линию; запрет прерываний другого устройства даже на короткое время может создать задержки, которые являются проблематичными для такого устройства и его пользователя. Как правило, программист должен помнить, что его драйвер не является собственником такого прерывания и его поведение должно быть более "социальным", чем это необходимо, если он один владеет линией прерывания.
Работа обработчика
Как указывалось ранее, когда ядро получает прерывание, вызываются все зарегистрированные обработчики. Обработчики разделяемых прерываний должны быть в состоянии различить прерывания, которые необходимо обработать, и прерывания, генерируемые другими устройствами.

Загрузка short с вариантом shared=1 устанавливает следующий обработчик вместо используемого по умолчанию:

irqreturn_t short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    int value, written;
    struct timeval tv;

    /* Если это не для short, немедленно вернуться */
    value = inb(short_base);
    if (!(value & 0x80))
        return IRQ_NONE;

    /* очистить бит прерывания */
    outb(value & 0x7F, short_base);

    /* остальное остаётся неизменным */

    do_gettimeofday(&tv);
    written = sprintf((char *)short_head,"%08u.%06u\n",
                        (int)(tv.tv_sec % 100000000), (int)(tv.tv_usec));
    short_incr_bp(&short_head, written);
    wake_up_interruptible(&short_queue); /* разбудить любой читающий процесс */
    return IRQ_HANDLED;
}

Объяснение прямо здесь. Так как параллельный порт не имеет для проверки бита "ожидание прерывания", обработчик использует для этой цели бит ACK (подтверждение). Если бит установлен, прерывание предназначено для short и обработчик очищает этот бит.

Обработчик сбрасывает этот бит, обнуляя старший бит порта данных параллельного интерфейса - short предполагает, что контакты 9 и 10 соединены вместе. Если прерывание генерирует одно из других устройств, разделяющих прерывание с short, short видит, что его собственная линия является неактивной и ничего не делает.

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

Заметим, что если вы используете принтер (вместо провода для перемычки) для проверки управления прерываниями с short, этот обработчик разделяемого прерывания не будет работать как рассказывалось, потому что протокол принтера не позволяет совместное использование и драйвер не сможет узнать, было ли прерывание от принтера.
Интерфейс /proc и разделяемые прерывания
Установка в систему обработчиков разделяемых прерываний не влияет на /proc/stat, который даже не знает об обработчиках. Однако, /proc/interrupts немного изменяется.

Все обработчики, установленные для одного номера прерывания, появляются в одной строчке /proc/interrupts. Следующий вывод (на системе x86_64) показывает, как отображаются обработчики разделяемых прерываний:

            CPU0
 0:    892335412       XT-PIC  timer
 1:       453971       XT-PIC  i8042
 2:            0       XT-PIC  cascade
 5:            0       XT-PIC  libata, ehci_hcd
 8:            0       XT-PIC  rtc
 9:            0       XT-PIC  acpi
 10:    11365067       XT-PIC  ide2, uhci_hcd, uhci_hcd, SysKonnect SK-98xx, EMU10K1
 11:     4391962       XT-PIC  uhci_hcd, uhci_hcd
 12:         224       XT-PIC  i8042
 14:     2787721       XT-PIC  ide0
 15:      203048       XT-PIC  ide1
NMI:       41234
LOC:   892193503
ERR:         102
MIS:           0

Эта система имеет несколько общих линий прерываний. IRQ 5 используется для контроллеров serial ATA и IEEE 1394; IRQ 10 имеет несколько устройств, включая контроллер IDE, два USB контроллера, интерфейс Ethernet и звуковую карту; и IRQ 11 также используется двумя USB контроллерами.

Комментариев нет:

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.