linux3.3.8 网络设备Net_dev_init初解析

目录

该函数所用的全局变量

1:Dev_proc_init()

2:netdev_kobject_init()

3:register_pernet_subsys(&netdev_net_ops)

4:open_softirq(NET_TX_SOFTIRQ, net_tx_action)

5: open_softirq(NET_TX_SOFTIRQ, net_rx_action)

6:net_rx_action收到数据执行的软中断。

7: process_backlog


Net_dev_init(/net/core/dev.c)

该函数所用的全局变量

1:dev_proc_ops

2:first_device 链表

3:net_namespace_list 网络设备链表用于寻找 net设备

4:net_ns_type_operations

1:Dev_proc_init()

其实这个函数就是在文件系统中 proc 中去得到相应的文件

设备功能初始化,其实在内部执行的为register_pernet_subsys(&dev_proc_ops)

在这个函数中其实是执行的 register_pernet_operations(first_device, ops); 这其中其实first_device其实就是一个链表,初始化一个链表的结构体。

这个函数的内部就是__register_pernet_operations(list, ops);

         ----- 目前假定函数宏CONFIG_NET_NS为1

         执行 list_add_tail(&ops->list, list) 表明将 first_device 加入到 dev_proc_ops的list结构中

         For_each_net(net)目前看是在寻找网络,在可执行的网络中,执行ops_init---

其实这个函数目的就是执行Ops->init(net), 寻找加入到net_namespace_list中的网络设备。

他就是执行dev_proc_ops下面的init 函数 dev_proc_net_init(struct net* net)

 

Proc_net_fops_creat( ) ------- 实际调用 proc_create(name, mode, net->proc_net, fops)

这个其实就是在proc 下面创建文件。对应的dev和 softnet_stat, ptype。

Proc/net/ 下对应的 dev, softnet_stat, ptype, wireless.

 

Proc/ net下面可以看到很有有用的信息 上面的几个文件对应的就是可以查看的信息。

 

2:netdev_kobject_init()

在内部执行的为 kobj_ns_type_register(&net_ns_type_operations)

3:register_pernet_subsys(&netdev_net_ops)

暂时没有关注。

4:open_softirq(NET_TX_SOFTIRQ, net_tx_action)

5: open_softirq(NET_TX_SOFTIRQ, net_rx_action)

接受网络数据的中断,先学习哈open_softirq函数

5.1:中断的下半部

         对于一个中断分为上半部和下半部,上半部处理对时间特别敏感一般来说和硬件相关的,并保证不被其他中断打断的任务。而实现下半部分的方法目前有三种方法,软中断,tasklet,工作队列,此处只是用于软中断。

         软中断其实包含了如下的结构:

         1:注册软中断:open_softirq

         2: 触发软中断: raise_softirq

         3: 执行软中断: do_softirq:

         4: 结束软中断

其中初始化时 有如下的init:

    for_each_possible_cpu(i)

    {

        struct softnet_data *queue;

        queue = &per_cpu(softnet_data, i);

        /*初始化输入队列*/

        skb_queue_head_init(&queue->input_pkt_queue);

        queue->completion_queue = NULL;

        /*初始化pool 链表头*/

        INIT_LIST_HEAD(&queue->poll_list);

        /*把backlog的轮询函数初始化为 process_backlog

         该函数来处理传统接口使用的输入队列的报文*/

        queue->backlog.poll = process_backlog;

        /*backlog 轮询函数一次可以处理的报文上限个数*/

        queue->backlog.weight = weight_p;

        queue->backlog.gro_list = NULL;

        queue->backlog.gro_count = 0;

}

处理上来的报文处理为process_backlog。

 

目前来看 网卡驱动后调用的是napi_schedule()调用的执行网络中断。

需要继续看的是ieee80211_napi_schedule 调度的执行软中断

在其中的___napi_schedule中执行的list_add_tail 把sd的数据挂在napi的polllist中

然后执行__raise_softirq_irqoff这一个软中断。这里就是执行net_dev

