作者: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 < na->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 < rxr->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 < 0) i += rxr->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 < na->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; }
发送部分的修改与接收类似,就不重复了。
今天的学习,到此为止 (未完待续。。。)