Обработчик прерывания |
Предыдущая Содержание Следующая |
Большинство аппаратных интерфейсов управляются посредством обработчика прерывания. Оборудование прерывает процессор, чтобы просигнализировать об одном из двух возможных событий: прибыл новый пакет или завершена передача исходящего пакета. Сетевые интерфейсы могут также генерировать прерывания для сигнализации об ошибках, изменениях состояния соединения и так далее.
Обычная процедура прерывания может определить отличие между прерыванием прибытия нового пакета и уведомлением о выполнении передачи, проверяя регистр статуса находящийся на физическом устройстве. Интерфейс 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 (который мы уже видели). |
Предыдущая Содержание Следующая |