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
原创粉丝点击