Обработчик прерывания

Предыдущая  Содержание  Следующая V*D*V

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

 

Обычная процедура прерывания может определить отличие между прерыванием прибытия нового пакета и уведомлением о выполнении передачи, проверяя регистр статуса находящийся на физическом устройстве. Интерфейс snull работает аналогично, но его слово статуса реализовано в программном обеспечении и находится в dev->priv. Обработчик прерывания для сетевого интерфейса выглядит следующим образом:

 

static irqreturn_t snull_regular_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

    int statusword;

    struct snull_priv *priv;

    struct snull_packet *pkt = NULL;

    /*

     * Как обычно, проверяем указатель "устройства" для уверенности,

     * что прервало действительно оно.

     * Затем присваиваем "struct device *dev"

     */

    struct net_device *dev = (struct net_device *)dev_id;

    /* ... и проверяем оборудование, если оно действительно наше */

 

    /* параноидальность */

    if (!dev)

        return;

 

    /* Блокируем устройство */

    priv = netdev_priv(dev);

    spin_lock(&priv->lock);

 

    /* получаем статусное слово: настоящее сетевое устройство использует инструкции ввода/вывода */

    statusword = priv->status;

    priv->status = 0;

    if (statusword & SNULL_RX_INTR) {

        /* отправляем его в snull_rx для обработки */

        pkt = priv->rx_queue;

        if (pkt) {

            priv->rx_queue = pkt->next;

            snull_rx(dev, pkt);

        }

    }

    if (statusword & SNULL_TX_INTR) {

        /* передача завершена: освобождаем skb */

        priv->stats.tx_packets++;

        priv->stats.tx_bytes += priv->tx_packetlen;

        dev_kfree_skb(priv->skb);

    }

 

    /* Разблокируем устройство и заканчиваем */

    spin_unlock(&priv->lock);

    if (pkt) snull_release_buffer(pkt); /* Делаем это вне блокировки! */

    return IRQ_HANDLED;

}

 

Первой задачей обработчика является получение указателя на корректную struct net_device. Этот указатель обычно поступает от указателя dev_id, полученного в качестве аргумента.

 

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

 

dev_kfree_skb(struct sk_buff *skb);

Эта версия может быть вызвана, когда вы знаете, что ваш код не будет работать в контексте прерывания. Так как snull не имеет фактического аппаратного прерывания, мы и используем эту версию.

 

dev_kfree_skb_irq(struct sk_buff *skb);

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

 

dev_kfree_skb_any(struct sk_buff *skb);

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

 

Наконец, если ваш драйвер временно остановил очередь передачи, это обычное место для её перезапуска с помощью netif_wake_queue.

 

Приём пакета, в отличие от передачи, не требует специальной обработки прерывания. Всё, что требуется, это вызвать snull_rx (который мы уже видели).

Предыдущая  Содержание  Следующая