网络子系统7_l2、l3接口
// 调用路径:
// 1.NAPI设备的poll函数->netif_receive_skb
// 2.积压设备的process_backlog函数->netif_receive_skb
// 参数:
// skb,驱动程序已经去掉了以太帧最后的四字节crc32,skb->mac.raw指向mac头,skb->data,skb->nh指向l3帧头
// 函数主要任务:
// 1.处理netpoll衔接点
// 2.更新时间戳,dev的统计信息
// 3.处理bonding衔接点
// 4.向ETH_P_ALL类型的l3协议传递skb
// 5.处理bridge衔接点
// 6.通过数据帧标示的l3协议,传递skb到正确的l3协议
1.1 int netif_receive_skb(struct sk_buff *skb)
{
struct packet_type *ptype, *pt_prev;
int ret = NET_RX_DROP;
unsigned short type;
#ifdef CONFIG_NETPOLL
//netpoll机制
if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) {
//返回1,说明netpoll处理了这个skb,则释放skb,并返回
kfree_skb(skb);
return NET_RX_DROP;
}
#endif
//设置skb的时间戳
if (!skb->stamp.tv_sec)
net_timestamp(&skb->stamp);
//处理bonding
//在3.x的版本中,虚拟dev会注册一个handler,处理bonding
skb_bond(skb);
//更新本cpu接收到的数据包个数
__get_cpu_var(netdev_rx_stat).total++;
//将l4,l3报头对应的指针,指向skb->data
skb->h.raw = skb->nh.raw = skb->data;
skb->mac_len = skb->nh.raw - skb->mac.raw;
pt_prev = NULL;
rcu_read_lock();
...
//向所有ETH_P_ALL类型的l3协议,传递一份skb
list_for_each_entry_rcu(ptype, &ptype_all, list) {
//如果对应的l3协议没有指定dev,或者l3指定的dev就是接收到本skb的dev
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev)
//向对应l3传递skb
ret = deliver_skb(skb, pt_prev);
pt_prev = ptype;
}
}
//处理分流器
handle_diverter(skb);
//处理桥接
if (handle_bridge(&skb, &pt_prev, &ret))
goto out;
//获取skb的l3协议
type = skb->protocol;
//hash到l3协议对应的链表,会存在协议号相同的l3协议,向所有协议号相同的处理函数传递一份skb。
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)
//将skb传递给对应的l3协议
ret = deliver_skb(skb, pt_prev);
pt_prev = ptype;
}
}
//处理遍历过程中最后一个l3协议
if (pt_prev) {
ret = pt_prev->func(skb, skb->dev, pt_prev);
} else {
kfree_skb(skb);
ret = NET_RX_DROP;
}
out:
rcu_read_unlock();
return ret;
}
// 调用路径:
// 1.邻居子系统邻居项output->dev_queue_xmit
// 2.l2帧头缓存output->dev_queue_xmit
// 3.其他l3协议
// 参数:
// skb,已经填充好mac头,l3,l4协议头,skb->data指向l2帧头
// 函数主要任务:
// 1.处理skb与驱动能力不匹配的情况
// 1.1 skb->frag_list分片,驱动不能处理frag_list,
// 1.2 skb->frags分散聚集内存,驱动不能处理分散内存
// 1.3 重新分配sk_buff,拷贝所有数据到sk->data中
// 2.处理上层协议与驱动能力不匹配的情况
// 2.1 上层要求硬件计算校验和,驱动不能提供此能力
// 2.2 由软件计算校验和
// 3.关中断,禁止下半部
// 4.传输
// 4.1 驱动程序使用队列规则
// 4.1.1 获取dev->queue_lock
// 4.1.2 入队skb
// 4.1.3 通过qdisc_run启动队列进行传输
// 4.1.4 释放dev->queue_lock
// 4.2 驱动程序没有使用队列规则
// 4.2.1 获取xmit_lock锁
// 4.2.2 通过hard_start_xmit进行传输
// 4.2.3 释放xmit_lock锁
2.1 int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct Qdisc *q;
int rc = -ENOMEM;
//1.如果,skb被分片,设备不能处理skb->frag_list,重新分配sk_buff,
//将skb_shinfo(skb)->frag_list中的数据拷贝到新skb->data
if (skb_shinfo(skb)->frag_list &&
!(dev->features & NETIF_F_FRAGLIST) &&
__skb_linearize(skb, GFP_ATOMIC))
goto out_kfree_skb;
//2.如果,skb使用分散聚集内存,设备不支持分散聚集IO,或者skb处于高端dma内存,
//重新分配skb_buff,将skb_shinfo(skb)->frags中的数据拷贝到skb->data
if (skb_shinfo(skb)->nr_frags &&
(!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
__skb_linearize(skb, GFP_ATOMIC))
goto out_kfree_skb;
//在1,2中,只要有一个成立,就会通过__skb_linearize将skb_shinfo(skb)->frags,
//skb_shinfo(skb)->frag_list中的数据拷贝新分配的sk_buff
//3.l4协议要求使用硬件计算校验和,但是设备驱动没有提供校验和功能,
//通过软件方式完成校验和
if (skb->ip_summed == CHECKSUM_HW &&
(!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
(!(dev->features & NETIF_F_IP_CSUM) ||
skb->protocol != htons(ETH_P_IP))))
if (skb_checksum_help(skb, 0))
goto out_kfree_skb;
//1.设备驱动使用规则队列,通过队列规则完成数据传输
//关软中断,获取dev的队列规则
local_bh_disable();
q = rcu_dereference(dev->qdisc);
//队列规则具有入队功能
if (q->enqueue) {
//获取队列锁
spin_lock(&dev->queue_lock);
rc = q->enqueue(skb, q);
//在获取设备队列锁之后,启动规则队列的数据传输
qdisc_run(dev);
spin_unlock(&dev->queue_lock);
rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
goto out;
}
//设备没有使用队列规则,直接通过驱动程序完成传输
if (dev->flags & IFF_UP) {
int cpu = smp_processor_id();
if (dev->xmit_lock_owner != cpu) {
//获取xmit_lock
HARD_TX_LOCK(dev, cpu);
//如果设备传输队列没有被关闭
if (!netif_queue_stopped(dev)) {
//向ETH_P_ALL类型的l3协议传递一份skb
if (netdev_nit)//netdev_nit表示ETH_P_ALL类型l3协议的个数
dev_queue_xmit_nit(skb, dev);
rc = 0;
//通过驱动程序完成传输
if (!dev->hard_start_xmit(skb, dev)) {
HARD_TX_UNLOCK(dev);
goto out;
}
}
//对xmit_lock解锁
HARD_TX_UNLOCK(dev);
if (net_ratelimit())
printk(KERN_CRIT "Virtual device %s asks to "
"queue packet!\n", dev->name);
} else {
if (net_ratelimit())
printk(KERN_CRIT "Dead loop on virtual device "
"%s, fix it urgently!\n", dev->name);
}
}
rc = -ENETDOWN;
local_bh_enable();
out_kfree_skb:
kfree_skb(skb);
return rc;
out:
local_bh_enable();
return rc;
}
最后更新:2017-04-03 15:22:03