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