歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux協議棧之鏈路層上的數據傳輸-數據接收

linux協議棧之鏈路層上的數據傳輸-數據接收

日期:2017/3/2 10:06:07   编辑:關於Linux

數據接收

為了了解網卡數據接收的過程。有必要先討論DMA的具體過程。

DMA傳輸數據可以分為以下幾個步驟:

首先:CPU向DMA送命令,如DMA方式,主存地址,傳送的字數等,之後CPU執行原來的程序.

然後DMA 控制在 I/O 設備與主存間交換數據。接收數據完後, 向CPU發DMA請求,取得總線控制權,進行數據傳送,修改卡上主存地址,修改字數計數器內且檢查其值是否為零,不為零則繼續傳送,若已為零,則向 CPU發中斷請求.。

也就是說,網卡收到包時,將它放入當前skb->data中。再來一個包時。DMA會修改卡上主存地址,轉到skb->next,將數據放入其中。這也就是,一個skb->data存儲一個數據包的原因。

好了,現在就可以來看具體的代碼實現了。

當網絡數據到絡,網卡將其放到DMA內存,然後DMA向CPU報告中斷,CPU根據中斷向量,找到中斷處理例程,也就是我們前面注冊的e100_intr()進行處理。

static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs)

{

struct net_device *netdev = dev_id;

struct nic *nic = netdev_priv(netdev);

u8 stat_ack = readb(&nic->csr->scb.stat_ack);



DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack);



if(stat_ack == stat_ack_not_ours || /* Not our interrupt */

stat_ack == stat_ack_not_present) /* Hardware is ejected */

return IRQ_NONE;



/* Ack interrupt(s) */

//發送中斷ACK。Cpu向設備發送ACK。表示此中斷已經處理

writeb(stat_ack, &nic->csr->scb.stat_ack);



/* We hit Receive No Resource (RNR); restart RU after cleaning */

if(stat_ack & stat_ack_rnr)

nic->ru_running = 0;

//禁用中斷

e100_disable_irq(nic);

//CPU開始調度此設備。轉而會運行netdev->poll

netif_rx_schedule(netdev);



return IRQ_HANDLED;

}

netif_rx_schedule(netdev)後,cpu開始調度此設備,輪詢設備是否有數據要處理。轉後調用netdev->poll函數,即:e100_poll()

static int e100_poll(struct net_device *netdev, int *budget)

{

struct nic *nic = netdev_priv(netdev);

unsigned int work_to_do = min(netdev->quota, *budget);

unsigned int work_done = 0;

int tx_cleaned;

//開始對nic中,DMA數據的處理

e100_rx_clean(nic, &work_done, work_to_do);

tx_cleaned = e100_tx_clean(nic);



/* If no Rx and Tx cleanup work was done, exit polling mode. */

if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) {

netif_rx_complete(netdev);

e100_enable_irq(nic);

return 0;

}



*budget -= work_done;

netdev->quota -= work_done;



return 1;

}

跟蹤進e100_rx_clean():

static inline void e100_rx_clean(struct nic *nic, unsigned int *work_done,

unsigned int work_to_do)

{

struct rx *rx;



/* Indicate newly arrived packets */

//遍歷環形DMA中的數據,調用e100_rx_indicate()進行處理

for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) {

if(e100_rx_indicate(nic, rx, work_done, work_to_do))

break; /* No more to clean */

}



/* Alloc new skbs to refill list */

for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) {

if(unlikely(e100_rx_alloc_skb(nic, rx)))

break; /* Better luck next time (see watchdog) */

}



e100_start_receiver(nic);

}

在這裡,它會遍歷環形DMA中的數據,即從nic->rx_to_clean開始的數據,直至數據全部處理完

進入處理函數:e100_rx_indicate()

static inline int e100_rx_indicate(struct nic *nic, struct rx *rx,

unsigned int *work_done, unsigned int work_to_do)

{

struct sk_buff *skb = rx->skb;

//從這裡取得rfd.其中包括了一些接收信息,但不是鏈路傳過來的有效數據

struct rfd *rfd = (struct rfd *)skb->data;

u16 rfd_status, actual_size;



if(unlikely(work_done && *work_done >= work_to_do))

return -EAGAIN;

//同步DMA緩存

pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr,

sizeof(struct rfd), PCI_DMA_FROMDEVICE);

//取得接收狀態

rfd_status = le16_to_cpu(rfd->status);

DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status);

/* If data isn't ready, nothing to indicate */

//沒有接收完全,返回

Copyright © Linux教程網 All Rights Reserved