Решил, что стоит поднять тему, где будут задаваться вопросы по функционированию ядра ОС Linux, о её функциях, и интересных особенностях. И первый вопрос. Касается функции net_rx_action (определена в net/core/dev.c) вот её код Код (Text): static void net_rx_action(struct softirq_action *h) { struct list_head *list = &__get_cpu_var(softnet_data).poll_list; unsigned long time_limit = jiffies + 2; int budget = netdev_budget; void *have; local_irq_disable(); while (!list_empty(list)) { struct napi_struct *n; int work, weight; /* If softirq window is exhuasted then punt. * Allow this to run for 2 jiffies since which will allow * an average latency of 1.5/HZ. */ if (unlikely(budget <= 0 || time_after(jiffies, time_limit))) goto softnet_break; local_irq_enable(); /* Even though interrupts have been re-enabled, this * access is safe because interrupts can only add new * entries to the tail of this list, and only ->poll() * calls can remove this head entry from the list. */ n = list_entry(list->next, struct napi_struct, poll_list); have = netpoll_poll_lock(n); weight = n->weight; /* This NAPI_STATE_SCHED test is for avoiding a race * with netpoll's poll_napi(). Only the entity which * obtains the lock and sees NAPI_STATE_SCHED set will * actually make the ->poll() call. Therefore we avoid * accidently calling ->poll() when NAPI is not scheduled. */ work = 0; if (test_bit(NAPI_STATE_SCHED, &n->state)) { work = n->poll(n, weight); trace_napi_poll(n); } WARN_ON_ONCE(work > weight); budget -= work; local_irq_disable(); /* Drivers must not modify the NAPI state if they * consume the entire weight. In such cases this code * still "owns" the NAPI instance and therefore can * move the instance around on the list at-will. */ if (unlikely(work == weight)) { if (unlikely(napi_disable_pending(n))) { local_irq_enable(); napi_complete(n); local_irq_disable(); } else list_move_tail(&n->poll_list, list); } netpoll_poll_unlock(have); } out: local_irq_enable(); #ifdef CONFIG_NET_DMA /* * There may not be any more sk_buffs coming right now, so push * any pending DMA copies to hardware */ dma_issue_pending_all(); #endif return; softnet_break: __get_cpu_var(netdev_rx_stat).time_squeeze++; __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out; } Меня интересует этот участок struct napi_struct *n; n = list_entry(list->next, struct napi_struct, poll_list); ( если кто-то не помнит, вот описание функции list_entry ) list_entry(p, t, m) Возвращает адрес структуры типа t, включающей в себя поле типа list_head с именем m и адресом p Как видно, код возвращает указатель на struct napi_struct ( определена в include/linux/netdevice.h) Код (Text): struct napi_struct { /* The poll_list must only be managed by the entity which * changes the state of the NAPI_STATE_SCHED bit. This means * whoever atomically sets that bit can add this napi_struct * to the per-cpu poll_list, and whoever clears that bit * can remove from the list right before clearing the bit. */ struct list_head poll_list; unsigned long state; int weight; int (*poll)(struct napi_struct *, int); #ifdef CONFIG_NETPOLL spinlock_t poll_lock; int poll_owner; #endif unsigned int gro_count; struct net_device *dev; struct list_head dev_list; struct sk_buff *gro_list; // ЧТО ЭТО ЗА УКАЗАТЕЛЬ?????? struct sk_buff *skb; }; Теперь поясню, что за функция такая net_rx_action - это процедура обработки NET_RX_SOFTIRQ по окончанию её работы, пакет skb должен передаться для обработки дальше, сетевому уровню сетевого стека. Она должна принимать из входной очереди текущего процессора пакеты и передавать их обрабатывающему протоколу. Как я понял n->skb должен указывать на обрабатываемый пакет. Но при проверке после n = list_entry(list->next, struct napi_struct, poll_list); оказывается что if( !n->skb ) printk ("Указатель на буфер сокета пуст!!! \n"); Мне не ясно! когда struct napi_struct будет содержать адрес текущего пакета,а также не понятно, как именно net_rx_action передаёт пакет вышележащим протоколам. Рассчитываю на помощь.
это не так, net_rx_action передаст не один пакет а столько пакетов сколько успеет за два тика но не больше бюджета. это тоже не так. napi_struct никогда не содержит адрес текущего пакета по той причине что на этом уровне нет такого понятия в принципе. Если драйвер поддерживает netpoll API то napi_struct::poll() будет указывать на поллинг процедуру драйвера и пакеты в стек будет помещать эта поллинг процедура. если драйвер не поддерживает netpoll API то napi_struct::poll() будет указывать на process_backlog() ( net/core/dev.c) и пакеты в стек будет помещать эта процедура беря их из softnet_data::queue куды их запихает драйвер при помощи netif_rx().
stmia Спасибо БОЛЬШОЕ за пояснение а в struct napi_struct struct sk_buff *gro_list; // на что должен указывать?
вот тут небольшое обсуждение - http://kerneltrap.org/mailarchive/linux-netdev/2009/1/2/4573194 я надеюсь оно поможет вам разобраться.
Следующий вопрос касается функции netif_nit_deliver ( net/core/dev.c ) Код (Text): void netif_nit_deliver(struct sk_buff *skb) { struct packet_type *ptype; if (list_empty(&ptype_all)) return; skb_reset_network_header(skb); skb_reset_transport_header(skb); skb->mac_len = skb->network_header - skb->mac_header; rcu_read_lock(); list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) deliver_skb(skb, ptype, skb->dev); } rcu_read_unlock(); } Интересует вот этот участок: list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) deliver_skb(skb, ptype, skb->dev); } list_for_each_entry_rcu определён как #define в include/linux/rculist.h Код (Text): /** * list_for_each_entry_rcu - iterate over rcu list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ #define list_for_each_entry_rcu(pos, head, member) \ for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry_rcu(pos->member.next, typeof(*pos), member)) теперь посмотрим на структуру struct packet_type Код (Text): struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */ int (*func) (struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int features); int (*gso_send_check)(struct sk_buff *skb); struct sk_buff **(*gro_receive)(struct sk_buff **head, struct sk_buff *skb); int (*gro_complete)(struct sk_buff *skb); void *af_packet_priv; struct list_head list; }; Она содержит struct list_head list значит можем листать связанный список Меня интересует, вот что. Почему при компиляции модуля, выдаётся ошибка: В функции ‘netif_nit_deliver’: ошибка: ‘struct packet_type’ не содержит элемента с именем ‘next’ и указана строка вот этого участка: list_for_each_entry_rcu(ptype, &ptype_all, list) { Из определения #define list_for_each_entry_rcu(pos, head, member) видно, что for (pos = list_entry_rcu((head)->next, typeof(*pos), member); Тоесть элемент next выбирается из аргумента ptype структуры struct packet_type, хотя по всей видимости должен выбираться из элемента list. И потому выдаётся ошибка. Но ведь это код ядра! kernel-linus-2.6.31.6, значит должно быть всё верно. Я совсем запутался, почему такая ошибка вылезает????? И ещё, я в своём модуле, использую многие функции из /net/core/dev.c , чтоб нормально компилировался модуль. мне приходится копировать данные функции к себе в код(если этого не сделать то, функция будет undefined!!!!). Можно ли сделать так? чтоб при компиляции сборка происходила корректно, и чтоб компилятор, всёже находил нормально тела функций и их прототипы из .c файлов ядра.
Booster Вот залил на файлообменник всё, что есть. Заголовочные файлы, как я понимаю находятся в inslude/linux/ а код, наподобии netif_nit_deliver находятся на уровень ниже папки include. В net/core/dev.c там только .c файлы. через [featurelles@localhost /]$ clear && clear && grep 'void netif_nit_deliver' -r /usr/src/kernel-linus-2.6.31.6-1mdv искал хидр, но его нет. Если не сложно, посмотрите пожалуйста архив, (source и headers) может у меня что-то не доустановленно, для нормальной сборки кода. архив весит 49777 KB упакован tar.bz2 http://rapidshare.com/files/309231334/kernel-linus-2.6.31.6-1mdv.tar.bz2.html
1) скорее всего код заголовков (отдельный пакет kernel-headers или нечто в таком роде) и код ядра не соответствуют друг другу что может быть например вызвано тем что они установлены из разных источников. 2) Экспортировать можно только то что явно разрешено для экспорта через EXPORT (можно посмотреть в /proc/kallsyms). Если нечто для экспорта не разрешено то а) вам это не нужно (а нужно пересмотреть свой дизайн ) b) если все-таки нужно то либо пометить это макросом EXPORT либо поместить в свой файл.
насколько я понял вы поместили код функции netif_nit_deliver() в свой модуль и он у вас не собирается. Проблема в том что функция netif_nit_deliver() обращается к глобальному списку ptype_all (тип естественно struct list_head а не struct packet_type) который не экспортируется. код ядра правильный.
stmia Как не странно Вы оказались правы насчёт Насчёт /proc/kallsyms , заглавными буквами указаны экспортируемые символы, а строчными не экспортируемые? c03dcad0 T netif_nit_deliver ( T - заглавная, значит экспортируется?) Что значит пометить макросом EXPORT ? Например c025f980 t proc_sys_call_handler (t - прописная..если не ошибаюсь, не экспортируемая) если я в своём коде использую EXPORT(proc_sys_call_handler) , то тогда смогу обращаться к не экспортируемой переменной? или я совсем запутался?
точно. не в своем коде а в коде ядра после этого ядро надо пересобрать. Это грязное решение, но с отладочными целями сойдет.