netmap源码分析(五)ioctl 注册过程
来源:互联网 发布:java构造器和构造函数 编辑:程序博客网 时间:2024/05/29 19:43
上篇文章中,提到用户态 netmap 的使用过程,其中有一个 nm_open 函数。
nm_open 封装好了一些内部操作,包括注册部分ioctl(d->fd, NIOCREGIF, &d->req)
,这次我们来分析一下netmap_ioctl
函数。
这个函数比较长,这里只分析注册部分:
netmap_ioctl() |-- netmap_do_regif() |-- netmap_update_config() |-- nm_config()
netmap_ioctl
int netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread *td){ struct nmreq *nmr = (struct nmreq *) data; struct netmap_adapter *na = NULL; struct ifnet *ifp = NULL; int error = 0; struct netmap_if *nifp; enum txrx t; if (cmd == NIOCGINFO || cmd == NIOCREGIF) { nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0'; // nr_name 就是 net interface 名 if (nmr->nr_version != NETMAP_API) { nmr->nr_version = NETMAP_API; } // 判断版本是否有效,当前 netmap API 版本是 11 if (nmr->nr_version < NETMAP_MIN_API || nmr->nr_version > NETMAP_MAX_API) { return EINVAL; } } switch (cmd) { case NIOCREGIF: i = nmr->nr_cmd; // nr_cmd 为 0 NMG_LOCK(); // 防止并发注册 do { u_int memflags; struct ifnet *ifp; if (priv->np_nifp != NULL) { // 如果已经注册过了,跳出 switch error = EBUSY; break; } error = netmap_get_na(nmr, &na, &ifp, 1); // 获取 na 和 interface if (error) break; if (NETMAP_OWNED_BY_KERN(na)) { // 查看设备是否是 Busy 状态 netmap_unget_na(na, ifp); error = EBUSY; break; } // 判断是否可以处理 virtio-net if (na->virt_hdr_len && !(nmr->nr_flags & NR_ACCEPT_VNET_HDR)) { netmap_unget_na(na, ifp); error = EIO; break; } error = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags); if (error) { // 如果注册失败 netmap_unget_na(na, ifp); break; } nifp = priv->np_nifp; priv->np_td = td; // 队列数和槽位数赋值 nmr->nr_rx_rings = na->num_rx_rings; nmr->nr_tx_rings = na->num_tx_rings; nmr->nr_rx_slots = na->num_rx_desc; nmr->nr_tx_slots = na->num_tx_desc; error = netmap_mem_get_info(na->nm_mem, &nmr->nr_memsize, &memflags, &nmr->nr_arg2); // 遍历所有等待队列 wait_queue for_rx_tx(t) { priv->np_si[t] = nm_si_user(priv, t) ? &na->si[t] : &NMR(na, t)[priv->np_qfirst[t]].si; } nmr->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp); // 计算 nifp 的偏移 priv->np_ifp = ifp; } while (0); NMG_UNLOCK(); break; } return error;}
netmap_do_regif —— 真 · 注册部分
int netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na, uint16_t ringid, uint32_t flags){ struct netmap_if *nifp = NULL; int error; netmap_update_config(na); // 更新配置,详细见下文 priv->np_na = na; error = netmap_set_ringid(priv, ringid, flags); // 设置 ring ID error = netmap_mem_finalize(na->nm_mem, na); // 更新一些配置 error = netmap_krings_get(priv); // 确保 netmap 有内核态队列 nifp = netmap_mem_if_new(na); // 创建一个新的 nifp na->active_fds++; // 增加这个 interface 的引用计数 mb(); priv->np_nifp = nifp; return 0;}
netmap_update_config
从硬件设备中获取相关配置信息并更新到 netmap 中
int netmap_update_config(struct netmap_adapter *na){ u_int txr, txd, rxr, rxd; txr = txd = rxr = rxd = 0; // nm_config 指向 netmap_linux_config if (na->nm_config == NULL || na->nm_config(na, &txr, &txd, &rxr, &rxd)) { txr = na->num_tx_rings; txd = na->num_tx_desc; rxr = na->num_rx_rings; rxd = na->num_rx_desc; } if (na->active_fds == 0) { // 更新收发队列和槽位的个数 na->num_tx_rings = txr; na->num_tx_desc = txd; na->num_rx_rings = rxr; na->num_rx_desc = rxd; return 0; } return 1; }
netmap_linux_config
int netmap_linux_config(struct netmap_adapter *na, u_int *txr, u_int *txd, u_int *rxr, u_int *rxd){ struct ifnet *ifp = na->ifp; int error = 0; rtnl_lock(); if (ifp == NULL) { error = ENXIO; goto out; } error = nm_os_generic_find_num_desc(ifp, txd, rxd); // 获取硬件网卡的槽位个数 if (error) goto out; nm_os_generic_find_num_queues(ifp, txr, rxr); // 获取硬件网卡的TX/RX个数out: rtnl_unlock(); return error;}
nm_os_generic_find_num_desc
获取槽位
int nm_os_generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx){ int error = EOPNOTSUPP;#ifdef NETMAP_LINUX_HAVE_GET_RINGPARAM struct ethtool_ringparam rp; // 通过 ethtool_ops 获取槽位 if (ifp->ethtool_ops && ifp->ethtool_ops->get_ringparam) { ifp->ethtool_ops->get_ringparam(ifp, &rp); *tx = rp.tx_pending ? rp.tx_pending : rp.tx_max_pending; *rx = rp.rx_pending ? rp.rx_pending : rp.rx_max_pending; } return error;}
注意:下面这个 nm_os_generic_find_num_queues( ) 函数需要稍加注意一下
nm_os_generic_find_num_queues
我当时使用 netmap 环境:
- OS:RHEL6.5 x86_64
- Kernel:Linux 2.6.32-431
这里有一个问题,linux 2.6.32 的 ethtool_ops 中并没有get_channels
,这就导致 RX 被设置成了 1,那么会有什么问题呢?
比如我用 Intel 82599EB 10-Gigabit 测试,网卡是 8 TX 和 8 RX,不过 netmap 测试时,收包相当低,经过排查发现只使用了 1 个 RX。
更改方法见我之前的文章 使用 netmap 提升 ixgbe 性能(续),算是临时方案吧。
获取队列
void nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq){ struct ethtool_channels ch; memset(&ch, 0, sizeof(ch)); // 也是通过 ethtool_ops 获取队列信息 if (ifp->ethtool_ops && ifp->ethtool_ops->get_channels) { ifp->ethtool_ops->get_channels(ifp, &ch); *txq = ch.tx_count ? ch.tx_count : ch.combined_count; *rxq = ch.rx_count ? ch.rx_count : ch.combined_count; } else { *rxq = 1; }}
1 0
- netmap源码分析(五)ioctl 注册过程
- Netmap分析(五)
- netmap源码分析(三)内核态收包过程
- netmap源码分析(四)用户态收包过程
- netmap源码分析(一)插入 netmap 代码到驱动程序
- Netmap分析(一)
- Netmap分析(二)
- Netmap分析(三)
- Netmap分析(四)
- Netmap分析(六)
- curl源码分析(二)协议注册与使用过程
- Android请求注册服务过程源码分析
- Android服务注册完整过程源码分析
- Android请求注册服务过程源码分析
- Android服务注册完整过程源码分析
- netmap源码分析(二)内核态关键结构的初始化
- netmap源码读后感(第二版)
- Ext4文件系统架构分析(五) ——ioctl源码分析之设置/获取文件版本号
- struts1.X配置文件详解
- MATLAB符号函数运算与conj
- 理解JavaScript面向对象(三):作用域链及原型
- 知识回顾之什么是Ajax?
- C# .NET万能数据库访问封装类(ACCESS、SQLServer、Oracle)
- netmap源码分析(五)ioctl 注册过程
- Java 方法传参方式: 按值调用
- Hierarchical Configurations
- at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:370) 异常
- Java匿名内部类访问外部变量,为何需被标志为final?
- redis快速入门
- springboot(六):如何优雅的使用mybatis
- Android研发安全1-Activity组件安全(上)
- 伪类和伪元素以及nth-child() 应用