linux网络实现分析(1)——数据包的接收(从网卡到协议栈)

  • 时间:
  • 浏览:1

}

    unsigned long start_time = jiffies;

                local_irq_disable();

}

    } while (++work < quota && jiffies == start_time);

    }

 */

        local_irq_enable();

    struct sk_buff      *gro_list;

    struct list_head    poll_list;//通过什儿 成员和许多napi_struct相连

    __get_cpu_var(netdev_rx_stat).dropped++;

            if (unlikely(napi_disable_pending(n))) {

    unsigned long time_limit = jiffies + 2;//每次net_rx_action的执行时间

        budget -= work;

    struct napi_struct  backlog;

对于NAPI设备,使用买车人的poll函数,从买车人的私有队列取出skb(NAPI设备不使用公用入口队列softnet_data .input_pkt_queue),怎么让调用netif_receive_skb(skb)最好的最好的办法。而进入netif_receive_skb最好的最好的办法也就正式进入协议栈的补救逻辑了。

    napi->weight = weight_p;

l   napi_schedule_prep – 检测napi否是能被调度

        goto enqueue;

    struct sk_buff      *completion_queue;

l   虚拟poll最好的最好的办法

        local_irq_disable();

//net_rx_action被迫返回(执行时间到意味着分析补救skb数量超过了budget),而入口队列(softnet_data .input_pkt_queue)中仍有skb时则会执行最后一段代码。

int netif_rx(struct sk_buff *skb)

            } else

};

        netpoll_poll_unlock(have);

        __napi_schedule(n);

