ImageVerifierCode 换一换
格式:DOCX , 页数:38 ,大小:37.04KB ,
资源ID:6796508      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6796508.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(高性能网络IO框架netmap源码分析.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

高性能网络IO框架netmap源码分析.docx

1、高性能网络IO框架netmap源码分析高性能网络I/O框架-netmap源码分析(1)作者:gfree.wind博客:blog.focus- 微博:QQ技术群:4367710前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西。其设计思想与业界不谋而合因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的。由于保密性,我是不可能拿公司的设计来和大家讨论和分享的。而netmap的出现,提供了这么一个好机会。它既实现了一个高性能的网络I/O框架,代码量又不算大,非常适合学习和研究。netmap简单介绍首先要感谢netmap的作者,创造出了netmap并无私的分享了他的设

2、计和代码。netmap的文档写得很不错,这里我简单说明一下为什么netmap可以达到高性能。1. 利用mmap,将网卡驱动的ring内存空间映射到用户空间。这样用户态可以直接访问到原始的数据包,避免了内核和用户态的两次拷贝;前两天我还想写这么一个东西呢。2. 利用预先分配的固定大小的buff来保存数据包。这样减少了内核原有的动态分配;对于网络设备来说,固定大小的内存池比buddy要有效的多。之前我跟Bean_lee也提过此事呵。3. 批量处理数据包。这样就减少了系统调用;更具体的内容,大家直接去netmap的官方网站上看吧,写得很详细。虽然英文,大家还是耐着性子好好看看,收获良多。netmap

3、的源码分析从上面netmap的简单介绍中可以看到,netmap不可避免的要修改网卡驱动。不过这个修改量很小。驱动的修改下面我以e1000.c为例来分析。由于netmap最早是在FreeBSD上实现的,为了在linux达到最小的修改,使用了大量的宏,这给代码的阅读带来了一些困难。e1000_probe的修改俺不是写驱动的。e1000_probe里面很多代码看不明白,但是不影响我们对netmap的分析。通过netmap的patch,知道是在e1000完成一系列硬件初始化以后,并注册成功,这时调用e1000_netmap_attach -1175,6 +1183,10 static int _dev

4、init e1000_probe(structif (err) goto err_register;+#ifdef DEV_NETMAP+ e1000_netmap_attach(adapter);+#endif /* DEV_NETMAP */+/* print bus type/speed/width info */e_info(probe, (PCI%s:%dMHz:%d-bit) %pMn, (hw-bus_type = e1000_bus_type_pcix) ? -X : ), 下面是e1000_netmap_attach的代码static voide1000_netmap_att

5、ach(struct SOFTC_T *adapter) struct netmap_adapter na; bzero(&na, sizeof(na); na.ifp = adapter-netdev; na.separate_locks = 0; na.num_tx_desc = adapter-tx_ring0.count; na.num_rx_desc = adapter-rx_ring0.count; na.nm_register = e1000_netmap_reg; na.nm_txsync = e1000_netmap_txsync; na.nm_rxsync = e1000_

6、netmap_rxsync; netmap_attach(&na, 1); SOFTC_T是一个宏定义,对于e1000,实际上是e1000_adapter,即e1000网卡驱动对应的private data。 下面是struct netmap_adapter的定义/* This struct extends the struct adapter (or* equivalent) device descriptor. It contains all fields needed to* support netmap operation.*/struct netmap_adapter /* * On

7、 linux we do not have a good way to tell if an interface * is netmap-capable. So we use the following trick: * NA(ifp) points here, and the first entry (which hopefully * always exists and is at least 32 bits) contains a magic * value which we can use to detect that the interface is good. */ uint32_

8、t magic; uint32_t na_flags; /* future place for IFCAP_NETMAP */ int refcount; /* number of user-space descriptors using this interface, which is equal to the number of struct netmap_if objs in the mapped region. */ /* * The selwakeup in the interrupt thread can use per-ring * and/or global wait queu

9、es. We track how many clients * of each type we have so we can optimize the drivers, * and especially avoid huge contention on the locks. */ int na_single; /* threads attached to a single hw queue */ int na_multi; /* threads attached to multiple hw queues */ int separate_locks; /* set if the interfa

10、ce suports different locks for rx, tx and core. */ u_int num_rx_rings; /* number of adapter receive rings */ u_int num_tx_rings; /* number of adapter transmit rings */ u_int num_tx_desc; /* number of descriptor in each queue */ u_int num_rx_desc; /* tx_rings and rx_rings are private but allocated *

11、as a contiguous chunk of memory. Each array has * N+1 entries, for the adapter queues and for the host queue. */ struct netmap_kring *tx_rings; /* array of TX rings. */ struct netmap_kring *rx_rings; /* array of RX rings. */ NM_SELINFO_T tx_si, rx_si; /* global wait queues */ /* copy of if_qflush an

12、d if_transmit pointers, to intercept * packets from the network stack when netmap is active. */ int (*if_transmit)(struct ifnet *, struct mbuf *); /* references to the ifnet and device routines, used by * the generic netmap functions. */ struct ifnet *ifp; /* adapter is ifp-if_softc */ NM_LOCK_T cor

13、e_lock; /* used if no device lock available */ int (*nm_register)(struct ifnet *, int onoff); void (*nm_lock)(struct ifnet *, int what, u_int ringid); int (*nm_txsync)(struct ifnet *, u_int ring, int lock); int (*nm_rxsync)(struct ifnet *, u_int ring, int lock); int bdg_port;#ifdef linux struct net_

14、device_ops nm_ndo; int if_refcount; / XXX additions for bridge#endif /* linux */; 从struct netmap_adapter可以看出,netmap的注释是相当详细。所以后面,我不再列出netmap的结构体定义,大家可以自己查看,免得满篇全是代码。这样的注释,有几个公司能够做到?e1000_netmap_attach完成简单的初始化工作以后,调用netmap_attach执行真正的attach工作。前者是完成与具体驱动相关的attach工作或者说是准备工作,而后者则是真正的attach。intnetmap_att

15、ach(struct netmap_adapter *na, int num_queues) int n, size; void *buf; /* 这里ifnet又是一个宏,linux下ifnet实际上是net_device */ struct ifnet *ifp = na-ifp; if (ifp = NULL) D(ifp not set, giving up); return EINVAL; /* clear other fields ? */ na-refcount = 0; /* 初始化接收和发送ring */ if (na-num_tx_rings = 0) na-num_tx_

16、rings = num_queues; na-num_rx_rings = num_queues; /* on each direction we have N+1 resources * 0.n-1 are the hardware rings * n is the ring attached to the stack. */ /* 这么详细的注释。还用得着我说吗? 0到n-1的ring是用于转发的ring,而n是本机协议栈的队列 n+1为哨兵位置 */ n = na-num_rx_rings + na-num_tx_rings + 2; /* netmap_adapter与其ring统一申

17、请内存 */ size = sizeof(*na) + n * sizeof(struct netmap_kring); /* 这里的malloc,实际上为kmalloc。 这里还有一个小trick。M_DEVBUF,M_NOWAIT和M_ZERO都是FreeBSD的定义。那么在linux下怎么使用呢? 我开始以为其被定义为linux对应的flag,如GFP_ATOMIC和_GFP_ZERO,于是grep了M_NOWAIT,也没有找到任何的宏定义。 正在奇怪的时候,想到一种情况。让我们看看malloc的宏定义 /* use volatile to fix a probable compiler

18、 error on 2.6.25 */ #define malloc(_size, type, flags) ( volatile int _v = _size; kmalloc(_v, GFP_ATOMIC | _GFP_ZERO); ) 这里type和flags完全没有任何引用的地方。所以在linux下,上面的M_DEVBUG实际上直接被忽略掉了。 */ buf = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf) /* Linux下重用了struct net_device-ax25_ptr,用其保存buf的地址 */ WNA(ifp)

19、 = buf; /* 初始化tx_rings和rx_rings,tx_rings和rx_rings之间用了一个额外的ring分隔,目前不知道这个ring是哨兵呢,还是本主机的ring */ na-tx_rings = (void *)(char *)buf + sizeof(*na); na-rx_rings = na-tx_rings + na-num_tx_rings + 1; /* 复制netmap_device并设置对应的标志位,用于表示其为netmap_device*/ bcopy(na, buf, sizeof(*na); NETMAP_SET_CAPABLE(ifp); na =

20、 buf; /* Core lock initialized here. Others are initialized after * netmap_if_new. */ mtx_init(&na-core_lock, netmap core lock, MTX_NETWORK_LOCK, MTX_DEF); if (na-nm_lock = NULL) ND(using default locks for %s, ifp-if_xname); na-nm_lock = netmap_lock_wrapper; /* 这几行Linux才用的上的代码,是为linux网卡的驱动框架准备的。未来有用

21、处 */#ifdef linux if (ifp-netdev_ops) D(netdev_ops %p, ifp-netdev_ops); /* prepare a clone of the netdev ops */ na-nm_ndo = *ifp-netdev_ops; na-nm_ndo.ndo_start_xmit = linux_netmap_start;#endif D(%s for %s, buf ? ok : failed, ifp-if_xname); return (buf ? 0 : ENOMEM); 完成了netmap_attach,e1000的probe函数e10

22、00_probe即执行完毕。前面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(ada

23、pter);+#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成功返回,

24、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*

25、 slot; struct e1000_tx_ring* txr = &adapter-tx_ring0; 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_

26、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_

27、ringr; for (i = 0; i count; i+) / XXX the skb check and cleanup can go away struct e1000_buffer *bi = &rxr-buffer_infoi; /* 将当前的buff索引转换为netmap的buff索引 */ si = netmap_idx_n2k(&na-rx_ringsr, i); /* 获得netmap的buff的物理地址 */ PNMB(slot + si, &paddr); if (bi-skb) D(rx buf %d was set, i); bi-skb = NULL; / net

28、map_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_rings0.nr_hwavail; if (i count; D(i now is %d, i

29、); 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 =

30、netmap_idx_n2k(&na-tx_rings0, 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_NETMA

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1