作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
微博:weibo.com/glinuxer
QQ技术群:4367710
前面e1000_probe的分析,按照Linux驱动框架,接下来就该e1000_open。netmap并没有对e1000_open进行任何修改,而是改动了e1000_configure,其会被e1000_open及e1000_up调用。
e1000_configure的修改
按照惯例,还是先看diff文件
@@ -393,6 +397,10 @@ static void e1000_configure(struct e1000
e1000_configure_tx(adapter);
e1000_setup_rctl(adapter);
e1000_configure_rx(adapter);
+#ifdef DEV_NETMAP
+ if (e1000_netmap_init_buffers(adapter))
+ return;
+#endif /* DEV_NETMAP */
/* call E1000_DESC_UNUSED which always leaves
* at least 1 descriptor unused to make sure
* next_to_use != next_to_clean */
从diff文件可以看出,netmap替代了原有的e1000申请ring buffer的代码。如果e1000_netmap_init_buffers成功返回,e1000_configure就直接退出了。
接下来进入e1000_netmap_init_buffers:
/*
* Make the tx and rx rings point to the netmap buffers.
*/
static int e1000_netmap_init_buffers(struct SOFTC_T *adapter)
{
struct e1000_hw *hw = &adapter->hw;
struct ifnet *ifp = adapter->netdev;
struct netmap_adapter* na = NA(ifp);
struct netmap_slot* slot;
struct e1000_tx_ring* txr = &adapter->tx_ring[0];
unsigned int i, r, si;
uint64_t paddr;
/*
还记得前面的netmap_attach吗?
所谓的attach,即申请了netmap_adapter,并将net_device->ax25_ptr保存了指针,并设置了NETMAP_SET_CAPABLE。
因此这里做一个sanity check,以免影响正常的网卡驱动
*/
if (!na || !(na->ifp->if_capenable & IFCAP_NETMAP))
return 0;
/* e1000_no_rx_alloc如其名,为一个不该调用的函数,只输出一行错误日志 */
adapter->alloc_rx_buf = e1000_no_rx_alloc;
for (r = 0; r num_rx_rings; r++) {
struct e1000_rx_ring *rxr;
/* 初始化对应的netmap对应的ring */
slot = netmap_reset(na, NR_RX, r, 0);
if (!slot) {
D("strange, null netmap ring %d", r);
return 0;
}
/* 得到e1000对应的ring */
rxr = &adapter->rx_ring[r];
for (i = 0; i count; i++) {
// XXX the skb check and cleanup can go away
struct e1000_buffer *bi = &rxr->buffer_info[i];
/* 将当前的buff索引转换为netmap的buff索引 */
si = netmap_idx_n2k(&na->rx_rings[r], i);
/* 获得netmap的buff的物理地址 */
PNMB(slot + si, &paddr);
if (bi->skb)
D("rx buf %d was set", i);
bi->skb = NULL;
// netmap_load_map(...)
/* 现在网卡的这个buffer已经指向了netmap申请的buff地址了 */
E1000_RX_DESC(*rxr, i)->buffer_addr = htole64(paddr);
}
rxr->next_to_use = 0;
/*
下面这几行代码没看明白怎么回事。
有明白的同学指点一下,多谢。
*/
/* preserve buffers already made available to clients */
i = rxr->count - 1 - na->rx_rings[0].nr_hwavail;
if (i count;
D("i now is %d", i);
wmb(); /* Force memory writes to complete */
writel(i, hw->hw_addr + rxr->rdt);
}
/*
初始化发送ring,与接收类似.
区别在于没有考虑发送多队列。难道是因为e1000只可能是接收多队列,发送只可能是一个队列?
这个问题不影响后面的代码阅读。咱们可以暂时将其假设为e1000只有一个发送队列
*/
/* now initialize the tx ring(s) */
slot = netmap_reset(na, NR_TX, 0, 0);
for (i = 0; i num_tx_desc; i++) {
si = netmap_idx_n2k(&na->tx_rings[0], i);
PNMB(slot + si, &paddr);
// netmap_load_map(...)
E1000_TX_DESC(*txr, i)->buffer_addr = htole64(paddr);
}
return 1;
}
e1000cleanrx_irq的修改
@@ -3952,6 +3973,11 @@ static bool e1000_clean_rx_irq(struct e1
bool cleaned = false;
unsigned int total_rx_bytes=0, total_rx_packets=0;
+#ifdef DEV_NETMAP
+ ND("calling netmap_rx_irq");
+ if (netmap_rx_irq(netdev, 0, work_done))
+ return 1; /* seems to be ignored */
+#endif /* DEV_NETMAP */
i = rx_ring->next_to_clean;
rx_desc = E1000_RX_DESC(*rx_ring, i);
buffer_info = &rx_ring->buffer_info[i];
进入netmap_rx_irq, int netmaprxirq(struct ifnet *ifp, int q, int *workdone) { struct netmapadapter *na; struct netmap_kring *r; NMSELINFOT *main_wq;
if (!(ifp->if_capenable & IFCAP_NETMAP))
return 0;
na = NA(ifp);
/*
尽管函数名为rx,但实际上这个函数服务于rx和tx两种情况,用work_done做区分。
*/
if (work_done) { /* RX path */
r = na->rx_rings + q;
r->nr_kflags |= NKR_PENDINTR;
main_wq = (na->num_rx_rings > 1) ? &na->rx_si : NULL;
} else { /* tx path */
r = na->tx_rings + q;
main_wq = (na->num_tx_rings > 1) ? &na->tx_si : NULL;
work_done = &q; /* dummy */
}
/*
na->separate_locks只在ixgbe和bridge中会被设置为1。
根据下面的代码,这个separate_locks表示多队列时,是每个队列使用一个锁。——这样可以提高性能
其余的代码基本相同。都是唤醒等待数据的进程。
*/
if (na->separate_locks) {
mtx_lock(&r->q_lock);
selwakeuppri(&r->si, PI_NET);
mtx_unlock(&r->q_lock);
if (main_wq) {
mtx_lock(&na->core_lock);
selwakeuppri(main_wq, PI_NET);
mtx_unlock(&na->core_lock);
}
} else {
mtx_lock(&na->core_lock);
selwakeuppri(&r->si, PI_NET);
if (main_wq)
selwakeuppri(main_wq, PI_NET);
mtx_unlock(&na->core_lock);
}
*work_done = 1; /* do not fire napi again */
return 1;
}
发送部分的修改与接收类似,就不重复了。
今天的学习,到此为止 (未完待续。。。)