轉載請註明出處:http://blog.csdn.net/qq405180763/article/details/8800391
LAN9221採用NAPI方式讀取網卡緩存內數據。當接主機收大量網絡數據時,網卡中斷觸發驅動程序接收數據,驅動程序被觸發後調用輪詢函數,在輪詢函數裏接收固定個數據包後,固定個數據爲budget,一般是輪詢註冊函數netif_napi_add的第三個參數weight,如果數據還沒接收完畢,則中斷再次被觸發(中斷標誌位沒有被清除),繼續輪詢接收數據,知道數據被完全接收完畢,則在輪詢函數裏將中斷標誌位清楚,關閉輪詢函數,重新使能中斷接收下一組數據。NAPI的好處就是既避免了在接收大量數據時頻繁的中斷,又不必因爲輪詢而耗費大量CPU資源,結合了中斷和輪詢的優點。
static int smsc911x_poll(struct napi_struct *napi, int budget)
{
struct smsc911x_data *pdata =
container_of(napi, struct smsc911x_data, napi);
struct net_device *dev = pdata->dev;
int npackets = 0;
while (npackets < budget) { /*不管數據是否接收完畢,每個中斷最多輪詢budget=16次,
因爲中斷被清零,如果還有數據則會再次觸發中斷*/
unsigned int pktlength;
unsigned int pktwords;
struct sk_buff *skb;
unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
if (!rxstat) {
unsigned int temp;
/* We processed all packets available. Tell NAPI it can
* stop polling then re-enable rx interrupts */
smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_); /*清數據接收中斷*/
napi_complete(napi);
temp = smsc911x_reg_read(pdata, INT_EN);
temp |= INT_EN_RSFL_EN_;
smsc911x_reg_write(pdata, INT_EN, temp); /*數據接收完,關閉輪詢,從新使能數據接收中斷*/
break;
}
/* Count packet for NAPI scheduling, even if it has an error.
* Error packets still require cycles to discard */
npackets++;
pktlength = ((rxstat & 0x3FFF0000) >> 16);
pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2; /*獲取數據包長度,並計算按字讀取的長度*/
smsc911x_rx_counterrors(dev, rxstat);
if (unlikely(rxstat & RX_STS_ES_)) {
SMSC_WARNING(RX_ERR,
"Discarding packet with error bit set");
/* Packet has an error, discard it and continue with
* the next */
smsc911x_rx_fastforward(pdata, pktwords);
dev->stats.rx_dropped++;
continue;
}
skb = netdev_alloc_skb(dev, pktlength + NET_IP_ALIGN); /*mac頭是14個字節,而緊接着的IP頭要求14字節對齊,所以多申請兩個字節,保證IP16字節對齊*/
if (unlikely(!skb)) {
SMSC_WARNING(RX_ERR,
"Unable to allocate skb for rx packet");
/* Drop the packet and stop this polling iteration */
smsc911x_rx_fastforward(pdata, pktwords);
dev->stats.rx_dropped++;
break;
}
skb->data = skb->head; /*空頭*/
skb_reset_tail_pointer(skb); /*空數據*/
/* Align IP on 16B boundary */
skb_reserve(skb, NET_IP_ALIGN); /*調節data的位置,使IP按16字節對齊*/
skb_put(skb, pktlength - 4); /*將tail指針往後拉,接着就把數據拷貝到skb緩存中*/
smsc911x_rx_readfifo(pdata, (unsigned int *)skb->head, pktwords);/*強制轉換成unsigned int,每加一次跳四個字節*/
skb->protocol = eth_type_trans(skb, dev); /*使skb->mac_header指向mac協議頭,
skb->data後移14字節,剝離mac頭*/
skb->ip_summed = CHECKSUM_NONE;
netif_receive_skb(skb); /*將skb發往上一層*/
/* Update counters */
dev->stats.rx_packets++;
dev->stats.rx_bytes += (pktlength - 4);
}
/* Return total received packets */
return npackets;
}