_init中执行的net_rx_action

6:net_rx_action收到数据执行的软中断。

Softnet_data *sd 其实就是ieee80211_napi_schedule 中加入到cpu数据的poll_list链表。

 

如下的表示是该函数的解释

 

static void net_rx_action(struct softirq_action *h)

{

    /*取得本地cpu 的softnet_data 的poll_list  链表*/

    struct list_head *list = &__get_cpu_var(softnet_data).poll_list;

    /*设置软中断处理程序一次允许的最大执行时间为2个jiffies*/

    unsigned long time_limit = jiffies + 2;

  

    /*设置软中断接收函数一次最多处理的报文个数为 300 */

    int budget = netdev_budget;

    /*关闭本地cpu的中断,下面判断list是否为空时防止硬中断抢占*/

    local_irq_disable();

    /*循环处理pool_list 链表上的等待处理的napi*/

    while (!list_empty(list))

    {

        struct napi_struct *n;

        int work, weight;

                                                                                                                                     

        /*如果处理报文超出一次处理最大的个数

          或允许时间超过最大时间就停止执行,

          跳到softnet_break 处*/

        if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))

        {

            goto softnet_break;

        }

        /*使能本地中断,上面判断list为空已完成,下面调用NAPI

          的轮询函数是在硬中断开启的情况下执行*/

        local_irq_enable();

                                                                                                                                      

        /* 取得softnet_data pool_list 链表上的一个napi,

           即使现在硬中断抢占软中断,会把一个napi挂到pool_list的尾端

           软中断只会从pool_list 头部移除一个pool_list,这样不存在临界区*/

        n = list_entry(list->next, struct napi_struct, poll_list);

        /*用weighe 记录napi 一次轮询允许处理的最大报文数*/

        weight = n->weight;

        /* work 记录一个napi总共处理的报文数*/

        work = 0;

  

        /*如果取得的napi状态是被调度的,就执行napi的轮询处理函数*/

        if (test_bit(NAPI_STATE_SCHED, &n->state))

        {

            work = n->poll(n, weight);

        }

        WARN_ON_ONCE(work > weight);

        /*预算减去已经处理的报文数*/

        budget -= work;

        /*禁止本地CPU 的中断,下面会有把没执行完的NAPI挂到softnet_data

          尾部的操作,和硬中断存在临界区。同时while循环时判断list是否

          为空时也要禁止硬中断抢占*/

        local_irq_disable();

  

        /*如果napi 一次轮询处理的报文数正好等于允许处理的最大数,

          说明一次轮询没处理完全部需要处理的报文*/

        if (unlikely(work == weight))

        {

            /*如果napi已经被禁用,就把napi 从 softnet_data 的pool_list 上移除*/

            if (unlikely(napi_disable_pending(n)))

            {

                local_irq_enable();

                napi_complete(n);

                local_irq_disable();

            }

            else

            {

                /*否则,把napi 移到 pool_list 的尾端*/

                list_move_tail(&n->poll_list, list);

            }

        }

    }

out:

    local_irq_enable();

    return;

  

    /*如果处理时间超时,或处理的报文数到了最多允许处理的个数,

      说明还有napi 上有报文需要处理,调度软中断。

      否则,说明这次软中断处理完全部的napi上的需要处理的报文,不再需要

      调度软中断了*/

softnet_break:

    __get_cpu_var(netdev_rx_stat).time_squeeze++;

    __raise_softirq_irqoff(NET_RX_SOFTIRQ);

    goto out;

}

 

其实在这其中执行的 就是 当其处于被调度的 就执行

n->poll(n, weight);

而是否被调度 在上面ieee的函数中就是设置调度这个的函数

而设置n->poll是在ieee80211_napi_poll中去设置得到

即函数中 netif_napi_add中执行最开始调用的为

Ieee80211_register_hw中设置。

目前来看其调用的为 net_dev_iniit中设置的 process_backlog函数。

7: process_backlog

下一个小节 开始分析

你可能感兴趣的:(linux)