说明:

    unsigned long       state;

    queue = &__get_cpu_var(softnet_data); //获取本cpusoftnet_data

    补救NET_RX_SOFTIRQ软中断的函数是net_rx_action。

            __skb_queue_tail(&queue->input_pkt_queue, skb); //数据包入队

    if (napi_schedule_prep(n))  //检测napi否是能被scheduled

        }

    int work = 0;

    struct list_head    dev_list;

}

    struct list_head    poll_list; //这是另二个 双向链表

   不同设备驱动的poll最好的最好的办法不一样,对于非NAPI设备其poll最好的最好的办法被初始化为process_backlog。

        !test_and_set_bit(NAPI_STATE_SCHED, &n->state);

     * can remove from the list right before clearing the bit.

            //调用struct napi_structpoll最好的最好的办法,每次最多补救weightskb

    if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {//意味着分析队列不满

    local_irq_disable();

    local_irq_enable();

    return !napi_disable_pending(n) &&

至此,中断的上半部意味着分析完成,以下的工作则交由中断的下半部来实现。总之,NON-NAPI 的中断上半部接收过程能只有简单的描述为,它首先为新到来的数据帧分 配要花费长度的 SKB,再将接收到的数据从 NIC 中拷贝过来,怎么让将什儿 SKB 链入当前 CPU 的 softnet_data 中的链表中,最后进一步触发中断下半部发继续补救。

netif_rx通常在中断环境中被驱动守护进程调用,怎么让还会例外,怪怪的是当此函数是被环回设备调用时。怎么让,netif_rx在其启动还会关闭本地CPU的中断事件,在完成工作后再开启。

    struct sk_buff      *skb;

    int         poll_owner;

        netif_receive_skb(skb);

 softnet_break:

                local_irq_enable();

    do {

{

        struct napi_struct *n;

//调用NET_RX_SOFTIRQ软中断

    __raise_softirq_irqoff(NET_RX_SOFTIRQ);// NET_RX_SOFTIRQ再次被调度

    spinlock_t      poll_lock;

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

 //获取struct napi_struct链表

说明:源码参考2.6.32

};

整个补救流程如下图所示。

   napi_schedule

list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);

/*

    struct softnet_data *queue;

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

     * whoever atomically sets that bit can add this napi_struct

        struct sk_buff *skb;

// work == weight说明队列中的skb还没补救完,还要将本napi_struct放进链尾,下次继续执行其poll最好的最好的办法补救skb

    unsigned int        gro_count;

        weight = n->weight; //weigthpoll最好的最好的办法每次调用每次最多补救skb的数量

        napi_schedule(&queue->backlog);

l   __napi_schedule

        local_irq_disable();

    /* The poll_list must only be managed by the entity which

——lvyilong316

        have = netpoll_poll_lock(n);

                napi_complete(n);

    goto out;

         * calls can remove this head entry from the list.

        work = 0;

            local_irq_restore(flags);

        WARN_ON_ONCE(work > weight);

    if (netpoll_rx(skb)) //NETPOLL否是还要消费该帧

            return NET_RX_SUCCESS;

#endif

void __napi_schedule(struct napi_struct *n)

        if (unlikely(work == weight)) {//说明队列中的skb意味着分析补救完

return;

  netif_rx

        }

    __raise_softirq_irqoff(NET_RX_SOFTIRQ);

    struct softnet_data *queue = &__get_cpu_var(softnet_data);



    }

   每cpu数据

struct napi_struct {

1.     能只有看完发送帧队列并还会skb的链表,什么都有有Qdisc的链表,这意味着分析发送一般还要Qos流控,什么都有有有发送帧会存入相应dev关联的Qdisc中(Qdisc含晒 skb的队列),详见“顶端链路层数据包发送”分析。

            goto softnet_break;

2.     poll_list是另二个 双向链表,每另二个 节点是另二个 napi_struct价值形式,而napi_struct又是net_device的成员,什么都有有有什儿 链表要能只有理解为另二个 net_device链表,哪些地方地方net_device都含晒 输入帧等着被补救。

        /* Even though interrupts have been re-enabled, this

{

该函数从设备驱动守护进程中接收skb,并送入每cpu变量softnet_data的队列中,以完成数据包从设备驱动接受进入协议栈。

    int budget = netdev_budget;

         * entries to the tail of this list, and only ->poll()

         * access is safe because interrupts can only add new

static inline void napi_schedule(struct napi_struct *n)

    非NAPI设备驱动,接收数据包,分配skb,封装完skb还会调用netif_rx。而对于 NAPI 最好的最好的办法,它那末使用 input_pkt_queue 队列,什么都有有使用私有的 队列,什么都有有有它那末这另二个 步骤。

    local_irq_restore(flags);

    __get_cpu_var(netdev_rx_stat).total++; //更新cpu统计计数

         */

{

        }

        if (queue->input_pkt_queue.qlen) {//意味着分析输入队列不空

    int         (*poll)(struct napi_struct *, int);

 * Structure for NAPI scheduling similar to tasklet but with weighting

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

    void *have;

l   net_rx_action

            break;

测试napi例程否是意味着分析运行,意味着分析那末则将其标记为运行状况(通过设置NAPI_STATE_SCHED位),NAPI_STATE_SCHED标记位用来保证当前只有另二个 NAPI poll函数在运行。同还要检查当前napi的状况只有是NAPI_STATE_DISABLE的。

3.     input_pkt_queue是设备驱动将数据从物理介质接收后封装成skb后存放的缓存队列,所有非NAPI设备共有这另二个 输入缓存队列,而NAPI设备有买车人的私有队列用于存放输入包。

     */

local_irq_save(flags);

返回值:NET_RX_SUCCESS(接受入队),NET_RX_DROP(意味着分析队满等意味着丢弃)。

}

            local_irq_enable();

static int process_backlog(struct napi_struct *napi, int quota)

        skb = __skb_dequeue(&queue->input_pkt_queue);  //取出skb

    struct net_device   *dev;

    unsigned long flags;

从网卡到协议栈的skb接收有什儿 最好的最好的办法:NAPI和非NAPI。其含晒 公共逻辑,还会区别。首先看下用到的基本数据价值形式。

(1)  NAPI设备接收数据包不还要存入softnet_data的input_pkt_queue,什么都有有由其自身的驱动函数放进设备自身的私有队列中。非NAPI则要由其驱动函数放进softnet_data的input_pkt_queue

}

            trace_napi_poll(n);

    /* if netpoll wants it, pretend we never saw it */

