函数调用过程预览:
ixgbe_netmap_attach()
|-- netmap_attach()
|-- _netmap_attach()
|-- netmap_attach_common()
|-- na->nm_krings_create()
|-- netmap_krings_create()
|-- na->nm_notify()
好,我们具体来看看各个函数
ixgbe_netmap_attach —— 初步初始化 na
static void ixgbe_netmap_attach(struct SOFTC_T *adapter)
{
struct netmap_adapter na;
bzero(&na, sizeof(na));
na.ifp = adapter->netdev; // ifp 是 net_device 结构
na.pdev = &adapter->pdev->dev;
na.num_tx_desc = NM_IXGBE_TX_RING(adapter, 0)->count; // 根据ixgbe的发送队列槽个数来赋值
na.num_rx_desc = NM_IXGBE_RX_RING(adapter, 0)->count; // 根据ixgbe的接收队列槽个数来赋值
na.nm_txsync = ixgbe_netmap_txsync; // 内核态发送函数
na.nm_rxsync = ixgbe_netmap_rxsync; // 内核态收包函数
na.nm_register = ixgbe_netmap_reg;
na.num_tx_rings = adapter->num_tx_queues; // 根据ixgbe的发送队列个数赋值
na.num_rx_rings = adapter->num_rx_queues; // 根据ixgbe的接收队列个数赋值
na.nm_intr = ixgbe_netmap_intr;
netmap_attach(&na);
}
注: SOFTC_T 是一个对应不同驱动的宏,对于 ixgbe,SOFTC_T 是 ixgbe_adapter。
上面函数主要是对netmap_adapter
的一些成员赋值的过程。
下面我们来看看 netmap_adapter 的庐山真面目。
netmap_adapter 的庐山真面目
struct netmap_adapter {
uint32_t na_flags;
u_int num_rx_rings; /* RX 队列个数 */
u_int num_tx_rings; /* TX 队列个数 */
u_int num_tx_desc; /* 发送队列槽个数 */
u_int num_rx_desc; /* 接收队列槽个数 */
struct netmap_kring *tx_rings; /* TX 队列 */
struct netmap_kring *rx_rings; /* RX 队列 */
NM_SELINFO_T si[NR_TXRX]; /* 全局的等待队列 */
struct ifnet *ifp; /* net_device 结构 */
/* 下面是一些比较主要的回调函数 */
int (*nm_txsync)(struct netmap_kring *kring, int flags);
int (*nm_rxsync)(struct netmap_kring *kring, int flags);
int (*nm_notify)(struct netmap_kring *kring, int flags);
int (*nm_config)(struct netmap_adapter *, u_int *txr, u_int *txd, u_int *rxr, u_int *rxd);
int (*nm_krings_create)(struct netmap_adapter *);
void (*nm_krings_delete)(struct netmap_adapter *);
};
下面是 attach 函数的主要部分:
static int _netmap_attach(struct netmap_adapter *arg, size_t size)
{
struct netmap_hw_adapter *hwna = NULL;
struct ifnet *ifp = NULL;
ifp = arg->ifp;
hwna = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
hwna->up = *arg; // up 指向了 na
if (netmap_attach_common(&hwna->up)) { // 对 na 的余下成员进行配置
free(hwna, M_DEVBUF);
goto fail;
}
}
netmap_attach_common —— 进一步对 na 赋值
下面是对 na 的进一步赋值:
int netmap_attach_common(struct netmap_adapter *na)
{
na->nm_krings_create = netmap_hw_krings_create; // 实际调用 netmap_krings_create() 函数建立队列
na->nm_krings_delete = netmap_hw_krings_delete;
na->nm_notify = netmap_notify; // 唤醒等待队列函数
}
netmap 一对好基友 —— TX/RX
看看 TX/RX 的建立过程:
// 对收发队列进行初始化
int netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
{
struct netmap_kring *kring;
u_int n[NR_TXRX];
enum txrx t;
n[NR_TX] = na->num_tx_rings + 1; /* TX 队列 */
n[NR_RX] = na->num_rx_rings + 1; /* RX 队列 */
// 总长度 TX + RX + tailroom
len = (n[NR_TX] + n[NR_RX]) * sizeof(struct netmap_kring) + tailroom;
na->tx_rings = malloc((size_t)len, M_DEVBUF, M_NOWAIT | M_ZERO);
na->rx_rings = na->tx_rings + n[NR_TX];
for_rx_tx(t) {
for (i = 0; i < n[t]; i++) { // 遍历所有的队列并初始化
kring = &NMR(na, t)[i];
bzero(kring, sizeof(*kring));
kring->na = na; /* 每个队列都指向 na */
kring->ring_id = i;
kring->tx = t;
kring->nkr_num_slots = ndesc;
kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
kring->nm_notify = na->nm_notify; /* 队列的唤醒函数就是 na 的等待队列唤醒函数 */
kring->rhead = kring->rcur = kring->nr_hwcur = 0; /* 指针偏移都在起始处 */
mtx_init(&kring->q_lock, (t == NR_TX ? "nm_txq_lock" : "nm_rxq_lock"), NULL, MTX_DEF);
}
}
na->tailroom = na->rx_rings + n[NR_RX];
return 0;
}
TX/RX 布局图
tx_rings、rx_rings 和 tailroom 布局图如下:
+----------+
na->tx_rings ----->| | \
| | } na->num_tx_ring
| | /
+----------+
| | host tx kring
na->rx_rings ----> +----------+
| | \
| | } na->num_rx_rings
| | /
+----------+
| | host rx kring
+----------+
na->tailroom ----->| | \
| | } tailroom bytes
| | /
+----------+
当 netmap_notify 来敲门
下面就是唤醒函数了:
喂,有好吃的了,大家起来吃东西吧 ^_^
// 唤醒函数
static int netmap_notify(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
enum txrx t = kring->tx;
nm_os_selwakeup(&kring->si); // 实际调用 wake_up_interruptible() 函数,唤醒等待队列上的进程
if (na->si_users[t] > 0)
nm_os_selwakeup(&na->si[t]);
return NM_IRQ_COMPLETED;
}