網絡數據包收發流程(三):e1000網卡和DMA

1、硬件佈局

每一個網卡(MAC)都有本身的專用DMA Engine,如上圖的 TSEC 和 e1000 網卡intel82546。
上圖中的紅色線就是以太網數據流,DMA與DDR打交道須要其餘模塊的協助,如TSEC,PCI controller
以太網數據在 TSEC<-->DDR  PCI_Controller<-->DDR 之間的流動,CPU的core是不須要介入的
只有在數據流動結束時(接收完、發送完),DMA Engine纔會之外部中斷的方式告訴CPU的core

2、DMA Engine

上面是DMA Engine的框圖,以接收爲例:
1.在System memory中爲DMA開闢一端連續空間,用來BD數組  (一致性dma內存)
  BD是給DMA Engine使用的,因此不一樣的設備,BD結構不一樣,可是大體都有狀態、長度、指針3個成員。

2.初始化BD數組,status爲E,length爲0
   在System memory中再開闢一塊一塊的內存,能夠不連續,用來存放以太網包
   將這些內存塊的總線地址賦給buf(dma映射)

3.當MAC接收以太網數據流,放在了Rx FIFO中

4.當一個以太網包接收徹底後,DMA engine依次作如下事情
    fetch bd:開始一個個的遍歷BD數組,直到當前BD狀態爲Empty爲止
    update bd:更新BD狀態爲Ready
    move data:把數據從Rx FIFO中搬移到System Memory中dma映射的部分
    generate interrupt:數據搬移完了,產生外部中斷給cpu core

5.cpu core處理外部中斷,此時以太網數據已經在System memory中dma映射的部分了
    解除dma映射,更新bd狀態爲Empty
    再開闢一端內存,將這塊內存的總線地址賦給bd的指針字段

3、內核中DMA相關API

void *dma_alloc_cohrent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
功能:分配一致性dma內存,返回這塊內存的虛擬地址EA, 這塊內存的物理地址保存在 dma_handle
dev:  NULL也行
size: 分配空間的大小
dma_handle: 用來保存內存的總線地址(物理地址)

注意:一致性DMA映射BD所佔內存就是靠dma_alloc_cohrent來分配的。

dma_addr_t *dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction);
功能:將一塊連續的內存 buffer 映射爲DMA內存來使用。映射後,CPU不能再操做這塊 buffer
返回:這塊buffer的總線地址(物理地址)
dev: NULL也行
buffer: 一塊連續內存的虛擬地址EA
size: 連續內存的大小
dma_data_direction: dma數據流的方向

注意:流式DMA映射以太網包所佔內存先經過kmalloc來分配,而後經過dma_map_single來映射給bd的

4、e1000驅動中的DMA

網卡驅動中使用DMA的套路差很少都同樣,以e1000驅動爲例講一下(TSEC驅動的dma見這裏

4.1 加載e1000網卡驅動

e1000_probe(){                        //主要是初始化鉤子函數
     netdev = alloc_etherdev(sizeof(struct e1000_adapter));
    netdev->open = &e1000_open;       //重要
    netdev->stop = &e1000_close;
    netdev->hard_start_xmit = &e1000_xmit_frame;
    netdev->get_stats = &e1000_get_stats;
    netdev->set_multicast_list = &e1000_set_multi;
    netdev->set_mac_address = &e1000_set_mac;
    netdev->change_mtu = &e1000_change_mtu;
    netdev->do_ioctl = &e1000_ioctl;
    e1000_set_ethtool_ops(netdev);
    netdev->tx_timeout = &e1000_tx_timeout;
    netdev->watchdog_timeo = 5 * HZ;
#ifdef CONFIG_E1000_NAPI
    netif_napi_add(netdev, &adapter->napi, e1000_clean, 64); //重要
#endif
}

4.1 啓動e1000網卡

   e1000_open() //當用戶敲ifconfig up命令時,最終調用網卡驅動的open函數
     -->e1000_setup_all_rx_resources(adapter)
         -->e1000_setup_rx_resources(adapter, &adapter->rx_ring[i]) 
               //給rx bd分配一致性dma內存
               rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
     -->e1000_configure(adapter)
         -->e1000_configure_rx(adapter)
               adapter->clean_rx = e1000_clean_rx_irq;
               adapter->alloc_rx_buf = e1000_alloc_rx_buffers;
         -->調用 adapter->alloc_rx_buf鉤子函數,即 e1000_alloc_rx_buffers
                --> skb = netdev_alloc_skb(netdev, bufsz); //調用kmalloc新建一個skb
                    buffer_info->dma = pci_map_single(pdev,
                        skb->data,
                        adapter->rx_buffer_len,
                        PCI_DMA_FROMDEVICE);               //給skb->data創建DMA映射
                    rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);//初始化bd的buf指針
     -->e1000_request_irq(adapter);
        //掛rx 中斷ISR函數爲 e1000_intr()

最終bd數據結構應該是下面這個樣子
       
4.2 e1000的中斷

注意:e1000產生rx中斷時,以太網數據包已經在系統內存中,即在skb->data裏面
下面的中斷處理過程就簡略了,詳細的看這裏
do_IRQ()
{
    中斷上半部
       調用e1000網卡的rx中斷函數 e1000_intr()
          觸發軟中斷 (使用NAPI的話)
    中斷下半部
       依次調用軟中斷的全部handler
       在net_rx_action中最終調用e1000的napi_struct.poll()鉤子函數,即e1000_clean
       e1000_clean()最終調用 e1000_clean_rx_irq()
}

e1000_clean_rx_irq()
{
     rx_desc = E1000_RX_DESC(*rx_ring, i); //獲取rx bd
     status = rx_desc->status;
     skb = buffer_info->skb;
     buffer_info->skb = NULL;

     pci_unmap_single(pdev,                //解除skb->data的DMA映射
                   buffer_info->dma,
                   buffer_info->length,
                   PCI_DMA_FROMDEVICE);
     length = le16_to_cpu(rx_desc->length);
     length -= 4;                          //以太網包的FCS校驗就不要了
     skb_put(skb, length);
     skb->protocol = eth_type_trans(skb, netdev);
     netif_receive_skb(skb);               //skb進入協議棧
}php

相關文章
相關標籤/搜索