Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
17.2. Соединение с ядром
hardorsoft
26.10.10 в 01:26
|
|
Мы начнем изучение структуры сетевого драйвера, на примере snull. Наличие исходных кодов нескольких драйверов, помогут вам следить за обсуждением, и видеть, как работают драйвера Linux. Для начала мы рекомендуем рассмотреть, в порядке возрастания сложности, файлы loopback.c, plip.c и e100.c. Их можно найти в дереве исходных кодов ядра drivers/net.
hardorsoft
26.10.10 в 01:26
|
|
17.2.1. Регистрация устройства
hardorsoft
26.10.10 в 01:27
|
|
Удобно, когда модуль драйвера загружается в работающее ядро, но на это нужно потратить ресурсы. В этом нет ничего нового. Драйвер должен протестировать устройство и найти его размещение (определить порты ввода/вывода и прерывания), но не регистрировать их, как описано в главе 10 «Инсталляция обработчиков прерываний». Сетевой драйвер выполняет эти регистрации в функции инициализации модуля, и этим он отличается от символьных и блочных устройств. Помимо этого имеется еще одно отличие сетевого интерфейса – сетевой драйвер не запрашивает старший и младший номера устройства. Вместо этого драйвер создает новую структуру данных для каждого нового обнаруженного интерфейса в глобальном списке сетевых устройств.
hardorsoft
26.10.10 в 01:27
|
|
Каждый интерфейс описывается структурой net_device, которая определена в файле <linux></linux>netdevice.h>. snull драйвер хранит указатели на две такие структуры (для интерфейса sn0 и sn1) в простом массиве:
hardorsoft
26.10.10 в 01:27
|
|
struct net_device *snull_devs[2];
hardorsoft
26.10.10 в 01:27
|
|
Структура net_device, подобно многим другим структурам ядра, содержит kobject, и, следовательно, счетчик ссылок, и экспортируется через sysfs. Как и другие такие структуры должна создаваться динамически. Для этого ядро предоставляет функцию:
hardorsoft
26.10.10 в 01:27
|
|
struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device *)); Здесь sizeof_priv – размер пространства «внутренних данных» драйвера. Эта область размещается вместе со структурой net_device. Фактически вместо 2-х выделений памяти производится 1 большое, но драйвер делает вид, что не знает об этом. Name – имя интерфейса, каким оно будет в пространстве пользователя. Это имя может иметь printf-стиль – содержать «%d». Ядро заменяет «%d» доступным номером интерфейса. setup – указатель на функцию инициализации, которая используется для настройки структуры net_device. Мы кратко рассмотрели функцию инициализации, и этого пока достаточно. Итак, snull требует выделения 2-х структур для устройств:
hardorsoft
26.10.10 в 01:28
|
|
snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d",snull_init); snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d",snull_init); if (snull_devs[0] = = NULL || snull_devs[1] = = NULL) goto out; Как всегда, мы должны проверить возвращаемое значение, чтобы убедиться, что выделение памяти прошло успешно.
hardorsoft
26.10.10 в 01:28
|
|
Для alloc_netdev, сетевая подсистема предоставляет вспомогательные функции-обертки, чтобы использовать их для различных типов интерфейсов. Наиболее общая - alloc_etherdev, определена в #include /linux/etherdevice.h>:
hardorsoft
26.10.10 в 01:28
|
|
struct net_device *alloc_etherdev(int sizeof_priv);
hardorsoft
26.10.10 в 01:29
|
|
Эта функция выделяет память под структуру сетевого устройства, используя eth%d в качестве имени аргумента. А так же производит инициализацию (с помощью функции ether_setup) некоторых полей net_device, со значениями подходящими для Ethernet-устройств. Таким образом упрощается инициализация, после динамического выделения памяти. Разработчики драйверов других типов устройств, возможно, захотят использовать еще какую-либо вспомогательную функцию, как например, alloc_fcdev (определенную в <linux></linux>fcdevice.h>) для оптоволоконных устройств, alloc_fddidev (<linux></linux>fddidevice.h>) для FDDI устройств (Fiber Distributed Data Interface - волоконно-оптический распределенный информационный интерфейс), или alloc_trdev (<linux></linux>trdevice.h>) для сетевых устройств с маркерным доступом (token ring).
hardorsoft
26.10.10 в 01:29
|
|
В snull можно было бы использовать alloc_etherdev, но мы будем использовать alloc_netdev, чтобы продемонстрировать низкоуровневый интерфейс, и назначить имя интерфейса.
hardorsoft
26.10.10 в 01:31
|
|
После инициализации структуры net_device, необходимо передать их в функцию register_netdev, как показано ниже:
hardorsoft
26.10.10 в 01:31
|
|
for (i = 0; i < 2; i++) if ((result = register_netdev(snull_devs[i]))) printk("snull: error %i registering device \"%s\"\n", result, snull_devs[i]->name); Будьте внимательны, как только вы вызвали register_netdev, ваш драйвер может быть использован устройством. Не вызывайте register_netdev, до тех пор, пока полностью не закончите инициализацию.
hardorsoft
26.10.10 в 01:32
|
|
17.2.2. Инициализация каждого устройства
hardorsoft
26.10.10 в 01:32
|
|
Мы рассмотрели создание и регистрацию структур net_device. Теперь сделаем промежуточный шаг инициализации этих структур. Структура net_device создается во время выполнения, и не может быть создана на этапе компиляции, так же как структуры file_operations или block_device_operations. Инициализация должна быть выполнена до вызова register_netdev. Структура net_device большая и сложная, к счастью для Ethernet- устройств, ядро заполняет ее большую часть при вызове функции ether_setup (котрая вызывается из alloc_etherdev).
hardorsoft
26.10.10 в 01:32
|
|
После использования в snull функции alloc_netdev, остановимся на функции инициализации. Концепция этой функции(snull_init), приведена ниже:
hardorsoft
26.10.10 в 01:33
|
|
ether_setup(dev); /* Инициализация некоторых полей */ dev->open = snull_open; dev->stop = snull_release; dev->set_config = snull_config; dev->hard_start_xmit = snull_tx; dev->do_ioctl = snull_ioctl; dev->get_stats = snull_stats; dev->rebuild_header = snull_rebuild_header; dev->hard_header = snull_header; dev->tx_timeout = snull_tx_timeout; dev->watchdog_timeo = timeout; /* добавим флаг NOARP, к флагам по умолчанию */ dev->flags |= IFF_NOARP; dev->features |= NETIF_F_NO_CSUM; dev->hard_header_cache = NULL; /* запретим кеширование */
hardorsoft
26.10.10 в 01:33
|
|
В начале происходит инициализация структуры net_device. В основном настраиваются указатели на методы нашего драйвера. Единственная не обычная особенность этого кода – установка флага IFF_NOARP. Дело в том, что интерфейс не может использовать протокол определения адресов ARP (Address Resolution Protocol). ARP – низкоуровневый протокол Ethernet. Его задача заключается в определении MAC-адреса по IP-адресу (medium access control). Поскольку удаленные системы, моделируемые snull, реально не существуют, некому ответить на запрос ARP. Вместо дополнительной реализации ARP в snull , мы просто помечаем, что не можем работать с этим протоколом. По той же причине происходит присвоение hard_header_cache = NULL. Это присвоение запрещает кэширование несуществующих ARP ответов в этом интерфейсе. Даная тема обсуждается в подразделе «Определение MAC адресов» этой главы.
hardorsoft
26.10.10 в 01:33
|
|
Код инициализации так же устанавливает два поля tx_timeout и watchdog_timeo, эти поля содержат обработчики таймаутов передачи. Эта рассматривается в подразделе «Таймауты передачи».
hardorsoft
26.10.10 в 01:34
|
|
Рассмотрим еще одно поле структуры net_device - priv. Ее назначение аналогично указателю private_data, который мы использовали для символьных устройств. Отличие от fops->private_data, в том, что под priv выделяется память одновременно с выделением под net_device. Прямой доступ к полю priv не одобряется, это сделано в целях повышения производительности и гибкости. Для доступа к этому полю драйвер snull должен использовать функцию netdev_priv:
hardorsoft
26.10.10 в 01:34
|
|
struct snull_priv *priv = netdev_priv(dev);
hardorsoft
26.10.10 в 01:34
|
|
для поля Priv, модуль snull объявляет структуру snull_priv:
hardorsoft
26.10.10 в 01:34
|
|
struct snull_priv { struct net_device_stats stats; int status; struct snull_packet *ppool; struct snull_packet *rx_queue; /* список входящих пакетов */ int rx_int_enabled; int tx_packetlen; u8 *tx_packetdata; struct sk_buff *skb; spinlock_t lock; };
hardorsoft
26.10.10 в 01:34
|
|
Здесь, помимо всего прочего, содержится экземпляр структуры net_device_stats, которая является стандартным хранилищем статистических данных интерфейса. Следующие строки инициализируют dev->priv:
hardorsoft
26.10.10 в 01:35
|
|
priv = netdev_priv(dev); memset(priv, 0, sizeof(struct snull_priv)); spin_lock_init(&priv->lock); snull_rx_ints(dev, 1); /* Разрешение прерываний */
hardorsoft
26.10.10 в 01:35
|
|
17.2.3. Выгрузка модуля
hardorsoft
26.10.10 в 01:35
|
|
При выгрузке модуля ничего особенного не происходит. Функция выгрузки отменяет регистрацию интерфейса, выполняет внутренние операции по выгрузке, и освобождает структуру net_device.
hardorsoft
26.10.10 в 01:35
|
|
void snull_cleanup(void) { int i; for (i = 0; i < 2; i++) { if (snull_devs[i]) { unregister_netdev(snull_devs[i]); snull_teardown_pool(snull_devs[i]); free_netdev(snull_devs[i]); } } return; }
hardorsoft
26.10.10 в 01:36
|
|
Вызов unregister_netdev удаляет интерфейс из системы. free_netdev освобождает выделенную память. Если ссылки на эту структуру существуют где-нибудь еще, они могут продолжать использоваться, и ваш драйвер не должен об этом беспокоиться. После отмены регистрации, ядро не станет вызывать методы интерфейса.
hardorsoft
26.10.10 в 01:36
|
|
Имейте в виду, что внутренние операции по выгрузке (выполняемые в snull_teardown_pool), должны производиться после отмены регистрации, и до выполнения free_netdev.
hardorsoft
26.10.10 в 01:36
|
|