2.1 数据的接收
数据的接收是通过net_rx_action进行的。
这里简单描述一下使用NAPI方式的驱动程序的接收方式,没有使用NAPI的驱动,实际上系统里面也为其模拟了一个NAPI实体,所以其实是一样的。
驱动程序收到数据包之后会调用,netif_rx_schedule,它的目的就是使软中断net_rx_action能够被执行。
net_rx_action
主要工作就是调用n->poll(n, weight);(这个poll就是驱动程序里面调用netif_napi_add注册的poll,老一点的kernel中驱动程序是注册dev->poll函数,net_rx_action也是调用dev->poll函数)
poll函数会从网卡取得数据,并调用netif_receive_skb传送数据。
netif_receive_skb
把数据传送给所有注册到ptype_all的第三层协议:
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
deliver_skb
static inline int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev,
struct net_device *orig_dev)
{
atomic_inc(&skb->users);
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
就是调用第三层协议注册的func函数,这个函数是数据链路层到第三层的唯一接口。
把数据传送给:
type = skb->protocol;
list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
if (ptype->type == type &&
(!ptype->dev || ptype->dev == skb->dev)) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
还有传送给网桥和VLAN:
skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
2.2 数据的发送
device_queue_xmit
第三层协议要传送数据的时候,就调用这个函数。这是唯一的3层到2层发送数据的入口函数!它的具体流程:
struct Qdisc *q;
rc = q->enqueue(skb, q);
qdisc_run(dev);
qdisc_restart(dev)
有一种特殊情况:如果dev->enqueue == NULL,直接调用dev->hard_start_xmit(),loopback之类的本地接口就是这样做的。
这里的Qdisc,是在网口UP的时候,dev_activate所启动的。
dev_activate
qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops,TC_H_ROOT);
static struct Qdisc_ops pfifo_fast_ops = {
.id = "pfifo_fast",
.priv_size = PFIFO_FAST_BANDS * sizeof(struct sk_buff_head),
.enqueue = pfifo_fast_enqueue,
.dequeue = pfifo_fast_dequeue,
.requeue = pfifo_fast_requeue,
.init = pfifo_fast_init,
.reset = pfifo_fast_reset,
.dump = pfifo_fast_dump,
.owner = THIS_MODULE,
};
所以q->enqueue(skb, q)就是调用pfifo_fast_enqueue,函数很简单:如果queue还没有满,则把skb放入queue的末尾
系统中有其他的qdisc_ops,比如优先级调度之类的,以后章节再详述,这里我们只说了缺省的FIFO方式。
qdisc_run
如果queue的状态不是__LINK_STATE_XOFF(在驱动程序里面调用的netif_stop_queue(dev)和netif_start_queue(dev);就是用来控制这个符号的),并且当前queue没有被处理(__LINK_STATE_QDISC_RUNNING)就可以对queue进行处理:处理的方式是
void __qdisc_run(struct net_device *dev)
{
do {
if (!qdisc_restart(dev))
break;
} while (!netif_queue_stopped(dev));
clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);
}
qdisc_restart
主要就是调用驱动程序提供的hard_start_xmit对数据进行传输。
net_tx_action
从上面的流程可以看到,包传输的整个过程是一气呵成的,根本没有调用到系统里面设置的软中断函数net_tx_action。
这是因为我只描述了正常的流程,如果在过程中有些意外发生,比如queue目前状态是__LINK_STATE_XOFF或者__LINK_STATE_QDISC_RUNNING或者hard_start_xmit返回error了,那包实际上就没有被传送,而是继续放在queue里面的。系统就会在合适的时候(比如pfifo_fast_requeue,netif_wake_queue,qdisc_watchdog)调用netif_schedule或者__netif_schedule来唤起软中断net_tx_action的执行,net_tx_action主要任务也就是调用qdisc_run传送数据包