這個方法通常被網(wǎng)絡層在向驅動的接收循環(huán)隊列獲取新的數(shù)據(jù)包時刻調用,而驅動的接收循環(huán)隊列中可以向網(wǎng)絡層交付的包數(shù)量則在 dev->quota 字段中表示,我們來看 8139cp 中 POLL 的原型:
static int cp_rx_poll (struct net_device *dev, int *budget)
參數(shù) budget 的上層任務所需要底層傳遞的數(shù)據(jù)包的數(shù)量,這個數(shù)值不能超過netdev_max_backlog 的值。
總而言之,POLL 方法被網(wǎng)絡層調用,只負責按照網(wǎng)絡層的要求值("預算"值)提交對應數(shù)量的數(shù)據(jù)包。8139CP 的 POLL 方法注冊通常在設備驅動程序模塊初始化(調用 probe)的時候進行,如下:
static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
… …
dev->poll = cp_rx_poll;
… …
}
設備的 POLL 方法正如前所說的是被網(wǎng)絡層上的軟中斷 net_rx_action 調用,我們現(xiàn)在來看具體的流程:
static int cp_rx_poll (struct net_device *dev, int *budget)
{
struct cp_private *cp = netdev_priv(dev);
unsigned rx_tail = cp->rx_tail;
/*設定每次進行調度的時候從設備發(fā)送到網(wǎng)絡層次最大的數(shù)據(jù)包的大小*/
unsigned rx_work = dev->quota;
unsigned rx;
rx_status_loop:
rx = 0;
/*重新打開NIC中斷,在 cp_interrupt 中斷句柄中中斷關閉了,現(xiàn)在 POLl 已經(jīng)開始處理環(huán)行緩沖隊列中的數(shù)據(jù),
所以中斷可以打開,準備接收新的數(shù)據(jù)包*/
cpw16(IntrStatus, cp_rx_intr_mask);
while (1) {/*POLL循環(huán)的開始*/
u32 status, len;
dma_addr_t mapping;
struct sk_buff *skb, *new_skb;
struct cp_desc *desc;
unsigned buflen;
/*從下標為rx_tail的內存中的環(huán)行緩沖隊列接收隊列rx_skb上"摘下"套接字緩沖區(qū)*/
skb = cp->rx_skb[rx_tail].skb;
if (!skb)
BUG();
desc = &cp->rx_ring[rx_tail];
/*檢查在 NIC 的環(huán)形隊列(rx_ring)上的最后的數(shù)據(jù)接收狀態(tài),是否有出現(xiàn)接收或者 FIFO 的錯誤,是否*/
status = le32_to_cpu(desc->opts1);
if (status & DescOwn)
break;
len = (status & 0x1fff) - 4;
mapping = cp->rx_skb[rx_tail].mapping;
if ((status & (FirstFrag LastFrag)) != (FirstFrag LastFrag)) {
/* we don't support incoming fragmented frames.
* instead, we attempt to ensure that the
* pre-allocated RX skbs are properly sized such
* that RX fragments are never encountered
*/
cp_rx_err_acct(cp, rx_tail, status, len);
cp->net_stats.rx_dropped++;
cp->cp_stats.rx_frags++;
goto rx_next;
}
if (status & (RxError RxErrFIFO)) {
cp_rx_err_acct(cp, rx_tail, status, len);
goto rx_next;
}
if (netif_msg_rx_status(cp))
printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d\n",
cp->dev->name, rx_tail, status, len);
buflen = cp->rx_buf_sz + RX_OFFSET;
/*創(chuàng)建新的套接字緩沖區(qū)*/
new_skb = dev_alloc_skb (buflen);
if (!new_skb) {
cp->net_stats.rx_dropped++;
goto rx_next;
}
skb_reserve(new_skb, RX_OFFSET);
new_skb->dev = cp->dev;
/*解除原先映射的環(huán)行隊列上的映射區(qū)域*/
pci_unmap_single(cp->pdev, mapping,
buflen, PCI_DMA_FROMDEVICE);
/*檢查套接字緩沖區(qū)(sk_buff)上得到的數(shù)據(jù)校驗和是否正確*/
/* Handle checksum offloading for incoming packets. */
if (cp_rx_csum_ok(status))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
/*按照數(shù)據(jù)的實際大小重新定義套接字緩沖區(qū)的大小*/
skb_put(skb, len);
mapping =
cp->rx_skb[rx_tail].mapping =
/*DMA影射在前面新創(chuàng)建的套接字緩沖區(qū)虛擬地址new_buf->tail到實際的物理地址上,
并且把這個物理地址掛在接收緩沖區(qū)的隊列中*/
pci_map_single(cp->pdev, new_skb->tail,
buflen, PCI_DMA_FROMDEVICE);
/*把新建立的緩沖區(qū)的虛擬地址掛在接收緩沖區(qū)的隊列中,在下一次訪問rx_skb數(shù)組的這個結構時候,
POLL方法會從這個虛擬地址讀出接收到的數(shù)據(jù)包*/
cp->rx_skb[rx_tail].skb = new_skb;
/*在cp_rx_skb調用netif_rx_skb,填充接收數(shù)據(jù)包隊列,等待網(wǎng)絡層在Bottom half隊列中調用ip_rcv接收網(wǎng)絡數(shù)據(jù),
這個函數(shù)替代了以前使用的netif_rx*/
cp_rx_skb(cp, skb, desc);
rx++;
rx_next:
/*把前面映射的物理地址掛在NIC設備的環(huán)行隊列上(也就是rx_ring上,它是在和NIC中物理存儲區(qū)進行了DMA映射的,
而不是驅動在內存中動態(tài)建立的),準備提交給下層(NIC)進行數(shù)據(jù)傳輸*/
cp->rx_ring[rx_tail].opts2 = 0;
cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping);
/*在相應的傳輸寄存器中寫入控制字,把rx_ring的控制權從驅動程序交還給NIC硬件*/
if (rx_tail == (CP_RX_RING_SIZE - 1))
desc->opts1 = cpu_to_le32(DescOwn RingEnd
cp->rx_buf_sz);
else
desc->opts1 = cpu_to_le32(DescOwn cp->rx_buf_sz);
/*步進到下一個接收緩沖隊列的下一個單元*/
rx_tail = NEXT_RX(rx_tail);
if (!rx_work--)
break;
cp->rx_tail = rx_tail;
/*遞減配額值quota,一旦quota遞減到0表示這次的POLL傳輸已經(jīng)完成了使命,
就等待有數(shù)據(jù)到來的時候再次喚醒軟中斷執(zhí)行POLL方法*/
dev->quota -= rx;
*budget -= rx;
/* if we did not reach work limit, then we're done with
* this round of polling
*/
if (rx_work) {
/*如果仍然有數(shù)據(jù)達到,那么返回POLL方法循環(huán)的開始,繼續(xù)接收數(shù)據(jù)*/
if (cpr16(IntrStatus) & cp_rx_intr_mask)
goto rx_status_loop;
/*這里表示數(shù)據(jù)已經(jīng)接收完畢,而且沒有新的接收中斷產(chǎn)生了,這個時候使能NIC的接收中斷,
并且調用__netif_rx_complete把已經(jīng)完成POLL的設備從poll_list上摘除,等待下一次中斷產(chǎn)生的時候,
再次把設備掛上poll_list隊列中。*/
local_irq_disable();
cpw16_f(IntrMask, cp_intr_mask);
__netif_rx_complete(dev);
local_irq_enable();
return 0; /* done */
}
return 1; /* not done */
}
其他的使用 NAPI 的驅動程序和 8139CP 大同小異,只是使用了網(wǎng)絡層專門提供的 POLL 方法--proecess_backlog(/net/dev.c),在 NIC 中斷接收到了數(shù)據(jù)包后,調用網(wǎng)絡層上的 netif_rx(/net/dev.c)將硬件中斷中接收到數(shù)據(jù)幀存入 sk_buff 結構, 然后檢查硬件幀頭,識別幀類型, 放入接收隊列(softnet_data 結構中的 input_pkt_queue 隊列上), 激活接收軟中斷作進一步處理. 軟中斷函數(shù)(net_rx_action)提取接收包,而 process_backlog(也就是 POLL 方法)向上層提交數(shù)據(jù)。
|