linux 网络驱动程序 网卡接收数据,怎么知道是不是需要的,linux网络学习笔记--网卡驱动数据接收...

数据接收

网卡驱动的数据接收,实际上是一个生产者/消费者模型。核心是输入队列(全局的,或者网卡私有的)。网卡收到数据时,触发中断。在中断执行例程中,把skb挂入输入队列,并出发软中断。稍后的某个时刻,当软中断执行时,再从该队列中把skb取下来,投递给上层协议。

一软中断

2.6版中的数据传输是通过软中断softirq来实现的。它是以前bh的一个替代品。原理上和bh差不多,都是设置一个全局的向量softirq_vec,并在内核初始化时,调用net_dev_init:

open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);

open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);

把传输和发送数据的软中断中断函数设置为net_tx_action和net_rx_action。需要发送和接收数据时,只要触发相应的软中断,如__raise_softirq_irqoff(NET_RX_SOFTIRQ)。内核就会在适当时机执行do_softirq来处理pending的软中断。

关于softirq细节,可参考” Linux内核的Softirq机制”。关键点是谁发起,谁执行。

二softnet_data结构

为了发挥多cpu的优势,引入了softnet_data结构。

struct softnet_data

{

intthrottle;

intcng_level;

intavg_blog;

struct sk_buff_headinput_pkt_queue;

struct list_headpoll_list;

struct net_device*output_queue;

struct sk_buff*completion_queue;

struct net_devicebacklog_dev;

}

每个cpu都有自己的softnet_data结构。

前三个变量用于拥塞控制。

input_pkt_queue当驱动不适用NAPI时,收到数据后,会把skb直接挂到该队列。该队列是所有网卡共享的。如果使用NAPI的话,就使用自己的队列。

poll_list有数据要接收的设备队列。如果使用non NAPI,则被挂上去的是blacklog_dev。

output_queue有数据要发送的设备队列。

backlog_dev non NAPI方式下,使用的默认设备。其实主要是使用它的poll方法。该结构是在内核启动为每个cpu初始化softnet_data结构时,调用net_dev_init进行初始化的:

set_bit(__LINK_STATE_START, &queue->backlog_dev.state);

queue->backlog_dev.weight = weight_p;

queue->backlog_dev.poll = process_backlog;

其中process_backlog就相当于驱动中的poll方法。只是它针对是所有non NAPI网卡驱动。于是,传入并挂到poll_list中的是process_backlog结构。而NAPI传入和挂载的是该驱动对应的net_device结构。这个和下面提到的netif_rx实现有关系。

三Non NAPI数据接收

1.skb入队

对于使用non NAPI的驱动,如pcnet32(也可配置为NAPI),数据的接收起始于中断处理函数。

pcnet32_interrupt->pcnet32_rx

在quota允许的范围内,调用pcnet32_rx_entry处理每个收到的包。

pcnet32_interrupt->pcnet32_rx->pcnet32_rx_entry

该函数获取skb,并初始化skb的protocol skb->protocol = eth_type_trans(skb, dev);然后调用函数把skb挂到接收队列,以便在软中断中处理skb。注意,这儿是non NAPI和NAPI的分界点:

#ifdef CONFIG_PCNET32_NAPI

netif_receive_skb(skb);

#else

netif_rx(skb);

pcnet32_interrupt->pcnet32_rx->pcnet32_rx_entry-> netif_rx

该函数主要把skb放入softnet_data的input_pkt_queue

__skb_queue_tail(&queue->input_pkt_queue, skb);

然后把backlog_dev设备挂入poll_list;netif_rx_schedule(&queue->backlog_dev);

netif_rx_schedule

该函数先调用netif_rx_schedule_prep设置net_device.state。注意,该变量是数据收发的重点。它组成了一个状态机。然后调用__netif_rx_schedule把net_device(backlog_dev)加入poll_list。并触发一个软中断

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

__raise_softirq_irqoff(NET_RX_SOFTIRQ);

到此,在中断中,对于一个skb的处理完成。接下来,对数据包的处理,以及向协议上层传递,将在软中断处理例程中进行。

2.skb出队

内核会在适当时机检查pending的softirq,并调用do_softirq来执行相应得软中断。

do_softirq-> __do_softirq

h = softirq_vec;

do {

if (pending & 1) {

h->action(h);// net_rx_action

rcu_bh_qsctr_inc(cpu);

}

h++;

pending >>= 1;

} while (pending);

由于我们在网卡中断处理程序中出发了一个软中断,所以这儿会执行net_rx_action,来处理队列中的skb。

net_rx_action

该函数中,循环取出poll_list中的接收数据的net_device,然后执行它的poll方法。

dev = list_entry(queue->poll_list.next,

struct net_device, poll_list);

if (dev->quota <= 0 || dev->poll(dev, &budget)) {

netpoll_poll_unlock(have);

local_irq_disable();

list_move_tail(&dev->poll_list, &queue->poll_list);//如果配额用完,但还有数据,则把它加入poll_list对尾,等待下次处理。

if (dev->quota < 0)

dev->quota += dev->weight;

else

dev->quota = dev->weight;

}

注意,不论是non NAPI还是NAPI处理软中断都会调用该方法。只是由于由于在skb入队时,挂入的net_device不同,调用了不同的poll方法。NAPI调用的是自己的net_device.poll,而non NAPI调用的是backlog_dev.poll,即process_backlog,该方法中,只是dequeue softnet_data. input_pkt_queue。

在dev->poll中,无论是non NAPI还是NAPI,都会调用netif_receive_skb(skb)往上层协议发送数据。

四NAPI数据接收(e100)

1.skb入队

e100_intr

在e100_intr中断处理例程中,直接把当前net_device挂入poll_list,等待在软中断处理历程中,调用该net_device中的poll方法来处理接收队列。注意,由于现在不是共享input_pkt_queue,所以,驱动程序必须为net_device提供一个priv结构,来实现缓冲队列。如e100,该环形缓冲就是由nic->rxs指向,它相当于input_pkt_queue,不过这个queue,是e100网卡维护的。关于e100的ring的实现,参考后面的”e100 ring buffer”

2.skb出队

e100_poll

该函数中,把rx队列中的skb取下来投递给上层协议。

e100_poll->e100_rx_clean

对每个rx调用e100_rx_indicate。如果e100_rx_indicate返回错误,则停止循环处理rx。错误情况有两种,其中如果为EAGIN,则说明该net_device配额已经用完,需要等待下次处理。注意,此处不像non NAPI,无须把net_device重新入队。

e100_poll->e100_rx_clean->e100_rx_indicate

根据收到的数据,设置skb_buff_header。并把skb送往上层协议netif_receive_skb。

主要执行流程:

e100_rx_clean-> e100_rx_indicate-> netif_receive_skb

和process_backlog最大不同点是,它处理的是自己的输入队列rx。

附e100 ring buffer

E100采用了共享内存的架构,在共享内存中维护了一个ring buffer。

070613231423.jpg

E100中有个系统控制块scb,它的general pointer指向环形缓冲的第一个buffer。该buffer是以RFD结构来描述。同过RFD的link域组成环形队列。该RFD其实是skb data的一部分,是直接加在数据帧上的。上面还记录了队列的相关信息。

070613231439.jpg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值