Готовый перевод Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 17.6. Прием пакетов
17.6. Прием пакетов
Прием данных из сети сложнее, чем их передача, потому что sk_buff должен создаваться и обрабатываться верхними уровнями атомарно.
Существуют 2 режима приема пакетов, которые могут быть реализованы
сетевым драйвером: через прерывания и опрос. Большинство драйверов
используют прерывания, и этот режим мы рассмотрим в первую очередь.
Некоторые драйверы, для широкополосных адаптеров, могут
реализовывать режим опроса, мы рассмотрим этот способ в подразделе
«Подавление приема прерываний».
Реализация snull разделяет аппаратно-зависимые и аппаратно-
независимые действия. Поэтому функция snull_rx вызывается из
обработчика прерывания snull после приема пакета аппаратурой, и где
он передается в память компьютера. snull_rx получает указатель на
данные и длину пакета. Это ответственная задача - передать пакет и
дополнительные поля на верхние уровни сетевого кода. Этот код не
зависит от полученных данных и их длинны.
void snull_rx(struct net_device *dev, struct snull_packet *pkt)
{
struct sk_buff *skb;
struct snull_priv *priv = netdev_priv(dev);
/*
* Пакет принимается из передающей среды. Формируется
* skb, чтобы верхние уровни могли обработать его.
*/
skb = dev_alloc_skb(pkt->datalen + 2);
if (!skb) {
if (printk_ratelimit( ))
printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
priv->stats.rx_dropped++;
goto out;
}
memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
/* формируем метаданные и приступаем к приему пакета */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* контрольная сумма не нужна */
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
netif_rx(skb);
out:
return;
}
Функция достаточно общая, чтобы послужить шаблоном, но, для
того чтобы вы могли уверенно пользоваться этим фрагментом кода,
необходимы некоторые пояснения.
На первом шаге выделяется буфер под пакет. Обратите внимание,
что функции выделения буфера dev_alloc_skb, необходимо передать его
длину. dev_alloc_skb вызывает kmalloc атомарно, таким образом, это
может быть безопасно использовано в обработчике прерывания. Ядро
предлагает другие интерфейсы для выделения буферов сокета, но они не
приведены здесь. Буферы сокета детально рассмотрены в подразделе
«Буферы сокетов».
Значение, возвращаемое dev_alloc_skb, необходимо проверить, и snull делает это. Мы вызывает printk_ratelimit для сообщения об
ошибке. Генерация сотен или тысяч сообщений в секунду является
хорошим способом затормозить работу системы и скрыть реальный
источник проблем. printk_ratelimit помогает предотвратить эту
проблему, функция возвращает 0, если на консоль поступает слишком
много сообщений.
После получения skb, пакет данных копируется в буфер, вызовом
memcpy. Функция skb_put, обновляет указатель конца данных в буфере, и
возвращает указатель на созданную область памяти.
Если пишется высокопроизводительный драйвер для интерфейса,
который может управлять передачей данных по шине ввода-вывода (full
bus-mastering I/O), то в нем имеется возможность оптимизации. Её мы
рассмотрим. Некоторые драйверы выделяют буфер сокета для принятия
пакетов заранее, тогда интерфейс размещает данные пакета
непосредственно в пространстве буфера сокета. Для этого сетевой
уровень выделяет все буферы сокетов в области доступной для ПДП
(которая может быть в верхней памяти, если у вашего устройства
установлен флаг NETIF_F_HIGHDMA). Данный подход позволяет избежать
дополнительной операции копирования в буфер сокета, но требует
обратить внимание на выбор размера буфера, поскольку размер
принимаемого пакета заранее неизвестен. В этой ситуации оказывается
полезным метод change_mtu, поскольку позволяет драйверу реагировать
на изменение максимального размера пакета.
Прежде чем принять пакет, сетевой уровень должен получить
дополнительную информацию. Должны быть сформированы поля dev и
protocol. Код поддержки Ethernet экспортирует вспомогательную
функцию eth_type_trans, которая находит подходящее значение для поля
protocol. Затем мы должны указать, как вычисляется контрольная сумма
для пакета, и вычисляется ли вообще (в snull, не нужно вычислять
контрольную сумму). Возможные значения:
CHECKSUM_HW
Устройство вычисляет контрольную сумму аппаратно. Пример
аппаратного вычисления контрольной суммы – интерфейс SPARC
HME.
CHECKSUM_NONE
Контрольная сумма не проверяется. Задача должна выполнятся
системным программным обеспечением. Это значение используется
по умолчанию, для вновь выделенного буфера.
CHECKSUM_UNNECESSARY
Не производится никаких вычислений контрольной суммы. Это
значение используется в snull и интерфейсе loopback.
Вы можете удивиться, почему статус контрольной суммы задается
здесь, если мы уже установили флаг в поле features нашей структуры
net_device. Дело в том что этот флаг информирует ядро об отправляемых
пакетах. О входных же пакетах информируется отдельно.
В конце концов, драйвер обновляет статистический счетчик,
регистрируя принятый пакет. Статистическая структура состоит из
нескольких полей. Наиболее важные из них: rx_packets, rx_bytes, tx_packets, и tx_bytes. Поля содержат количество принятых и
отправленных пакетов и общее количество переданных октетов. Полостью
все поля описаны в подразделе «Статистическая информация».
На последнем шаге приема пакета выполняется функция netif_rx,
которая передает буфер сокета верхним уровням. netif_rx возвращает
целое значение, NET_RX_SUCCESS (0) если пакет успешно принят, и другое
значение в случае возникновения проблем. Имеются три значения
(NET_RX_CN_LOW, NET_RX_CN_MOD и NET_RX_CN_HIGH) которые показывают
уровень перегрузки сетевой подсистемы; NET_RX_DROP - количество
пакетов было минимальным. Драйвер может использовать эти значения
для прекращения подачи пакетов в ядро, когда возникнет перегрузка, но
на практике многие драйверы игнорируют значение возвращаемое
netif_rx. Если вы пишите драйвер для устройства с высокой пропускной
способностью и хотите все сделать хорошо, то, для реагирования на
перегрузку, лучше всего реализовать NAPI, который мы рассмотрим после
обработчиков прерываний.
Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется
Переведено на Нотабеноиде
http://notabenoid.org/book/11832/38386
Переводчики: hardorsoft
Настройки
Готово:
