W3Cschool
恭喜您成為首批注冊用戶
獲得88經驗值獎勵
從網絡上接收報文比發(fā)送它要難一些, 因為必須分配一個 sk_buff 并從一個原子性上下文中遞交給上層. 網絡驅動可以實現(xiàn) 2 種報文接收的模式: 中斷驅動和查詢. 大部分驅動采用中斷驅動技術, 這是我們首先要涉及的. 有些高帶寬適配卡的驅動也可能采用查詢技術; 我們在"接收中斷緩解"一節(jié)中了解這個方法.
snull 的實現(xiàn)將"硬件"細節(jié)從設備獨立的常規(guī)事務中分離. 因此, 函數(shù) snull_rx 在硬件收到報文后從 snull 的"中斷"處理中調用, 并且報文現(xiàn)在已經在計算機的內存中. snull_rx 收到一個數(shù)據指針和報文長度; 它唯一的責任是發(fā)走這個報文和運行附加信息給上層的網絡代碼. 這個代碼獨立于獲得數(shù)據指針和長度的方式.
void snull_rx(struct net_device *dev, struct snull_packet *pkt)
{
struct sk_buff *skb;
struct snull_priv *priv = netdev_priv(dev);
/*
*
The packet has been retrieved from the transmission
*
medium. Build an skb around it, so upper layers can handle it
*/
skb = dev_alloc_skb(pkt->datalen + 2);
if (!skb) {
if (printk_ratelimit())
printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n"); priv->stats.rx_dropped++; goto out;
}
memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
/* Write metadata, and then pass to the receive level */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
netif_rx(skb);
out:
return;
}
這個函數(shù)足夠普通以作為任何網絡驅動的一個模板, 但是在你有信心重用這個代碼段前需要一些解釋.
第一步是分配一個緩存區(qū)來保存報文. 注意緩存分配函數(shù) (dev_alloc_skb) 需要知道數(shù)據長度. 函數(shù)用這些信息來給緩存區(qū)分配空間. dev_alloc_skb 使用 atomic 優(yōu)先級調用 kmalloc , 因此它可以在中斷時間安全使用. 內核提供了其他接口給 socket 緩存分配, 但是它們不值得在此介紹; socket 緩存在"socket 緩存"一節(jié)中詳細介紹.
當然, dev_alloc_skb 的返回值必須檢查, snull 這樣做了. 我們調用 printk_ratelimit 在抱怨失敗之前, 但是. 每秒鐘產生成百上千的控制臺消息是完全陷死系統(tǒng)和隱藏問題的真正源頭的好方法; printk_ratelimit 幫助阻止這個問題, 通過在有太多輸出到了控制臺時返回 0, 事情需要慢下來一點.
一旦有一個有效的 skb 指針, 通過調用 memcpy, 報文數(shù)據被拷貝到緩存區(qū); skb_put 函數(shù)更新緩存中的數(shù)據末尾指針并返回指向新建空間的指針.
如果你在編寫一個高性能驅動, 為一個可以進行完全總線占據 I/O 的接口, 一個可能的優(yōu)化值得在此考慮下. 一些驅動在報文接收前分配 sokcet 緩存, 接著使接口將報文數(shù)據直接放入 socket 緩存空間. 網絡層通過在可 DMA 的空間( 如果你的設備設置了 NETIF_F_HIGHDMA 標志, 這個空間有可能在高端內存)中分配所有 socket 緩存來配合這個策略. 這樣避免了單獨的填充 socket 緩存的拷貝操作, 但是需要小心緩存區(qū)的大小, 因為你無法提前知道進來的報文大小. change_mtu 方法的實現(xiàn)在這種情況下也重要, 因為它允許驅動對最大報文大小改變作出響應.
網絡層在搞懂報文的意思前需要清楚一些事情. 為此, dev 和 protocol 成員必須在緩存向上傳遞前賦值. 以太網支持代碼輸出一個幫助函數(shù)( eth_type_trans ), 它發(fā)現(xiàn)一個合適值來賦給 protocol. 接著我們需要指出校驗和要如何進行或者已經在報文上完成( snull 不需要做任何校驗和 ). 對于 skb->ip_summed 可能的策略有:
CHECKSUM_HW
設備已經在硬件里做了校驗. 一個硬件校驗的例子使 APARC HME 接口.
CHECKSUM_NONE
校驗和還沒被驗證, 必須由系統(tǒng)軟件來完成這個任務. 這個是缺省的, 在新分配的緩存中.
CHECKSUM_UNNECESSARY
不要做任何校驗. 這是 snull 和 環(huán)回接口的策略.
你可能奇怪為什么校驗和狀態(tài)必須在這里指定, 當我們已經在我們的 net_device 結構的特性成員中設置了標志. 答案是特性標志告訴內核我們的設備如何對待外出的報文. 它不用于進入的報文, 相反, 進入報文必須單獨標記.
最后, 驅動更新它的統(tǒng)計計數(shù)來記錄收到一個報文。 統(tǒng)計結構由幾個成員組成; 最重要的是 rx_packet, rx_bytes, 和 tx_bytes, 分別含有收到的報文數(shù)目, 發(fā)送的數(shù)目, 和發(fā)送的字節(jié)總數(shù). 所有的成員在"統(tǒng)計信息"一節(jié)中完全描述.
報文接收的最后一步由 netif_rx 進行, 它遞交 socket 緩存給上層. 實際上 netif_rx 返回一個整數(shù); NET_RX_SUCCESS(0) 意思是報文成功接收; 任何其他值指示錯誤. 有 3 個返回值 (NET_RX_CN_LOW, NET_RX_CN_MOD, 和 NET_RX_CN_HIGH )指出網絡子系統(tǒng)的遞增的擁塞級別; NET_RX_DROP 意思是報文被丟棄. 一個驅動在擁塞變高時可能使用這些值來停止輸送報文給內核, 但是, 實際上, 大部分驅動忽略從 netif_rx 的返回值. 如果你在編寫一個高帶寬設備的驅動, 并且希望正確處理擁塞, 最好的辦法是實現(xiàn) NAPI, 我們在快速討論中斷處理后討論它.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: