Вопросы по ядру Linux

Тема в разделе "WASM.UNIX", создана пользователем featurelles, 17 ноя 2009.

  1. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    Решил, что стоит поднять тему, где будут задаваться вопросы по функционированию ядра ОС Linux, о её функциях, и интересных особенностях.
    И первый вопрос.
    Касается функции net_rx_action (определена в net/core/dev.c)
    вот её код
    Код (Text):
    1. static void net_rx_action(struct softirq_action *h)
    2. {
    3.     struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
    4.     unsigned long time_limit = jiffies + 2;
    5.     int budget = netdev_budget;
    6.     void *have;
    7.  
    8.     local_irq_disable();
    9.  
    10.     while (!list_empty(list)) {
    11.         struct napi_struct *n;
    12.         int work, weight;
    13.  
    14.         /* If softirq window is exhuasted then punt.
    15.          * Allow this to run for 2 jiffies since which will allow
    16.          * an average latency of 1.5/HZ.
    17.          */
    18.         if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
    19.             goto softnet_break;
    20.  
    21.         local_irq_enable();
    22.  
    23.         /* Even though interrupts have been re-enabled, this
    24.          * access is safe because interrupts can only add new
    25.          * entries to the tail of this list, and only ->poll()
    26.          * calls can remove this head entry from the list.
    27.          */
    28.         n = list_entry(list->next, struct napi_struct, poll_list);
    29.  
    30.         have = netpoll_poll_lock(n);
    31.  
    32.         weight = n->weight;
    33.  
    34.         /* This NAPI_STATE_SCHED test is for avoiding a race
    35.          * with netpoll's poll_napi().  Only the entity which
    36.          * obtains the lock and sees NAPI_STATE_SCHED set will
    37.          * actually make the ->poll() call.  Therefore we avoid
    38.          * accidently calling ->poll() when NAPI is not scheduled.
    39.          */
    40.         work = 0;
    41.         if (test_bit(NAPI_STATE_SCHED, &n->state)) {
    42.             work = n->poll(n, weight);
    43.             trace_napi_poll(n);
    44.         }
    45.  
    46.         WARN_ON_ONCE(work > weight);
    47.  
    48.         budget -= work;
    49.  
    50.         local_irq_disable();
    51.  
    52.         /* Drivers must not modify the NAPI state if they
    53.          * consume the entire weight.  In such cases this code
    54.          * still "owns" the NAPI instance and therefore can
    55.          * move the instance around on the list at-will.
    56.          */
    57.         if (unlikely(work == weight)) {
    58.             if (unlikely(napi_disable_pending(n))) {
    59.                 local_irq_enable();
    60.                 napi_complete(n);
    61.                 local_irq_disable();
    62.             } else
    63.                 list_move_tail(&n->poll_list, list);
    64.         }
    65.  
    66.         netpoll_poll_unlock(have);
    67.     }
    68. out:
    69.     local_irq_enable();
    70.  
    71. #ifdef CONFIG_NET_DMA
    72.     /*
    73.      * There may not be any more sk_buffs coming right now, so push
    74.      * any pending DMA copies to hardware
    75.      */
    76.     dma_issue_pending_all();
    77. #endif
    78.  
    79.     return;
    80.  
    81. softnet_break:
    82.     __get_cpu_var(netdev_rx_stat).time_squeeze++;
    83.     __raise_softirq_irqoff(NET_RX_SOFTIRQ);
    84.     goto out;
    85. }
    Меня интересует этот участок

    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):
    1. struct napi_struct {
    2.     /* The poll_list must only be managed by the entity which
    3.      * changes the state of the NAPI_STATE_SCHED bit.  This means
    4.      * whoever atomically sets that bit can add this napi_struct
    5.      * to the per-cpu poll_list, and whoever clears that bit
    6.      * can remove from the list right before clearing the bit.
    7.      */
    8.     struct list_head    poll_list;
    9.  
    10.     unsigned long       state;
    11.     int         weight;
    12.     int         (*poll)(struct napi_struct *, int);
    13. #ifdef CONFIG_NETPOLL
    14.     spinlock_t      poll_lock;
    15.     int         poll_owner;
    16. #endif
    17.  
    18.     unsigned int        gro_count;
    19.  
    20.     struct net_device   *dev;
    21.     struct list_head    dev_list;
    22.     struct sk_buff      *gro_list; // ЧТО ЭТО ЗА УКАЗАТЕЛЬ??????
    23.     struct sk_buff      *skb;
    24. };
    Теперь поясню, что за функция такая 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 передаёт пакет вышележащим протоколам.
    Рассчитываю на помощь.
     
  2. stmia

    stmia New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2009
    Сообщения:
    35
    это не так, 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().
     
  3. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    stmia
    Спасибо БОЛЬШОЕ за пояснение
    а в struct napi_struct
    struct sk_buff *gro_list; // на что должен указывать?
     
  4. stmia

    stmia New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2009
    Сообщения:
    35
  5. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    stmia
    Спасибо
     
  6. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    Следующий вопрос касается функции netif_nit_deliver ( net/core/dev.c )
    Код (Text):
    1. void netif_nit_deliver(struct sk_buff *skb)
    2. {
    3.     struct packet_type *ptype;
    4.  
    5.     if (list_empty(&ptype_all))
    6.         return;
    7.  
    8.     skb_reset_network_header(skb);
    9.     skb_reset_transport_header(skb);
    10.     skb->mac_len = skb->network_header - skb->mac_header;
    11.  
    12.     rcu_read_lock();
    13.     list_for_each_entry_rcu(ptype, &ptype_all, list) {
    14.         if (!ptype->dev || ptype->dev == skb->dev)
    15.             deliver_skb(skb, ptype, skb->dev);
    16.     }
    17.     rcu_read_unlock();
    18. }
    Интересует вот этот участок:
    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):
    1. /**
    2.  * list_for_each_entry_rcu  -   iterate over rcu list of given type
    3.  * @pos:    the type * to use as a loop cursor.
    4.  * @head:   the head for your list.
    5.  * @member: the name of the list_struct within the struct.
    6.  *
    7.  * This list-traversal primitive may safely run concurrently with
    8.  * the _rcu list-mutation primitives such as list_add_rcu()
    9.  * as long as the traversal is guarded by rcu_read_lock().
    10.  */
    11. #define list_for_each_entry_rcu(pos, head, member) \
    12.     for (pos = list_entry_rcu((head)->next, typeof(*pos), member); \
    13.         prefetch(pos->member.next), &pos->member != (head); \
    14.         pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
    теперь посмотрим на структуру struct packet_type
    Код (Text):
    1. struct packet_type {
    2.     __be16          type;   /* This is really htons(ether_type). */
    3.     struct net_device   *dev;   /* NULL is wildcarded here       */
    4.     int         (*func) (struct sk_buff *,
    5.                      struct net_device *,
    6.                      struct packet_type *,
    7.                      struct net_device *);
    8.     struct sk_buff      *(*gso_segment)(struct sk_buff *skb,
    9.                         int features);
    10.     int         (*gso_send_check)(struct sk_buff *skb);
    11.     struct sk_buff      **(*gro_receive)(struct sk_buff **head,
    12.                            struct sk_buff *skb);
    13.     int         (*gro_complete)(struct sk_buff *skb);
    14.     void            *af_packet_priv;
    15.     struct list_head    list;
    16. };
    Она содержит 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 файлов ядра.
     
  7. Booster

    Booster New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2004
    Сообщения:
    4.860
    featurelles
    А заголовочного файла разве там нет?
     
  8. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    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
     
  9. stmia

    stmia New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2009
    Сообщения:
    35
    1) скорее всего код заголовков (отдельный пакет kernel-headers или нечто в таком роде) и код ядра не соответствуют
    друг другу что может быть например вызвано тем что они установлены из разных источников.

    2) Экспортировать можно только то что явно разрешено для экспорта через EXPORT (можно посмотреть в
    /proc/kallsyms). Если нечто для экспорта не разрешено то а) вам это не нужно (а нужно пересмотреть свой
    дизайн :) ) b) если все-таки нужно то либо пометить это макросом EXPORT либо поместить в свой файл.
     
  10. stmia

    stmia New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2009
    Сообщения:
    35
    насколько я понял вы поместили код функции netif_nit_deliver() в свой модуль и он у вас не собирается.
    Проблема в том что функция netif_nit_deliver() обращается к глобальному списку ptype_all (тип
    естественно struct list_head а не struct packet_type) который не экспортируется. код ядра правильный.
     
  11. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    stmia
    Как не странно Вы оказались правы насчёт
    Насчёт /proc/kallsyms , заглавными буквами указаны экспортируемые символы, а строчными не экспортируемые?
    c03dcad0 T netif_nit_deliver ( T - заглавная, значит экспортируется?)

    Что значит пометить макросом EXPORT ?
    Например c025f980 t proc_sys_call_handler (t - прописная..если не ошибаюсь, не экспортируемая)
    если я в своём коде использую EXPORT(proc_sys_call_handler) , то тогда смогу обращаться к не экспортируемой переменной? или я совсем запутался?
     
  12. stmia

    stmia New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2009
    Сообщения:
    35
    точно.
    не в своем коде а в коде ядра :) после этого ядро надо пересобрать. Это грязное решение, но с отладочными целями
    сойдет.
     
  13. featurelles

    featurelles New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2009
    Сообщения:
    562
    stmia
    Спасибо за объяснение =) , буду капать дальше.