天天看點

dma 相關

核心如何從網卡接收資料,傳統的過程:

1.資料到達網卡;

2.網卡産生一個中斷給核心;

3.核心使用I/O指令,從網卡I/O區域中去讀取資料;

我們在許多網卡驅動中(很老那些),都可以在網卡的中斷函數中見到這一過程。

但是,這一種方法,有一種重要的問題,就是大流量的資料來到,網卡會産生大量的中斷,核心在中斷上下文 中,會浪費大量的資源來進行中斷本身。是以,就有一個問題,“可不可以不使用中斷”,這就是輪詢技術,所謂NAPI技術,說來也不神秘,就是說,核心屏蔽 中斷,然後隔一會兒就去問網卡,“你有沒有資料啊?”……

從這個描述本身可以看到,如果資料量少,輪詢同樣占用大量的不必要的CPU資源,大家各有所長吧

OK,另一個問題,就是從網卡的I/O區域,包括I/O寄存器或I/O記憶體中去讀取資料,這都要CPU 去讀,也要占用CPU資源,“CPU從I/O區域讀,然後把它放到記憶體(這個記憶體指的是系統本身的實體記憶體,跟外設的記憶體不相幹,也叫主記憶體)中”。于是 自然地,就想到了DMA技術——讓網卡直接從主記憶體之間讀寫它們的I/O資料,CPU,這兒不幹你事,自己找樂子去:

1.首先,核心在主記憶體中為收發資料建立一個環形的緩沖隊列(通常叫DMA環形緩沖區)。

2.核心将這個緩沖區通過DMA映射,把這個隊列交給網卡;

3.網卡收到資料,就直接放進這個環形緩沖區了——也就是直接放進主記憶體了;然後,向系統産生一個中斷;

4.核心收到這個中斷,就取消DMA映射,這樣,核心就直接從主記憶體中讀取資料;

——呵呵,這一個過程比傳統的過程少了不少工作,因為裝置直接把資料放進了主記憶體,不需要CPU的幹預,效率是不是提高不少?

對應以上4步,來看它的具體實作:

1)配置設定環形DMA緩沖區

Linux核心中,用skb來描述一個緩存,所謂配置設定,就是建立一定數量的skb,然後用e1000_rx_ring 環形緩沖區隊列描述符連接配接起來

2)建立DMA映射

核心通過調用

dma_map_single(struct device *dev,void *buffer,size_t size,enum dma_data_direction direction)

建立映射關系。

struct device *dev 描述一個裝置;

buffer:把哪個位址映射給裝置;也就是某一個skb——要映射全部,當然是做一個雙向連結清單的循環即可;

size:緩存大小;

direction:映射方向——誰傳給誰:一般來說,是“雙向”映射,資料在裝置和記憶體之間雙向流動;

對于PCI裝置而言(網卡一般是PCI的),通過另一個包裹函數pci_map_single,這樣,就把buffer交給裝置了!裝置可以直接從裡邊讀/取資料。

3)這一步由硬體完成;

4)取消映射

dma_unmap_single,對PCI而言,大多調用它的包裹函數pci_unmap_single,不取消的話,緩存控制權還在裝置手裡,要調用 它,把主動權掌握在CPU手裡——因為我們已經接收到資料了,應該由CPU把資料交給上層網絡棧;當然,不取消之前,通常要讀一些狀态位資訊,諸如此類, 一般是調用dma_sync_single_for_cpu()讓CPU在取消映射前,就可以通路DMA緩沖區中的内容

相關引用:

接管總線,應該就是 

​​​​

enum_nic_normal , // 預設系統接收,作業系統協定棧

enum_nic_solarflare_efvi , // solarflare efvi接收

enum_nic_exablaze_exanic , // Exablaze exanic接收

enum_nic_x710_win_speed , // win高速接收,隻适用于windows

enum_nic_solarflare_win_speed , // win高速接收,目前隻适用于windows