一、概述
netmap 的简单介绍可以看我之前的一篇博客 netmap 介绍。
netmap 分成两个部分:内核态部分和用户态部分
使用方式:在网卡驱动中插入 netmap 相关代码,接管原来驱动程序的处理流程。
这里以 ixgbe 为例,插入 netmap 相关代码总共有6处,分别是在:
- ixgbe_clean_tx_irq( )
- ixgbe_clean_rx_irq( )
- ixgbe_configure_tx_ring( )
- ixgbe_configure_rx_ring( )
- ixgbe_probe( )
- ixgbe_remove( )
二、插入 netmap 代码模块
+#if defined(CONFIG_NETMAP) || defined(CONFIG_NETMAP_MODULE)
+#include <ixgbe_netmap_linux.h>
+#endif
ixgbe_clean_tx_irq
@@ -826,6 +842,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector,
unsigned int total_bytes = 0, total_packets = 0;
u16 i, eop, count = 0;
+#ifdef DEV_NETMAP
+ /*
+ * In netmap mode, all the work is done in the context
+ * of the client thread. Interrupt handlers only wake up
+ * clients, which may be sleeping on individual rings
+ * or on a global resource for all rings.
+ */
+ if (netmap_tx_irq(adapter->netdev, tx_ring->queue_index) != NM_IRQ_PASS) // 实际调用的是 netmap_rx_irq()
+ return 1; /* seems to be ignored */
+#endif /* DEV_NETMAP */
i = tx_ring->next_to_clean;
ixgbe_clean_rx_irq
@@ -1308,6 +1334,17 @@ static void ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
u16 cleaned_count = 0;
bool pkt_is_rsc = false;
+#ifdef DEV_NETMAP
+ int nm_irq;
+ /*
+ * Same as the txeof routine: only wakeup clients on intr.
+ */
+ nm_irq = netmap_rx_irq(adapter->netdev, rx_ring->queue_index, work_done); // TX/RX 中断处理程序
+ if (nm_irq != NM_IRQ_PASS) {
+ *work_done = (nm_irq == NM_IRQ_RESCHED) ? work_to_do : 1;
+ return;
+ }
+#endif /* DEV_NETMAP */
i = rx_ring->next_to_clean;
ixgbe_configure_tx_ring
@@ -2730,6 +2767,9 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter,
} while (--wait_loop && !(txdctl & IXGBE_TXDCTL_ENABLE));
if (!wait_loop)
e_err(drv, "Could not enable Tx Queue %d\n", reg_idx);
+#ifdef DEV_NETMAP
+ ixgbe_netmap_configure_tx_ring(adapter, reg_idx); // 配置 netmap 的 TX 队列
+#endif /* DEV_NETMAP */
ixgbe_configure_rx_ring
@@ -3094,6 +3134,10 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter,
IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(reg_idx), rxdctl);
ixgbe_rx_desc_queue_enable(adapter, ring);
+#ifdef DEV_NETMAP
+ if (ixgbe_netmap_configure_rx_ring(adapter, reg_idx)) // 配置 netmap 的 RX 队列
+ return;
+#endif /* DEV_NETMAP */
ixgbe_alloc_rx_buffers(ring, IXGBE_DESC_UNUSED(ring));
ixgbe_probe
@@ -7450,6 +7494,11 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
e_dev_info("Intel(R) 10 Gigabit Network Connection\n");
cards_found++;
+
+#ifdef DEV_NETMAP
+ ixgbe_netmap_attach(adapter); // 调用 netmap_attach()
+#endif /* DEV_NETMAP */
+
return 0;
ixgbe_remove
@@ -7490,6 +7539,10 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
+#ifdef DEV_NETMAP
+ netmap_detach(netdev); // 退出 netmap 模式
+#endif /* DEV_NETMAP */
+
set_bit(__IXGBE_DOWN, &adapter->state);
三、大致流程
netmap 接手 NIC 的传输和接收 ring, 并将 ring 映射到用户态。
对于 RX ring 来说,内核态存放报文数据,用户态来收取。
softc
+----------------+
| standard fields|
| if_pspare[0] --|-------+
+----------------+ |
|
+----------------+<------+
|(netmap_adapter)|
| | netmap_kring
| tx_rings *-----|--------------------------->+---------------+
| | netmap_kring | ring *-----|---.
| rx_rings *-----|--->+---------------+ | nr_hwcur | |
+----------------+ | ring *-----|--. | nr_hwavail | V
| nr_hwcur | | | selinfo | |
| nr_hwavail | | +---------------+ .
| selinfo | | | ... | .
+---------------+ | |(ntx+1 entries)|
| .... | | | |
|(nrx+1 entries)| | +---------------+
| | |
KERNEL +---------------+ |
|
====================================================================
|
USERSPACE | struct netmap_ring
+---->+---------------+
/ | head,cur,tail |
struct netmap_if (nifp, one per fd) / | buf_ofs |
+---------------+ / | other fields |
| ni_tx_rings | / +===============+
| ni_rx_rings | / | buf_idx, len | slot[0]
| | / | flags, ptr |
| | / +---------------+
+===============+ / | buf_idx, len | slot[1]
| txring_ofs[0] | (rel.to nifp)--' | flags, ptr |
| txring_ofs[1] | +---------------+
(tx+1 entries) (num_slots entries)
| txring_ofs[t] | | buf_idx, len | slot[n-1]
+---------------+ | flags, ptr |
| rxring_ofs[0] | +---------------+
| rxring_ofs[1] |
(rx+1 entries)
| rxring_ofs[r] |
+---------------+
上图中有几个重要的结构,从内核态和用户态分别说明:
内核态:
netmap_adapter
扩展了原来的 ixgbe_adapter 结构,netmap 有自己的报文收发同步和中断处理函数。netmap_kring
netmap 的队列,对应于网卡的队列,不过计算时需要加上偏移。
用户态:
netmap_if
一个接口对应一个 netmap_if,也就是 eth0 有自己的 netmap_if,eth1 也有自己的 netmap_if。netmap_ring
每个队列对应一个 netmap_ring,它就是真正的收发队列,里面有读/写指针,还有用来缓冲报文的槽。netmap_slot
一个队列有很多槽,槽里面有用于存放报文的buffer。