網絡子係統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