(2)  NAPI调用napi_schedule传入的参数是设备自身对应的napi_struct,将来软中断就会调用设备自身初始化给napi_struct的poll最好的最好的办法,而NAPI则通过调用napi_schedule传入的参数是softnet_data价值形式中的backlog成员,其poll最好的最好的办法被初始化为内核的process_backlog函数。NAPI通过调用自身的poll最好的最好的办法就能直接将后续到达的数据包从自身私用队列送入协议栈,直到私有队列空,而我不要 再产生中断(非NAPI设备产生中断的作用是将skb收入softnet_data的input_pkt_queue,再触发软中断,而NAPI设备产生中断只为触发软中断)。

#ifdef CONFIG_NETPOLL

struct softnet_data

        if (!skb) {//队列已空

    kfree_skb(skb);

        local_irq_enable();

{

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

}

out:

1. 每个napi_struct能只有通过其poll_list成员链接在softnet_data的poll_list,进而组成链表。

static inline int napi_schedule_prep(struct napi_struct *n)

     * changes the state of the NAPI_STATE_SCHED bit.  This means

            __napi_complete(napi);

l   softnet_data

                list_move_tail(&n->poll_list, list);

        int work, weight;

        // budget为完整性变量,为每次net_rx_action调用,最多补救的skb数量

    return NET_RX_DROP;

    unsigned long flags;

    int         weight;

    return work;

        net_timestamp(skb);// 初始化skb->tstamp.tv64

        //意味着分析输入队列为空

能只有看完该函数的主要功能时将skb放进queue->input_pkt_queue(非NAPI设备共享queue->input_pkt_queue什儿 队列,而NAPI设备有买车人的私有队列),为哪些地方只有队列为空时才调用napi_schedule而,不空时入队后直接返回呢?有那末想过就什么都有有返回后由谁来补救数据包呢?首先回答后者,补救包的任务由软中断负责,而软中断由napi_schedule来触发(见顶端分析)。意味着分析队列不为空,说明并且触发过软中断(最结速了了肯定是空的),怎么让软中断还那末把包补救完,这时只需将新包加入队列即可,并且唤醒的软中断稍还会同時 补救掉什儿 新入队的skb。意味着分析队列为空,说明软中断还未被调用过,意味着分析说上次软中断意味着分析补救完成,什么都有有有这次还要再次调用napi_schedule

static void net_rx_action(struct softirq_action *h)

    struct sk_buff_head input_pkt_queue;  //接收帧队列(入口队列)

enqueue:

{

            work = n->poll(n, weight);  //实际补救了workskb

    local_irq_save(flags); //关闭本地cpuIRQ

    __get_cpu_var(netdev_rx_stat).time_squeeze++;

        }

说明:

2. 通过state成员否是设置NAPI_STATE_SCHED位,来表示该napi_struct否是意味着分析链在某个cpu的softnet_data上,即否是处于调度状况。什么都有有有在链入softnet_data前,还要设置NAPI_STATE_SCHED位,在从softnet_data移除还要清除NAPI_STATE_SCHED位。

    while (!list_empty(list)) {

     * to the per-cpu poll_list, and whoever clears that bit

        return NET_RX_DROP;

而NAPI的中断补救函数(驱动)直接调用napi_schedule,和非NAPI相比跳过了netif_rx函数,什么都有有有NAPI和非NAPI的最主要区别也就出来了:

    struct Qdisc        *output_queue;  //发送帧队列

{

//napi_struct打上去到softnet_datapoll_list

    local_irq_restore(flags);

    if (!skb->tstamp.tv64) // skb->tstamp.tv64否是设置