Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 17.6. Прием пакетов

Оглавление
Английский оригинал Перевод на русский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

17.6. Прием пакетов

hardorsoft 26.10.10 в 02:49

0

Прием данных из сети сложнее, чем их передача, потому что sk_buff должен создаваться и обрабатываться верхними уровнями атомарно.
Существуют 2 режима приема пакетов, которые могут быть реализованы
сетевым драйвером: через прерывания и опрос. Большинство драйверов
используют прерывания, и этот режим мы рассмотрим в первую очередь.
Некоторые драйверы, для широкополосных адаптеров, могут
реализовывать режим опроса, мы рассмотрим этот способ в подразделе
«Подавление приема прерываний».

hardorsoft 26.10.10 в 02:50

0

Реализация snull разделяет аппаратно-зависимые и аппаратно-
независимые действия. Поэтому функция snull_rx вызывается из
обработчика прерывания snull после приема пакета аппаратурой, и где
он передается в память компьютера. snull_rx получает указатель на
данные и длину пакета. Это ответственная задача - передать пакет и
дополнительные поля на верхние уровни сетевого кода. Этот код не
зависит от полученных данных и их длинны.

hardorsoft 26.10.10 в 02:50

0

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;
}

hardorsoft 26.10.10 в 02:50

0

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

hardorsoft 26.10.10 в 02:50

0

На первом шаге выделяется буфер под пакет. Обратите внимание,
что функции выделения буфера dev_alloc_skb, необходимо передать его
длину. dev_alloc_skb вызывает kmalloc атомарно, таким образом, это
может быть безопасно использовано в обработчике прерывания. Ядро
предлагает другие интерфейсы для выделения буферов сокета, но они не
приведены здесь. Буферы сокета детально рассмотрены в подразделе
«Буферы сокетов».

hardorsoft 26.10.10 в 02:50

0

Значение, возвращаемое dev_alloc_skb, необходимо проверить, и snull делает это. Мы вызывает printk_ratelimit для сообщения об
ошибке. Генерация сотен или тысяч сообщений в секунду является
хорошим способом затормозить работу системы и скрыть реальный
источник проблем. printk_ratelimit помогает предотвратить эту
проблему, функция возвращает 0, если на консоль поступает слишком
много сообщений.

hardorsoft 26.10.10 в 02:51

0

После получения skb, пакет данных копируется в буфер, вызовом
memcpy. Функция skb_put, обновляет указатель конца данных в буфере, и
возвращает указатель на созданную область памяти.

hardorsoft 26.10.10 в 02:51

0

Если пишется высокопроизводительный драйвер для интерфейса,
который может управлять передачей данных по шине ввода-вывода (full
bus-mastering I/O), то в нем имеется возможность оптимизации. Её мы
рассмотрим. Некоторые драйверы выделяют буфер сокета для принятия
пакетов заранее, тогда интерфейс размещает данные пакета
непосредственно в пространстве буфера сокета. Для этого сетевой
уровень выделяет все буферы сокетов в области доступной для ПДП
(которая может быть в верхней памяти, если у вашего устройства
установлен флаг NETIF_F_HIGHDMA). Данный подход позволяет избежать
дополнительной операции копирования в буфер сокета, но требует
обратить внимание на выбор размера буфера, поскольку размер
принимаемого пакета заранее неизвестен. В этой ситуации оказывается
полезным метод change_mtu, поскольку позволяет драйверу реагировать
на изменение максимального размера пакета.

hardorsoft 26.10.10 в 02:51

0

Прежде чем принять пакет, сетевой уровень должен получить
дополнительную информацию. Должны быть сформированы поля dev и
protocol. Код поддержки Ethernet экспортирует вспомогательную
функцию eth_type_trans, которая находит подходящее значение для поля
protocol. Затем мы должны указать, как вычисляется контрольная сумма
для пакета, и вычисляется ли вообще (в snull, не нужно вычислять
контрольную сумму). Возможные значения:

hardorsoft 26.10.10 в 02:51

0

CHECKSUM_HW

hardorsoft 26.10.10 в 02:51

0

Устройство вычисляет контрольную сумму аппаратно. Пример
аппаратного вычисления контрольной суммы – интерфейс SPARC
HME.

hardorsoft 26.10.10 в 02:52

0

CHECKSUM_NONE

hardorsoft 26.10.10 в 02:52

0

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

hardorsoft 26.10.10 в 02:52

0

CHECKSUM_UNNECESSARY

hardorsoft 26.10.10 в 02:52

0

Не производится никаких вычислений контрольной суммы. Это
значение используется в snull и интерфейсе loopback.

hardorsoft 26.10.10 в 02:52

0

Вы можете удивиться, почему статус контрольной суммы задается
здесь, если мы уже установили флаг в поле features нашей структуры
net_device. Дело в том что этот флаг информирует ядро об отправляемых
пакетах. О входных же пакетах информируется отдельно.

hardorsoft 26.10.10 в 02:53

0

В конце концов, драйвер обновляет статистический счетчик,
регистрируя принятый пакет. Статистическая структура состоит из
нескольких полей. Наиболее важные из них: rx_packets, rx_bytes, tx_packets, и tx_bytes. Поля содержат количество принятых и
отправленных пакетов и общее количество переданных октетов. Полостью
все поля описаны в подразделе «Статистическая информация».

hardorsoft 26.10.10 в 02:53

0

На последнем шаге приема пакета выполняется функция 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, который мы рассмотрим после
обработчиков прерываний.

hardorsoft 26.10.10 в 02:53

0
Минутку...